@push.rocks/smartproxy 25.2.1 → 25.3.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 +17 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +13 -0
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +10 -0
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +124 -75
- package/dist_ts/proxies/smart-proxy/utils/concurrency-semaphore.d.ts +11 -0
- package/dist_ts/proxies/smart-proxy/utils/concurrency-semaphore.js +29 -0
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -1
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/proxies/smart-proxy/models/interfaces.ts +15 -0
- package/ts/proxies/smart-proxy/smart-proxy.ts +148 -83
- package/ts/proxies/smart-proxy/utils/concurrency-semaphore.ts +28 -0
- package/ts/proxies/smart-proxy/utils/index.ts +3 -0
package/changelog.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-02-14 - 25.3.0 - feat(smart-proxy)
|
|
4
|
+
add background concurrent certificate provisioning with per-domain timeouts and concurrency control
|
|
5
|
+
|
|
6
|
+
- Add ISmartProxyOptions settings: certProvisionTimeout (ms) and certProvisionConcurrency (default 4)
|
|
7
|
+
- Run certProvisionFunction as fire-and-forget background tasks (stores promise on start/route-update and awaited on stop)
|
|
8
|
+
- Provision certificates in parallel with a concurrency limit using a new ConcurrencySemaphore utility
|
|
9
|
+
- Introduce per-domain timeout handling (default 300000ms) via withTimeout and surface timeout errors as certificate-failed events
|
|
10
|
+
- Refactor provisioning into provisionSingleDomain to isolate domain handling, ACME fallback preserved
|
|
11
|
+
- Run provisioning outside route update mutex so route updates are not blocked by slow provisioning
|
|
12
|
+
|
|
13
|
+
## 2026-02-14 - 25.2.2 - fix(smart-proxy)
|
|
14
|
+
start metrics polling before certificate provisioning to avoid blocking metrics collection
|
|
15
|
+
|
|
16
|
+
- Start metrics polling immediately after Rust engine startup so metrics are available without waiting for certificate provisioning.
|
|
17
|
+
- Run certProvisionFunction after startup because ACME/DNS-01 provisioning can hang or be slow and must not block observability.
|
|
18
|
+
- Code change in ts/proxies/smart-proxy/smart-proxy.ts: metricsAdapter.startPolling() moved to run before provisionCertificatesViaCallback().
|
|
19
|
+
|
|
3
20
|
## 2026-02-14 - 25.2.1 - fix(smartproxy)
|
|
4
21
|
no changes detected in git diff
|
|
5
22
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '25.
|
|
6
|
+
version: '25.3.0',
|
|
7
7
|
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHFQQUFxUDtDQUNuUSxDQUFBIn0=
|
|
@@ -150,6 +150,19 @@ export interface ISmartProxyOptions {
|
|
|
150
150
|
* Default: true
|
|
151
151
|
*/
|
|
152
152
|
certProvisionFallbackToAcme?: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Per-domain timeout in ms for certProvisionFunction calls.
|
|
155
|
+
* If a single domain's provisioning takes longer than this, it's aborted
|
|
156
|
+
* and a certificate-failed event is emitted.
|
|
157
|
+
* Default: 300000 (5 minutes)
|
|
158
|
+
*/
|
|
159
|
+
certProvisionTimeout?: number;
|
|
160
|
+
/**
|
|
161
|
+
* Maximum number of domains to provision certificates for concurrently.
|
|
162
|
+
* Prevents overwhelming ACME providers when many domains provision at once.
|
|
163
|
+
* Default: 4
|
|
164
|
+
*/
|
|
165
|
+
certProvisionConcurrency?: number;
|
|
153
166
|
/**
|
|
154
167
|
* Disable the default self-signed fallback certificate.
|
|
155
168
|
* When false (default), a self-signed cert is generated at startup and loaded
|
|
@@ -22,6 +22,7 @@ export declare class SmartProxy extends plugins.EventEmitter {
|
|
|
22
22
|
private metricsAdapter;
|
|
23
23
|
private routeUpdateLock;
|
|
24
24
|
private stopping;
|
|
25
|
+
private certProvisionPromise;
|
|
25
26
|
constructor(settingsArg: ISmartProxyOptions);
|
|
26
27
|
/**
|
|
27
28
|
* Start the proxy.
|
|
@@ -86,6 +87,15 @@ export declare class SmartProxy extends plugins.EventEmitter {
|
|
|
86
87
|
* If it returns 'http01', let Rust handle ACME.
|
|
87
88
|
*/
|
|
88
89
|
private provisionCertificatesViaCallback;
|
|
90
|
+
/**
|
|
91
|
+
* Provision a single domain's certificate via the callback.
|
|
92
|
+
* Includes per-domain timeout and shutdown checks.
|
|
93
|
+
*/
|
|
94
|
+
private provisionSingleDomain;
|
|
95
|
+
/**
|
|
96
|
+
* Race a promise against a timeout. Rejects with the given message if the timeout fires first.
|
|
97
|
+
*/
|
|
98
|
+
private withTimeout;
|
|
89
99
|
/**
|
|
90
100
|
* Normalize routing glob patterns into valid domain identifiers for cert provisioning.
|
|
91
101
|
* - `*nevermind.cloud` → `['nevermind.cloud', '*.nevermind.cloud']`
|
|
@@ -10,6 +10,7 @@ import { SharedRouteManager as RouteManager } from '../../core/routing/route-man
|
|
|
10
10
|
import { RouteValidator } from './utils/route-validator.js';
|
|
11
11
|
import { generateDefaultCertificate } from './utils/default-cert-generator.js';
|
|
12
12
|
import { Mutex } from './utils/mutex.js';
|
|
13
|
+
import { ConcurrencySemaphore } from './utils/concurrency-semaphore.js';
|
|
13
14
|
/**
|
|
14
15
|
* SmartProxy - Rust-backed proxy engine with TypeScript configuration API.
|
|
15
16
|
*
|
|
@@ -25,6 +26,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
25
26
|
super();
|
|
26
27
|
this.socketHandlerServer = null;
|
|
27
28
|
this.stopping = false;
|
|
29
|
+
this.certProvisionPromise = null;
|
|
28
30
|
// Apply defaults
|
|
29
31
|
this.settings = {
|
|
30
32
|
...settingsArg,
|
|
@@ -152,11 +154,16 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
152
154
|
logger.log('warn', `Failed to load certificates from consumer store: ${err.message}`, { component: 'smart-proxy' });
|
|
153
155
|
}
|
|
154
156
|
}
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
//
|
|
157
|
+
// Start metrics polling BEFORE cert provisioning — the Rust engine is already
|
|
158
|
+
// running and accepting connections, so metrics should be available immediately.
|
|
159
|
+
// Cert provisioning can hang indefinitely (e.g. DNS-01 ACME timeouts) and must
|
|
160
|
+
// not block metrics collection.
|
|
158
161
|
this.metricsAdapter.startPolling();
|
|
159
162
|
logger.log('info', 'SmartProxy started (Rust engine)', { component: 'smart-proxy' });
|
|
163
|
+
// Fire-and-forget cert provisioning — Rust engine is already running and serving traffic.
|
|
164
|
+
// Events (certificate-issued / certificate-failed) fire independently per domain.
|
|
165
|
+
this.certProvisionPromise = this.provisionCertificatesViaCallback(preloadedDomains)
|
|
166
|
+
.catch((err) => logger.log('error', `Unexpected error in cert provisioning: ${err.message}`, { component: 'smart-proxy' }));
|
|
160
167
|
}
|
|
161
168
|
/**
|
|
162
169
|
* Stop the proxy.
|
|
@@ -164,6 +171,11 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
164
171
|
async stop() {
|
|
165
172
|
logger.log('info', 'SmartProxy shutting down...', { component: 'smart-proxy' });
|
|
166
173
|
this.stopping = true;
|
|
174
|
+
// Wait for in-flight cert provisioning to bail out (it checks this.stopping)
|
|
175
|
+
if (this.certProvisionPromise) {
|
|
176
|
+
await this.certProvisionPromise;
|
|
177
|
+
this.certProvisionPromise = null;
|
|
178
|
+
}
|
|
167
179
|
// Stop metrics polling
|
|
168
180
|
this.metricsAdapter.stopPolling();
|
|
169
181
|
// Remove exit listener before killing to avoid spurious error events
|
|
@@ -187,7 +199,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
187
199
|
* Update routes atomically.
|
|
188
200
|
*/
|
|
189
201
|
async updateRoutes(newRoutes) {
|
|
190
|
-
|
|
202
|
+
await this.routeUpdateLock.runExclusive(async () => {
|
|
191
203
|
// Validate
|
|
192
204
|
const validation = RouteValidator.validateRoutes(newRoutes);
|
|
193
205
|
if (!validation.valid) {
|
|
@@ -214,10 +226,12 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
214
226
|
}
|
|
215
227
|
// Update stored routes
|
|
216
228
|
this.settings.routes = newRoutes;
|
|
217
|
-
// Handle cert provisioning for new routes
|
|
218
|
-
await this.provisionCertificatesViaCallback();
|
|
219
229
|
logger.log('info', `Routes updated (${newRoutes.length} routes)`, { component: 'smart-proxy' });
|
|
220
230
|
});
|
|
231
|
+
// Fire-and-forget cert provisioning outside the mutex — routes are already updated,
|
|
232
|
+
// cert provisioning doesn't need the route update lock and may be slow.
|
|
233
|
+
this.certProvisionPromise = this.provisionCertificatesViaCallback()
|
|
234
|
+
.catch((err) => logger.log('error', `Unexpected error in cert provisioning after route update: ${err.message}`, { component: 'smart-proxy' }));
|
|
221
235
|
}
|
|
222
236
|
/**
|
|
223
237
|
* Provision a certificate for a named route.
|
|
@@ -338,7 +352,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
338
352
|
const provisionFn = this.settings.certProvisionFunction;
|
|
339
353
|
if (!provisionFn)
|
|
340
354
|
return;
|
|
341
|
-
|
|
355
|
+
// Phase 1: Collect all unique (domain, route) pairs that need provisioning
|
|
356
|
+
const seen = new Set(skipDomains);
|
|
357
|
+
const tasks = [];
|
|
342
358
|
for (const route of this.settings.routes) {
|
|
343
359
|
if (route.action.tls?.certificate !== 'auto')
|
|
344
360
|
continue;
|
|
@@ -347,82 +363,115 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
347
363
|
const rawDomains = Array.isArray(route.match.domains) ? route.match.domains : [route.match.domains];
|
|
348
364
|
const certDomains = this.normalizeDomainsForCertProvisioning(rawDomains);
|
|
349
365
|
for (const domain of certDomains) {
|
|
350
|
-
if (
|
|
366
|
+
if (seen.has(domain))
|
|
351
367
|
continue;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
368
|
+
seen.add(domain);
|
|
369
|
+
tasks.push({ domain, route });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (tasks.length === 0)
|
|
373
|
+
return;
|
|
374
|
+
// Phase 2: Process all domains in parallel with concurrency limit
|
|
375
|
+
const concurrency = this.settings.certProvisionConcurrency ?? 4;
|
|
376
|
+
const semaphore = new ConcurrencySemaphore(concurrency);
|
|
377
|
+
const promises = tasks.map(async ({ domain, route }) => {
|
|
378
|
+
await semaphore.acquire();
|
|
379
|
+
try {
|
|
380
|
+
await this.provisionSingleDomain(domain, route, provisionFn);
|
|
381
|
+
}
|
|
382
|
+
finally {
|
|
383
|
+
semaphore.release();
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
await Promise.allSettled(promises);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Provision a single domain's certificate via the callback.
|
|
390
|
+
* Includes per-domain timeout and shutdown checks.
|
|
391
|
+
*/
|
|
392
|
+
async provisionSingleDomain(domain, route, provisionFn) {
|
|
393
|
+
if (this.stopping)
|
|
394
|
+
return;
|
|
395
|
+
let expiryDate;
|
|
396
|
+
let source = 'certProvisionFunction';
|
|
397
|
+
const eventComms = {
|
|
398
|
+
log: (msg) => logger.log('info', `[certProvision ${domain}] ${msg}`, { component: 'smart-proxy' }),
|
|
399
|
+
warn: (msg) => logger.log('warn', `[certProvision ${domain}] ${msg}`, { component: 'smart-proxy' }),
|
|
400
|
+
error: (msg) => logger.log('error', `[certProvision ${domain}] ${msg}`, { component: 'smart-proxy' }),
|
|
401
|
+
setExpiryDate: (date) => { expiryDate = date.toISOString(); },
|
|
402
|
+
setSource: (s) => { source = s; },
|
|
403
|
+
};
|
|
404
|
+
const timeoutMs = this.settings.certProvisionTimeout ?? 300_000; // 5 min default
|
|
405
|
+
try {
|
|
406
|
+
const result = await this.withTimeout(provisionFn(domain, eventComms), timeoutMs, `Certificate provisioning timed out for ${domain} after ${timeoutMs}ms`);
|
|
407
|
+
if (this.stopping)
|
|
408
|
+
return;
|
|
409
|
+
if (result === 'http01') {
|
|
410
|
+
if (route.name) {
|
|
411
|
+
try {
|
|
412
|
+
await this.bridge.provisionCertificate(route.name);
|
|
413
|
+
logger.log('info', `Triggered Rust ACME for ${domain} (route: ${route.name})`, { component: 'smart-proxy' });
|
|
378
414
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
await this.bridge.loadCertificate(domain, certObj.publicKey, certObj.privateKey);
|
|
383
|
-
logger.log('info', `Certificate loaded via provision function for ${domain}`, { component: 'smart-proxy' });
|
|
384
|
-
// Persist to consumer store
|
|
385
|
-
if (this.settings.certStore?.save) {
|
|
386
|
-
try {
|
|
387
|
-
await this.settings.certStore.save(domain, certObj.publicKey, certObj.privateKey);
|
|
388
|
-
}
|
|
389
|
-
catch (storeErr) {
|
|
390
|
-
logger.log('warn', `certStore.save() failed for ${domain}: ${storeErr.message}`, { component: 'smart-proxy' });
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
// Emit certificate-issued event
|
|
394
|
-
this.emit('certificate-issued', {
|
|
395
|
-
domain,
|
|
396
|
-
expiryDate: expiryDate || (certObj.validUntil ? new Date(certObj.validUntil).toISOString() : undefined),
|
|
397
|
-
source,
|
|
398
|
-
});
|
|
415
|
+
catch (provisionErr) {
|
|
416
|
+
logger.log('warn', `Cannot provision cert for ${domain} — callback returned 'http01' but Rust ACME failed: ${provisionErr.message}. ` +
|
|
417
|
+
'Note: Rust ACME is disabled when certProvisionFunction is set.', { component: 'smart-proxy' });
|
|
399
418
|
}
|
|
400
419
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
logger.log('info', `Falling back to Rust ACME for ${domain} (route: ${route.name})`, { component: 'smart-proxy' });
|
|
414
|
-
}
|
|
415
|
-
catch (acmeErr) {
|
|
416
|
-
logger.log('warn', `ACME fallback also failed for ${domain}: ${acmeErr.message}` +
|
|
417
|
-
(this.settings.disableDefaultCert
|
|
418
|
-
? ' — TLS will fail for this domain (disableDefaultCert is true)'
|
|
419
|
-
: ' — default self-signed fallback cert will be used'), { component: 'smart-proxy' });
|
|
420
|
-
}
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (result && typeof result === 'object') {
|
|
423
|
+
if (this.stopping)
|
|
424
|
+
return;
|
|
425
|
+
const certObj = result;
|
|
426
|
+
await this.bridge.loadCertificate(domain, certObj.publicKey, certObj.privateKey);
|
|
427
|
+
logger.log('info', `Certificate loaded via provision function for ${domain}`, { component: 'smart-proxy' });
|
|
428
|
+
// Persist to consumer store
|
|
429
|
+
if (this.settings.certStore?.save) {
|
|
430
|
+
try {
|
|
431
|
+
await this.settings.certStore.save(domain, certObj.publicKey, certObj.privateKey);
|
|
421
432
|
}
|
|
433
|
+
catch (storeErr) {
|
|
434
|
+
logger.log('warn', `certStore.save() failed for ${domain}: ${storeErr.message}`, { component: 'smart-proxy' });
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
this.emit('certificate-issued', {
|
|
438
|
+
domain,
|
|
439
|
+
expiryDate: expiryDate || (certObj.validUntil ? new Date(certObj.validUntil).toISOString() : undefined),
|
|
440
|
+
source,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
logger.log('warn', `certProvisionFunction failed for ${domain}: ${err.message}`, { component: 'smart-proxy' });
|
|
446
|
+
this.emit('certificate-failed', {
|
|
447
|
+
domain,
|
|
448
|
+
error: err.message,
|
|
449
|
+
source,
|
|
450
|
+
});
|
|
451
|
+
// Fallback to ACME if enabled and route has a name
|
|
452
|
+
if (this.settings.certProvisionFallbackToAcme !== false && route.name) {
|
|
453
|
+
try {
|
|
454
|
+
await this.bridge.provisionCertificate(route.name);
|
|
455
|
+
logger.log('info', `Falling back to Rust ACME for ${domain} (route: ${route.name})`, { component: 'smart-proxy' });
|
|
456
|
+
}
|
|
457
|
+
catch (acmeErr) {
|
|
458
|
+
logger.log('warn', `ACME fallback also failed for ${domain}: ${acmeErr.message}` +
|
|
459
|
+
(this.settings.disableDefaultCert
|
|
460
|
+
? ' — TLS will fail for this domain (disableDefaultCert is true)'
|
|
461
|
+
: ' — default self-signed fallback cert will be used'), { component: 'smart-proxy' });
|
|
422
462
|
}
|
|
423
463
|
}
|
|
424
464
|
}
|
|
425
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* Race a promise against a timeout. Rejects with the given message if the timeout fires first.
|
|
468
|
+
*/
|
|
469
|
+
withTimeout(promise, ms, message) {
|
|
470
|
+
return new Promise((resolve, reject) => {
|
|
471
|
+
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
472
|
+
promise.then((val) => { clearTimeout(timer); resolve(val); }, (err) => { clearTimeout(timer); reject(err); });
|
|
473
|
+
});
|
|
474
|
+
}
|
|
426
475
|
/**
|
|
427
476
|
* Normalize routing glob patterns into valid domain identifiers for cert provisioning.
|
|
428
477
|
* - `*nevermind.cloud` → `['nevermind.cloud', '*.nevermind.cloud']`
|
|
@@ -465,4 +514,4 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
465
514
|
return validDomainRegex.test(domain);
|
|
466
515
|
}
|
|
467
516
|
}
|
|
468
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnQtcHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3NtYXJ0LXByb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBELDBCQUEwQjtBQUMxQixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDekQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDakUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFL0QsbUJBQW1CO0FBQ25CLE9BQU8sRUFBRSxrQkFBa0IsSUFBSSxZQUFZLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUN6RixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDNUQsT0FBTyxFQUFFLDBCQUEwQixFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDL0UsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBT3pDOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sT0FBTyxVQUFXLFNBQVEsT0FBTyxDQUFDLFlBQVk7SUFXbEQsWUFBWSxXQUErQjtRQUN6QyxLQUFLLEVBQUUsQ0FBQztRQU5GLHdCQUFtQixHQUErQixJQUFJLENBQUM7UUFHdkQsYUFBUSxHQUFHLEtBQUssQ0FBQztRQUt2QixpQkFBaUI7UUFDakIsSUFBSSxDQUFDLFFBQVEsR0FBRztZQUNkLEdBQUcsV0FBVztZQUNkLGtCQUFrQixFQUFFLFdBQVcsQ0FBQyxrQkFBa0IsSUFBSSxNQUFNO1lBQzVELGFBQWEsRUFBRSxXQUFXLENBQUMsYUFBYSxJQUFJLE9BQU87WUFD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
|
|
517
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnQtcHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3NtYXJ0LXByb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBELDBCQUEwQjtBQUMxQixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDekQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDakUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFL0QsbUJBQW1CO0FBQ25CLE9BQU8sRUFBRSxrQkFBa0IsSUFBSSxZQUFZLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUN6RixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDNUQsT0FBTyxFQUFFLDBCQUEwQixFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDL0UsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3pDLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBT3hFOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sT0FBTyxVQUFXLFNBQVEsT0FBTyxDQUFDLFlBQVk7SUFZbEQsWUFBWSxXQUErQjtRQUN6QyxLQUFLLEVBQUUsQ0FBQztRQVBGLHdCQUFtQixHQUErQixJQUFJLENBQUM7UUFHdkQsYUFBUSxHQUFHLEtBQUssQ0FBQztRQUNqQix5QkFBb0IsR0FBeUIsSUFBSSxDQUFDO1FBS3hELGlCQUFpQjtRQUNqQixJQUFJLENBQUMsUUFBUSxHQUFHO1lBQ2QsR0FBRyxXQUFXO1lBQ2Qsa0JBQWtCLEVBQUUsV0FBVyxDQUFDLGtCQUFrQixJQUFJLE1BQU07WUFDNUQsYUFBYSxFQUFFLFdBQVcsQ0FBQyxhQUFhLElBQUksT0FBTztZQUNuRCxxQkFBcUIsRUFBRSxXQUFXLENBQUMscUJBQXFCLElBQUksUUFBUTtZQUNwRSxpQkFBaUIsRUFBRSxXQUFXLENBQUMsaUJBQWlCLElBQUksUUFBUTtZQUM1RCx1QkFBdUIsRUFBRSxXQUFXLENBQUMsdUJBQXVCLElBQUksS0FBSztZQUNyRSxtQkFBbUIsRUFBRSxXQUFXLENBQUMsbUJBQW1CLElBQUksR0FBRztZQUMzRCw0QkFBNEIsRUFBRSxXQUFXLENBQUMsNEJBQTRCLElBQUksR0FBRztZQUM3RSxrQkFBa0IsRUFBRSxXQUFXLENBQUMsa0JBQWtCLElBQUksVUFBVTtZQUNoRSw2QkFBNkIsRUFBRSxXQUFXLENBQUMsNkJBQTZCLElBQUksQ0FBQztZQUM3RSx5QkFBeUIsRUFBRSxXQUFXLENBQUMseUJBQXlCLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUk7U0FDNUYsQ0FBQztRQUVGLHlCQUF5QjtRQUN6QixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDdkIsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxZQUFZLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDakUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQztZQUM3RCxDQUFDO1lBQ0QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEdBQUc7Z0JBQ25CLE9BQU8sRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxPQUFPLEtBQUssS0FBSztnQkFDN0MsSUFBSSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxFQUFFO2dCQUNuQyxLQUFLLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsS0FBSztnQkFDL0IsYUFBYSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLGFBQWEsSUFBSSxLQUFLO2dCQUN4RCxrQkFBa0IsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsSUFBSSxFQUFFO2dCQUMvRCxTQUFTLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsU0FBUyxLQUFLLEtBQUs7Z0JBQ2pELG1CQUFtQixFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLG1CQUFtQixJQUFJLEtBQUs7Z0JBQ3BFLHVCQUF1QixFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLHVCQUF1QixJQUFJLEVBQUU7Z0JBQ3pFLGFBQWEsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxhQUFhLElBQUksRUFBRTtnQkFDckQsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUk7YUFDdEIsQ0FBQztRQUNKLENBQUM7UUFFRCxrQkFBa0I7UUFDbEIsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsQ0FBQztZQUNqQyxNQUFNLFVBQVUsR0FBRyxjQUFjLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDdkUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDdEIsY0FBYyxDQUFDLG1CQUFtQixDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxJQUFJLEtBQUssQ0FBQyxvQ0FBb0MsVUFBVSxDQUFDLE1BQU0sQ0FBQyxJQUFJLHVCQUF1QixDQUFDLENBQUM7WUFDckcsQ0FBQztRQUNILENBQUM7UUFFRCx3QkFBd0I7UUFDeEIsTUFBTSxhQUFhLEdBQUc7WUFDcEIsS0FBSyxFQUFFLENBQUMsT0FBZSxFQUFFLElBQVUsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQztZQUMxRSxJQUFJLEVBQUUsQ0FBQyxPQUFlLEVBQUUsSUFBVSxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDO1lBQ3hFLElBQUksRUFBRSxDQUFDLE9BQWUsRUFBRSxJQUFVLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUM7WUFDeEUsS0FBSyxFQUFFLENBQUMsT0FBZSxFQUFFLElBQVUsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQztTQUMzRSxDQUFDO1FBRUYsd0JBQXdCO1FBQ3hCLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxZQUFZLENBQUM7WUFDbkMsTUFBTSxFQUFFLGFBQWE7WUFDckIscUJBQXFCLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxxQkFBcUI7WUFDMUQsTUFBTSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTTtTQUM3QixDQUFDLENBQUM7UUFFSCxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksZUFBZSxFQUFFLENBQUM7UUFDcEMsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLGlCQUFpQixFQUFFLENBQUM7UUFDNUMsSUFBSSxDQUFDLGNBQWMsR0FBRyxJQUFJLGtCQUFrQixDQUMxQyxJQUFJLENBQUMsTUFBTSxFQUNYLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLGdCQUFnQixJQUFJLElBQUksQ0FDaEQsQ0FBQztRQUNGLElBQUksQ0FBQyxlQUFlLEdBQUcsSUFBSSxLQUFLLEVBQUUsQ0FBQztJQUNyQyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLEtBQUs7UUFDaEIsb0JBQW9CO1FBQ3BCLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUMxQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDYixNQUFNLElBQUksS0FBSyxDQUNiLGdHQUFnRztnQkFDaEcsbUNBQW1DLENBQ3BDLENBQUM7UUFDSixDQUFDO1FBRUQsMEVBQTBFO1FBQzFFLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLElBQW1CLEVBQUUsTUFBcUIsRUFBRSxFQUFFO1lBQ3BFLElBQUksSUFBSSxDQUFDLFFBQVE7Z0JBQUUsT0FBTztZQUMxQixNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSx1Q0FBdUMsSUFBSSxZQUFZLE1BQU0sR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7WUFDcEgsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxLQUFLLENBQUMsMEJBQTBCLElBQUksWUFBWSxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDckYsQ0FBQyxDQUFDLENBQUM7UUFFSCxpRkFBaUY7UUFDakYsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQ2hELENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FDSixDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxLQUFLLGdCQUFnQixJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDO1lBQzlELENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxLQUFLLFVBQVUsSUFBSSxPQUFPLENBQUMsQ0FBQyxJQUFJLEtBQUssVUFBVSxDQUFDLENBQzlGLENBQUM7UUFFRixrRkFBa0Y7UUFDbEYsSUFBSSxnQkFBZ0IsRUFBRSxDQUFDO1lBQ3JCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxJQUFJLG1CQUFtQixDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQztZQUN0RSxNQUFNLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUN6QyxDQUFDO1FBRUQsd0VBQXdFO1FBQ3hFLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUU3RSx3REFBd0Q7UUFDeEQsMERBQTBEO1FBQzFELElBQUksV0FBVyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDO1FBQ3JDLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsSUFBSSxXQUFXLEVBQUUsT0FBTyxFQUFFLENBQUM7WUFDaEUsV0FBVyxHQUFHLEVBQUUsR0FBRyxXQUFXLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxDQUFDO1lBQ2pELE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLGlGQUFpRixFQUFFLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7UUFDdEksQ0FBQztRQUVELG9CQUFvQjtRQUNwQixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLFVBQVUsRUFBRSxXQUFXLENBQUMsQ0FBQztRQUU3RCx1QkFBdUI7UUFDdkIsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUVyQyxpRUFBaUU7UUFDakUsSUFBSSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztZQUM3QixNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMscUJBQXFCLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGFBQWEsRUFBRSxDQUFDLENBQUM7UUFDcEYsQ0FBQztRQUVELDhEQUE4RDtRQUM5RCxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1lBQ3RDLElBQUksQ0FBQztnQkFDSCxNQUFNLFdBQVcsR0FBRywwQkFBMEIsRUFBRSxDQUFDO2dCQUNqRCxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLEdBQUcsRUFBRSxXQUFXLENBQUMsSUFBSSxFQUFFLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDMUUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsaURBQWlELEVBQUUsRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQztZQUN0RyxDQUFDO1lBQUMsT0FBTyxHQUFRLEVBQUUsQ0FBQztnQkFDbEIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsMkNBQTJDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO1lBQzdHLENBQUM7UUFDSCxDQUFDO1FBRUQsb0NBQW9DO1FBQ3BDLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztRQUMzQyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDNUIsSUFBSSxDQUFDO2dCQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ3ZELEtBQUssTUFBTSxLQUFLLElBQUksTUFBTSxFQUFFLENBQUM7b0JBQzNCLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsS0FBSyxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsU0FBUyxFQUFFLEtBQUssQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO29CQUM3RixnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUNyQyxDQUFDO2dCQUNELE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLFVBQVUsTUFBTSxDQUFDLE1BQU0scUNBQXFDLEVBQUUsRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQztZQUNqSCxDQUFDO1lBQUMsT0FBTyxHQUFRLEVBQUUsQ0FBQztnQkFDbEIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsb0RBQW9ELEdBQUcsQ0FBQyxPQUFPLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO1lBQ3RILENBQUM7UUFDSCxDQUFDO1FBRUQsOEVBQThFO1FBQzlFLGlGQUFpRjtRQUNqRiwrRUFBK0U7UUFDL0UsZ0NBQWdDO1FBQ2hDLElBQUksQ0FBQyxjQUFjLENBQUMsWUFBWSxFQUFFLENBQUM7UUFFbkMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsa0NBQWtDLEVBQUUsRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQztRQUVyRiwwRkFBMEY7UUFDMUYsa0ZBQWtGO1FBQ2xGLElBQUksQ0FBQyxvQkFBb0IsR0FBRyxJQUFJLENBQUMsZ0NBQWdDLENBQUMsZ0JBQWdCLENBQUM7YUFDaEYsS0FBSyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSwwQ0FBMEMsR0FBRyxDQUFDLE9BQU8sRUFBRSxFQUFFLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNoSSxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsSUFBSTtRQUNmLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLDZCQUE2QixFQUFFLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7UUFDaEYsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7UUFFckIsNkVBQTZFO1FBQzdFLElBQUksSUFBSSxDQUFDLG9CQUFvQixFQUFFLENBQUM7WUFDOUIsTUFBTSxJQUFJLENBQUMsb0JBQW9CLENBQUM7WUFDaEMsSUFBSSxDQUFDLG9CQUFvQixHQUFHLElBQUksQ0FBQztRQUNuQyxDQUFDO1FBRUQsdUJBQXVCO1FBQ3ZCLElBQUksQ0FBQyxjQUFjLENBQUMsV0FBVyxFQUFFLENBQUM7UUFFbEMscUVBQXFFO1FBQ3JFLElBQUksQ0FBQyxNQUFNLENBQUMsa0JBQWtCLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFdkMsa0JBQWtCO1FBQ2xCLElBQUksQ0FBQztZQUNILE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNoQyxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AsNEJBQTRCO1FBQzlCLENBQUM7UUFDRCxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO1FBRW5CLDRCQUE0QjtRQUM1QixJQUFJLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1lBQzdCLE1BQU0sSUFBSSxDQUFDLG1CQUFtQixDQUFDLElBQUksRUFBRSxDQUFDO1lBQ3RDLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxJQUFJLENBQUM7UUFDbEMsQ0FBQztRQUVELE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLCtCQUErQixFQUFFLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7SUFDcEYsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFlBQVksQ0FBQyxTQUF5QjtRQUNqRCxNQUFNLElBQUksQ0FBQyxlQUFlLENBQUMsWUFBWSxDQUFDLEtBQUssSUFBSSxFQUFFO1lBQ2pELFdBQVc7WUFDWCxNQUFNLFVBQVUsR0FBRyxjQUFjLENBQUMsY0FBYyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQzVELElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQ3RCLGNBQWMsQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQ3RELE1BQU0sSUFBSSxLQUFLLENBQUMsNEJBQTRCLFVBQVUsQ0FBQyxNQUFNLENBQUMsSUFBSSx1QkFBdUIsQ0FBQyxDQUFDO1lBQzdGLENBQUM7WUFFRCxzQkFBc0I7WUFDdEIsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUVsRSxlQUFlO1lBQ2YsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUUzQyw2QkFBNkI7WUFDN0IsSUFBSSxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQUMsU0FBUyxDQUFDLENBQUM7WUFFMUMsd0RBQXdEO1lBQ3hELE1BQU0sZ0JBQWdCLEdBQUcsU0FBUyxDQUFDLElBQUksQ0FDckMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUNKLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssZ0JBQWdCLElBQUksQ0FBQyxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUM7Z0JBQzlELENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxLQUFLLFVBQVUsSUFBSSxPQUFPLENBQUMsQ0FBQyxJQUFJLEtBQUssVUFBVSxDQUFDLENBQzlGLENBQUM7WUFFRixJQUFJLGdCQUFnQixJQUFJLENBQUMsSUFBSSxDQUFDLG1CQUFtQixFQUFFLENBQUM7Z0JBQ2xELElBQUksQ0FBQyxtQkFBbUIsR0FBRyxJQUFJLG1CQUFtQixDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQztnQkFDdEUsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQ3ZDLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsYUFBYSxFQUFFLENBQUMsQ0FBQztZQUNwRixDQUFDO2lCQUFNLElBQUksQ0FBQyxnQkFBZ0IsSUFBSSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztnQkFDekQsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBQ3RDLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxJQUFJLENBQUM7WUFDbEMsQ0FBQztZQUVELHVCQUF1QjtZQUN2QixJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUM7WUFFakMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsbUJBQW1CLFNBQVMsQ0FBQyxNQUFNLFVBQVUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO1FBQ2xHLENBQUMsQ0FBQyxDQUFDO1FBRUgsb0ZBQW9GO1FBQ3BGLHdFQUF3RTtRQUN4RSxJQUFJLENBQUMsb0JBQW9CLEdBQUcsSUFBSSxDQUFDLGdDQUFnQyxFQUFFO2FBQ2hFLEtBQUssQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsNkRBQTZELEdBQUcsQ0FBQyxPQUFPLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbkosQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLG9CQUFvQixDQUFDLFNBQWlCO1FBQ2pELE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUNwRCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsZ0JBQWdCLENBQUMsU0FBaUI7UUFDN0MsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ2hELENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxvQkFBb0IsQ0FBQyxTQUFpQjtRQUNqRCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsb0JBQW9CLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDckQsQ0FBQztJQUVEOztPQUVHO0lBQ0ksVUFBVTtRQUNmLE9BQU8sSUFBSSxDQUFDLGNBQWMsQ0FBQztJQUM3QixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsYUFBYTtRQUN4QixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxFQUFFLENBQUM7SUFDckMsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLGdCQUFnQixDQUFDLElBQVk7UUFDeEMsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzNDLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxJQUFZO1FBQzNDLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM5QyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsaUJBQWlCO1FBQzVCLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU87WUFBRSxPQUFPLEVBQUUsQ0FBQztRQUNwQyxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUN6QyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxpQ0FBaUM7UUFDdEMsTUFBTSxPQUFPLEdBQWEsRUFBRSxDQUFDO1FBQzdCLEtBQUssTUFBTSxLQUFLLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLElBQUksRUFBRSxFQUFFLENBQUM7WUFDL0MsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsT0FBTztnQkFBRSxTQUFTO1lBQ25DLElBQ0UsS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssU0FBUztnQkFDL0IsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUc7Z0JBQ2pCLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksS0FBSyxhQUFhO2dCQUN2QyxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEtBQUssTUFBTTtnQkFFdkMsU0FBUztZQUVYLE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUN0RyxNQUFNLFFBQVEsR0FBRyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLElBQUksSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3ZGLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxRQUFRLENBQUMsQ0FBQztRQUM1QixDQUFDO1FBQ0QsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLGlCQUFpQjtRQUM1QixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUN6QyxDQUFDO0lBRUQsMEJBQTBCO0lBRTFCOztPQUVHO0lBQ0ssZUFBZSxDQUFDLE1BQXNCLEVBQUUsWUFBMkI7UUFDekUsTUFBTSxJQUFJLEdBQUcsWUFBWSxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQztRQUM1RSxPQUFPO1lBQ0wsTUFBTTtZQUNOLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVE7WUFDaEMsSUFBSSxFQUFFLElBQUk7Z0JBQ1IsQ0FBQyxDQUFDO29CQUNFLE9BQU8sRUFBRSxJQUFJLENBQUMsT0FBTztvQkFDckIsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLO29CQUNqQixhQUFhLEVBQUUsSUFBSSxDQUFDLGFBQWE7b0JBQ2pDLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtvQkFDZixrQkFBa0IsRUFBRSxJQUFJLENBQUMsa0JBQWtCO29CQUMzQyxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7b0JBQ3pCLHVCQUF1QixFQUFFLElBQUksQ0FBQyx1QkFBdUI7aUJBQ3REO2dCQUNILENBQUMsQ0FBQyxTQUFTO1lBQ2IsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxpQkFBaUI7WUFDbEQsa0JBQWtCLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0I7WUFDcEQsYUFBYSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsYUFBYTtZQUMxQyxxQkFBcUIsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQjtZQUMxRCx1QkFBdUIsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLHVCQUF1QjtZQUM5RCxtQkFBbUIsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLG1CQUFtQjtZQUN0RCw0QkFBNEIsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLDRCQUE0QjtZQUN4RSxrQkFBa0IsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQjtZQUNwRCw2QkFBNkIsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLDZCQUE2QjtZQUMxRSx5QkFBeUIsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLHlCQUF5QjtZQUNsRSxtQkFBbUIsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLG1CQUFtQjtZQUN0RCxpQkFBaUIsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLGlCQUFpQjtZQUNsRCxPQUFPLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPO1NBQy9CLENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLEtBQUssQ0FBQyxnQ0FBZ0MsQ0FBQyxjQUEyQixJQUFJLEdBQUcsRUFBRTtRQUNqRixNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixDQUFDO1FBQ3hELElBQUksQ0FBQyxXQUFXO1lBQUUsT0FBTztRQUV6QiwyRUFBMkU7UUFDM0UsTUFBTSxJQUFJLEdBQUcsSUFBSSxHQUFHLENBQVMsV0FBVyxDQUFDLENBQUM7UUFDMUMsTUFBTSxLQUFLLEdBQW1ELEVBQUUsQ0FBQztRQUVqRSxLQUFLLE1BQU0sS0FBSyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDekMsSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxXQUFXLEtBQUssTUFBTTtnQkFBRSxTQUFTO1lBQ3ZELElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLE9BQU87Z0JBQUUsU0FBUztZQUVuQyxNQUFNLFVBQVUsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDcEcsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLG1DQUFtQyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBRXpFLEtBQUssTUFBTSxNQUFNLElBQUksV0FBVyxFQUFFLENBQUM7Z0JBQ2pDLElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUM7b0JBQUUsU0FBUztnQkFDL0IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDakIsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1lBQ2hDLENBQUM7UUFDSCxDQUFDO1FBRUQsSUFBSSxLQUFLLENBQUMsTUFBTSxLQUFLLENBQUM7WUFBRSxPQUFPO1FBRS9CLGtFQUFrRTtRQUNsRSxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLHdCQUF3QixJQUFJLENBQUMsQ0FBQztRQUNoRSxNQUFNLFNBQVMsR0FBRyxJQUFJLG9CQUFvQixDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBRXhELE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUU7WUFDckQsTUFBTSxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDMUIsSUFBSSxDQUFDO2dCQUNILE1BQU0sSUFBSSxDQUFDLHFCQUFxQixDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsV0FBVyxDQUFDLENBQUM7WUFDL0QsQ0FBQztvQkFBUyxDQUFDO2dCQUNULFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN0QixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7UUFFSCxNQUFNLE9BQU8sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDckMsQ0FBQztJQUVEOzs7T0FHRztJQUNLLEtBQUssQ0FBQyxxQkFBcUIsQ0FDakMsTUFBYyxFQUNkLEtBQW1CLEVBQ25CLFdBQThHO1FBRTlHLElBQUksSUFBSSxDQUFDLFFBQVE7WUFBRSxPQUFPO1FBRTFCLElBQUksVUFBOEIsQ0FBQztRQUNuQyxJQUFJLE1BQU0sR0FBRyx1QkFBdUIsQ0FBQztRQUVyQyxNQUFNLFVBQVUsR0FBNkI7WUFDM0MsR0FBRyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxrQkFBa0IsTUFBTSxLQUFLLEdBQUcsRUFBRSxFQUFFLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDO1lBQ2xHLElBQUksRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsa0JBQWtCLE1BQU0sS0FBSyxHQUFHLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQztZQUNuRyxLQUFLLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGtCQUFrQixNQUFNLEtBQUssR0FBRyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLENBQUM7WUFDckcsYUFBYSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxVQUFVLEdBQUcsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQztZQUM3RCxTQUFTLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxHQUFHLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ2xDLENBQUM7UUFFRixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLG9CQUFvQixJQUFJLE9BQU8sQ0FBQyxDQUFDLGdCQUFnQjtRQUVqRixJQUFJLENBQUM7WUFDSCxNQUFNLE1BQU0sR0FBbUMsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUNuRSxXQUFXLENBQUMsTUFBTSxFQUFFLFVBQVUsQ0FBQyxFQUMvQixTQUFTLEVBQ1QsMENBQTBDLE1BQU0sVUFBVSxTQUFTLElBQUksQ0FDeEUsQ0FBQztZQUVGLElBQUksSUFBSSxDQUFDLFFBQVE7Z0JBQUUsT0FBTztZQUUxQixJQUFJLE1BQU0sS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDeEIsSUFBSSxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7b0JBQ2YsSUFBSSxDQUFDO3dCQUNILE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7d0JBQ25ELE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLDJCQUEyQixNQUFNLFlBQVksS0FBSyxDQUFDLElBQUksR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7b0JBQy9HLENBQUM7b0JBQUMsT0FBTyxZQUFpQixFQUFFLENBQUM7d0JBQzNCLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLDZCQUE2QixNQUFNLHVEQUF1RCxZQUFZLENBQUMsT0FBTyxJQUFJOzRCQUNuSSxnRUFBZ0UsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO29CQUNwRyxDQUFDO2dCQUNILENBQUM7Z0JBQ0QsT0FBTztZQUNULENBQUM7WUFFRCxJQUFJLE1BQU0sSUFBSSxPQUFPLE1BQU0sS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDekMsSUFBSSxJQUFJLENBQUMsUUFBUTtvQkFBRSxPQUFPO2dCQUUxQixNQUFNLE9BQU8sR0FBRyxNQUF1QyxDQUFDO2dCQUN4RCxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUMvQixNQUFNLEVBQ04sT0FBTyxDQUFDLFNBQVMsRUFDakIsT0FBTyxDQUFDLFVBQVUsQ0FDbkIsQ0FBQztnQkFDRixNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxpREFBaUQsTUFBTSxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQztnQkFFNUcsNEJBQTRCO2dCQUM1QixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDO29CQUNsQyxJQUFJLENBQUM7d0JBQ0gsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxTQUFTLEVBQUUsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDO29CQUNwRixDQUFDO29CQUFDLE9BQU8sUUFBYSxFQUFFLENBQUM7d0JBQ3ZCLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLCtCQUErQixNQUFNLEtBQUssUUFBUSxDQUFDLE9BQU8sRUFBRSxFQUFFLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7b0JBQ2pILENBQUM7Z0JBQ0gsQ0FBQztnQkFFRCxJQUFJLENBQUMsSUFBSSxDQUFDLG9CQUFvQixFQUFFO29CQUM5QixNQUFNO29CQUNOLFVBQVUsRUFBRSxVQUFVLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztvQkFDdkcsTUFBTTtpQkFDMkIsQ0FBQyxDQUFDO1lBQ3ZDLENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxHQUFRLEVBQUUsQ0FBQztZQUNsQixNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxvQ0FBb0MsTUFBTSxLQUFLLEdBQUcsQ0FBQyxPQUFPLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO1lBRS9HLElBQUksQ0FBQyxJQUFJLENBQUMsb0JBQW9CLEVBQUU7Z0JBQzlCLE1BQU07Z0JBQ04sS0FBSyxFQUFFLEdBQUcsQ0FBQyxPQUFPO2dCQUNsQixNQUFNO2FBQzJCLENBQUMsQ0FBQztZQUVyQyxtREFBbUQ7WUFDbkQsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLDJCQUEyQixLQUFLLEtBQUssSUFBSSxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBQ3RFLElBQUksQ0FBQztvQkFDSCxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsb0JBQW9CLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO29CQUNuRCxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxpQ0FBaUMsTUFBTSxZQUFZLEtBQUssQ0FBQyxJQUFJLEdBQUcsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO2dCQUNySCxDQUFDO2dCQUFDLE9BQU8sT0FBWSxFQUFFLENBQUM7b0JBQ3RCLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLGlDQUFpQyxNQUFNLEtBQUssT0FBTyxDQUFDLE9BQU8sRUFBRTt3QkFDOUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQjs0QkFDL0IsQ0FBQyxDQUFDLCtEQUErRDs0QkFDakUsQ0FBQyxDQUFDLG1EQUFtRCxDQUFDLEVBQUUsRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQztnQkFDNUYsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssV0FBVyxDQUFJLE9BQW1CLEVBQUUsRUFBVSxFQUFFLE9BQWU7UUFDckUsT0FBTyxJQUFJLE9BQU8sQ0FBSSxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtZQUN4QyxNQUFNLEtBQUssR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDL0QsT0FBTyxDQUFDLElBQUksQ0FDVixDQUFDLEdBQUcsRUFBRSxFQUFFLEdBQUcsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUMvQyxDQUFDLEdBQUcsRUFBRSxFQUFFLEdBQUcsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUMvQyxDQUFDO1FBQ0osQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ssbUNBQW1DLENBQUMsVUFBb0I7UUFDOUQsTUFBTSxNQUFNLEdBQWEsRUFBRSxDQUFDO1FBQzVCLEtBQUssTUFBTSxHQUFHLElBQUksVUFBVSxFQUFFLENBQUM7WUFDN0Isb0NBQW9DO1lBQ3BDLElBQUksQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3ZCLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2pCLFNBQVM7WUFDWCxDQUFDO1lBRUQsZ0NBQWdDO1lBQ2hDLElBQUksR0FBRyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3hELE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2pCLFNBQVM7WUFDWCxDQUFDO1lBRUQsaUVBQWlFO1lBQ2pFLHlDQUF5QztZQUN6QyxJQUFJLEdBQUcsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDaEYsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLG1CQUFtQjtnQkFDcEQsTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztnQkFDeEIsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLFVBQVUsRUFBRSxDQUFDLENBQUM7Z0JBQy9CLFNBQVM7WUFDWCxDQUFDO1lBRUQsb0RBQW9EO1lBQ3BELE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLDREQUE0RCxHQUFHLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO1FBQ3RILENBQUM7UUFDRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRU8sYUFBYSxDQUFDLE1BQWM7UUFDbEMsSUFBSSxDQUFDLE1BQU0sSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUM7WUFBRSxPQUFPLEtBQUssQ0FBQztRQUNqRCxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDO1lBQUUsT0FBTyxLQUFLLENBQUM7UUFDdkMsTUFBTSxnQkFBZ0IsR0FDcEIsK0ZBQStGLENBQUM7UUFDbEcsT0FBTyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdkMsQ0FBQztDQUNGIn0=
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async concurrency semaphore — limits the number of concurrent async operations.
|
|
3
|
+
*/
|
|
4
|
+
export declare class ConcurrencySemaphore {
|
|
5
|
+
private readonly maxConcurrency;
|
|
6
|
+
private running;
|
|
7
|
+
private waitQueue;
|
|
8
|
+
constructor(maxConcurrency: number);
|
|
9
|
+
acquire(): Promise<void>;
|
|
10
|
+
release(): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async concurrency semaphore — limits the number of concurrent async operations.
|
|
3
|
+
*/
|
|
4
|
+
export class ConcurrencySemaphore {
|
|
5
|
+
constructor(maxConcurrency) {
|
|
6
|
+
this.maxConcurrency = maxConcurrency;
|
|
7
|
+
this.running = 0;
|
|
8
|
+
this.waitQueue = [];
|
|
9
|
+
}
|
|
10
|
+
async acquire() {
|
|
11
|
+
if (this.running < this.maxConcurrency) {
|
|
12
|
+
this.running++;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
this.waitQueue.push(() => {
|
|
17
|
+
this.running++;
|
|
18
|
+
resolve();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
release() {
|
|
23
|
+
this.running--;
|
|
24
|
+
const next = this.waitQueue.shift();
|
|
25
|
+
if (next)
|
|
26
|
+
next();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uY3VycmVuY3ktc2VtYXBob3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvcHJveGllcy9zbWFydC1wcm94eS91dGlscy9jb25jdXJyZW5jeS1zZW1hcGhvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFDSCxNQUFNLE9BQU8sb0JBQW9CO0lBSS9CLFlBQTZCLGNBQXNCO1FBQXRCLG1CQUFjLEdBQWQsY0FBYyxDQUFRO1FBSDNDLFlBQU8sR0FBRyxDQUFDLENBQUM7UUFDWixjQUFTLEdBQXNCLEVBQUUsQ0FBQztJQUVZLENBQUM7SUFFdkQsS0FBSyxDQUFDLE9BQU87UUFDWCxJQUFJLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3ZDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNmLE9BQU87UUFDVCxDQUFDO1FBQ0QsT0FBTyxJQUFJLE9BQU8sQ0FBTyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQ25DLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRTtnQkFDdkIsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO2dCQUNmLE9BQU8sRUFBRSxDQUFDO1lBQ1osQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxPQUFPO1FBQ0wsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2YsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNwQyxJQUFJLElBQUk7WUFBRSxJQUFJLEVBQUUsQ0FBQztJQUNuQixDQUFDO0NBQ0YifQ==
|
|
@@ -8,4 +8,5 @@ export * from './route-helpers.js';
|
|
|
8
8
|
export * from './route-validator.js';
|
|
9
9
|
export * from './route-utils.js';
|
|
10
10
|
export { generateDefaultCertificate } from './default-cert-generator.js';
|
|
11
|
+
export { ConcurrencySemaphore } from './concurrency-semaphore.js';
|
|
11
12
|
export { createApiGatewayRoute, addRateLimiting, addBasicAuth, addJwtAuth } from './route-helpers.js';
|
|
@@ -12,6 +12,8 @@ export * from './route-validator.js';
|
|
|
12
12
|
export * from './route-utils.js';
|
|
13
13
|
// Export default certificate generator
|
|
14
14
|
export { generateDefaultCertificate } from './default-cert-generator.js';
|
|
15
|
+
// Export concurrency semaphore
|
|
16
|
+
export { ConcurrencySemaphore } from './concurrency-semaphore.js';
|
|
15
17
|
// Export additional functions from route-helpers that weren't already exported
|
|
16
18
|
export { createApiGatewayRoute, addRateLimiting, addBasicAuth, addJwtAuth } from './route-helpers.js';
|
|
17
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3V0aWxzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7OztHQUtHO0FBRUgseURBQXlEO0FBQ3pELGNBQWMsb0JBQW9CLENBQUM7QUFFbkMsMERBQTBEO0FBQzFELGNBQWMsc0JBQXNCLENBQUM7QUFFckMsOENBQThDO0FBQzlDLGNBQWMsa0JBQWtCLENBQUM7QUFFakMsdUNBQXVDO0FBQ3ZDLE9BQU8sRUFBRSwwQkFBMEIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXpFLCtCQUErQjtBQUMvQixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUVsRSwrRUFBK0U7QUFDL0UsT0FBTyxFQUNMLHFCQUFxQixFQUNyQixlQUFlLEVBQ2YsWUFBWSxFQUNaLFVBQVUsRUFDWCxNQUFNLG9CQUFvQixDQUFDIn0=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartproxy",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '25.
|
|
6
|
+
version: '25.3.0',
|
|
7
7
|
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
|
|
8
8
|
}
|
|
@@ -180,6 +180,21 @@ export interface ISmartProxyOptions {
|
|
|
180
180
|
*/
|
|
181
181
|
certProvisionFallbackToAcme?: boolean;
|
|
182
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Per-domain timeout in ms for certProvisionFunction calls.
|
|
185
|
+
* If a single domain's provisioning takes longer than this, it's aborted
|
|
186
|
+
* and a certificate-failed event is emitted.
|
|
187
|
+
* Default: 300000 (5 minutes)
|
|
188
|
+
*/
|
|
189
|
+
certProvisionTimeout?: number;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Maximum number of domains to provision certificates for concurrently.
|
|
193
|
+
* Prevents overwhelming ACME providers when many domains provision at once.
|
|
194
|
+
* Default: 4
|
|
195
|
+
*/
|
|
196
|
+
certProvisionConcurrency?: number;
|
|
197
|
+
|
|
183
198
|
/**
|
|
184
199
|
* Disable the default self-signed fallback certificate.
|
|
185
200
|
* When false (default), a self-signed cert is generated at startup and loaded
|
|
@@ -12,6 +12,7 @@ import { SharedRouteManager as RouteManager } from '../../core/routing/route-man
|
|
|
12
12
|
import { RouteValidator } from './utils/route-validator.js';
|
|
13
13
|
import { generateDefaultCertificate } from './utils/default-cert-generator.js';
|
|
14
14
|
import { Mutex } from './utils/mutex.js';
|
|
15
|
+
import { ConcurrencySemaphore } from './utils/concurrency-semaphore.js';
|
|
15
16
|
|
|
16
17
|
// Types
|
|
17
18
|
import type { ISmartProxyOptions, TSmartProxyCertProvisionObject, IAcmeOptions, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent } from './models/interfaces.js';
|
|
@@ -38,6 +39,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
38
39
|
private metricsAdapter: RustMetricsAdapter;
|
|
39
40
|
private routeUpdateLock: Mutex;
|
|
40
41
|
private stopping = false;
|
|
42
|
+
private certProvisionPromise: Promise<void> | null = null;
|
|
41
43
|
|
|
42
44
|
constructor(settingsArg: ISmartProxyOptions) {
|
|
43
45
|
super();
|
|
@@ -191,13 +193,18 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
191
193
|
}
|
|
192
194
|
}
|
|
193
195
|
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
//
|
|
196
|
+
// Start metrics polling BEFORE cert provisioning — the Rust engine is already
|
|
197
|
+
// running and accepting connections, so metrics should be available immediately.
|
|
198
|
+
// Cert provisioning can hang indefinitely (e.g. DNS-01 ACME timeouts) and must
|
|
199
|
+
// not block metrics collection.
|
|
198
200
|
this.metricsAdapter.startPolling();
|
|
199
201
|
|
|
200
202
|
logger.log('info', 'SmartProxy started (Rust engine)', { component: 'smart-proxy' });
|
|
203
|
+
|
|
204
|
+
// Fire-and-forget cert provisioning — Rust engine is already running and serving traffic.
|
|
205
|
+
// Events (certificate-issued / certificate-failed) fire independently per domain.
|
|
206
|
+
this.certProvisionPromise = this.provisionCertificatesViaCallback(preloadedDomains)
|
|
207
|
+
.catch((err) => logger.log('error', `Unexpected error in cert provisioning: ${err.message}`, { component: 'smart-proxy' }));
|
|
201
208
|
}
|
|
202
209
|
|
|
203
210
|
/**
|
|
@@ -207,6 +214,12 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
207
214
|
logger.log('info', 'SmartProxy shutting down...', { component: 'smart-proxy' });
|
|
208
215
|
this.stopping = true;
|
|
209
216
|
|
|
217
|
+
// Wait for in-flight cert provisioning to bail out (it checks this.stopping)
|
|
218
|
+
if (this.certProvisionPromise) {
|
|
219
|
+
await this.certProvisionPromise;
|
|
220
|
+
this.certProvisionPromise = null;
|
|
221
|
+
}
|
|
222
|
+
|
|
210
223
|
// Stop metrics polling
|
|
211
224
|
this.metricsAdapter.stopPolling();
|
|
212
225
|
|
|
@@ -234,7 +247,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
234
247
|
* Update routes atomically.
|
|
235
248
|
*/
|
|
236
249
|
public async updateRoutes(newRoutes: IRouteConfig[]): Promise<void> {
|
|
237
|
-
|
|
250
|
+
await this.routeUpdateLock.runExclusive(async () => {
|
|
238
251
|
// Validate
|
|
239
252
|
const validation = RouteValidator.validateRoutes(newRoutes);
|
|
240
253
|
if (!validation.valid) {
|
|
@@ -270,11 +283,13 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
270
283
|
// Update stored routes
|
|
271
284
|
this.settings.routes = newRoutes;
|
|
272
285
|
|
|
273
|
-
// Handle cert provisioning for new routes
|
|
274
|
-
await this.provisionCertificatesViaCallback();
|
|
275
|
-
|
|
276
286
|
logger.log('info', `Routes updated (${newRoutes.length} routes)`, { component: 'smart-proxy' });
|
|
277
287
|
});
|
|
288
|
+
|
|
289
|
+
// Fire-and-forget cert provisioning outside the mutex — routes are already updated,
|
|
290
|
+
// cert provisioning doesn't need the route update lock and may be slow.
|
|
291
|
+
this.certProvisionPromise = this.provisionCertificatesViaCallback()
|
|
292
|
+
.catch((err) => logger.log('error', `Unexpected error in cert provisioning after route update: ${err.message}`, { component: 'smart-proxy' }));
|
|
278
293
|
}
|
|
279
294
|
|
|
280
295
|
/**
|
|
@@ -409,7 +424,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
409
424
|
const provisionFn = this.settings.certProvisionFunction;
|
|
410
425
|
if (!provisionFn) return;
|
|
411
426
|
|
|
412
|
-
|
|
427
|
+
// Phase 1: Collect all unique (domain, route) pairs that need provisioning
|
|
428
|
+
const seen = new Set<string>(skipDomains);
|
|
429
|
+
const tasks: Array<{ domain: string; route: IRouteConfig }> = [];
|
|
413
430
|
|
|
414
431
|
for (const route of this.settings.routes) {
|
|
415
432
|
if (route.action.tls?.certificate !== 'auto') continue;
|
|
@@ -419,91 +436,139 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
419
436
|
const certDomains = this.normalizeDomainsForCertProvisioning(rawDomains);
|
|
420
437
|
|
|
421
438
|
for (const domain of certDomains) {
|
|
422
|
-
if (
|
|
423
|
-
|
|
439
|
+
if (seen.has(domain)) continue;
|
|
440
|
+
seen.add(domain);
|
|
441
|
+
tasks.push({ domain, route });
|
|
442
|
+
}
|
|
443
|
+
}
|
|
424
444
|
|
|
425
|
-
|
|
426
|
-
let expiryDate: string | undefined;
|
|
427
|
-
let source = 'certProvisionFunction';
|
|
445
|
+
if (tasks.length === 0) return;
|
|
428
446
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
error: (msg) => logger.log('error', `[certProvision ${domain}] ${msg}`, { component: 'smart-proxy' }),
|
|
433
|
-
setExpiryDate: (date) => { expiryDate = date.toISOString(); },
|
|
434
|
-
setSource: (s) => { source = s; },
|
|
435
|
-
};
|
|
447
|
+
// Phase 2: Process all domains in parallel with concurrency limit
|
|
448
|
+
const concurrency = this.settings.certProvisionConcurrency ?? 4;
|
|
449
|
+
const semaphore = new ConcurrencySemaphore(concurrency);
|
|
436
450
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
451
|
+
const promises = tasks.map(async ({ domain, route }) => {
|
|
452
|
+
await semaphore.acquire();
|
|
453
|
+
try {
|
|
454
|
+
await this.provisionSingleDomain(domain, route, provisionFn);
|
|
455
|
+
} finally {
|
|
456
|
+
semaphore.release();
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
await Promise.allSettled(promises);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Provision a single domain's certificate via the callback.
|
|
465
|
+
* Includes per-domain timeout and shutdown checks.
|
|
466
|
+
*/
|
|
467
|
+
private async provisionSingleDomain(
|
|
468
|
+
domain: string,
|
|
469
|
+
route: IRouteConfig,
|
|
470
|
+
provisionFn: (domain: string, eventComms: ICertProvisionEventComms) => Promise<TSmartProxyCertProvisionObject>,
|
|
471
|
+
): Promise<void> {
|
|
472
|
+
if (this.stopping) return;
|
|
473
|
+
|
|
474
|
+
let expiryDate: string | undefined;
|
|
475
|
+
let source = 'certProvisionFunction';
|
|
476
|
+
|
|
477
|
+
const eventComms: ICertProvisionEventComms = {
|
|
478
|
+
log: (msg) => logger.log('info', `[certProvision ${domain}] ${msg}`, { component: 'smart-proxy' }),
|
|
479
|
+
warn: (msg) => logger.log('warn', `[certProvision ${domain}] ${msg}`, { component: 'smart-proxy' }),
|
|
480
|
+
error: (msg) => logger.log('error', `[certProvision ${domain}] ${msg}`, { component: 'smart-proxy' }),
|
|
481
|
+
setExpiryDate: (date) => { expiryDate = date.toISOString(); },
|
|
482
|
+
setSource: (s) => { source = s; },
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const timeoutMs = this.settings.certProvisionTimeout ?? 300_000; // 5 min default
|
|
453
486
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// Emit certificate-issued event
|
|
474
|
-
this.emit('certificate-issued', {
|
|
475
|
-
domain,
|
|
476
|
-
expiryDate: expiryDate || (certObj.validUntil ? new Date(certObj.validUntil).toISOString() : undefined),
|
|
477
|
-
source,
|
|
478
|
-
} satisfies ICertificateIssuedEvent);
|
|
487
|
+
try {
|
|
488
|
+
const result: TSmartProxyCertProvisionObject = await this.withTimeout(
|
|
489
|
+
provisionFn(domain, eventComms),
|
|
490
|
+
timeoutMs,
|
|
491
|
+
`Certificate provisioning timed out for ${domain} after ${timeoutMs}ms`,
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
if (this.stopping) return;
|
|
495
|
+
|
|
496
|
+
if (result === 'http01') {
|
|
497
|
+
if (route.name) {
|
|
498
|
+
try {
|
|
499
|
+
await this.bridge.provisionCertificate(route.name);
|
|
500
|
+
logger.log('info', `Triggered Rust ACME for ${domain} (route: ${route.name})`, { component: 'smart-proxy' });
|
|
501
|
+
} catch (provisionErr: any) {
|
|
502
|
+
logger.log('warn', `Cannot provision cert for ${domain} — callback returned 'http01' but Rust ACME failed: ${provisionErr.message}. ` +
|
|
503
|
+
'Note: Rust ACME is disabled when certProvisionFunction is set.', { component: 'smart-proxy' });
|
|
479
504
|
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
505
|
+
}
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (result && typeof result === 'object') {
|
|
510
|
+
if (this.stopping) return;
|
|
511
|
+
|
|
512
|
+
const certObj = result as plugins.tsclass.network.ICert;
|
|
513
|
+
await this.bridge.loadCertificate(
|
|
514
|
+
domain,
|
|
515
|
+
certObj.publicKey,
|
|
516
|
+
certObj.privateKey,
|
|
517
|
+
);
|
|
518
|
+
logger.log('info', `Certificate loaded via provision function for ${domain}`, { component: 'smart-proxy' });
|
|
519
|
+
|
|
520
|
+
// Persist to consumer store
|
|
521
|
+
if (this.settings.certStore?.save) {
|
|
522
|
+
try {
|
|
523
|
+
await this.settings.certStore.save(domain, certObj.publicKey, certObj.privateKey);
|
|
524
|
+
} catch (storeErr: any) {
|
|
525
|
+
logger.log('warn', `certStore.save() failed for ${domain}: ${storeErr.message}`, { component: 'smart-proxy' });
|
|
501
526
|
}
|
|
502
527
|
}
|
|
528
|
+
|
|
529
|
+
this.emit('certificate-issued', {
|
|
530
|
+
domain,
|
|
531
|
+
expiryDate: expiryDate || (certObj.validUntil ? new Date(certObj.validUntil).toISOString() : undefined),
|
|
532
|
+
source,
|
|
533
|
+
} satisfies ICertificateIssuedEvent);
|
|
534
|
+
}
|
|
535
|
+
} catch (err: any) {
|
|
536
|
+
logger.log('warn', `certProvisionFunction failed for ${domain}: ${err.message}`, { component: 'smart-proxy' });
|
|
537
|
+
|
|
538
|
+
this.emit('certificate-failed', {
|
|
539
|
+
domain,
|
|
540
|
+
error: err.message,
|
|
541
|
+
source,
|
|
542
|
+
} satisfies ICertificateFailedEvent);
|
|
543
|
+
|
|
544
|
+
// Fallback to ACME if enabled and route has a name
|
|
545
|
+
if (this.settings.certProvisionFallbackToAcme !== false && route.name) {
|
|
546
|
+
try {
|
|
547
|
+
await this.bridge.provisionCertificate(route.name);
|
|
548
|
+
logger.log('info', `Falling back to Rust ACME for ${domain} (route: ${route.name})`, { component: 'smart-proxy' });
|
|
549
|
+
} catch (acmeErr: any) {
|
|
550
|
+
logger.log('warn', `ACME fallback also failed for ${domain}: ${acmeErr.message}` +
|
|
551
|
+
(this.settings.disableDefaultCert
|
|
552
|
+
? ' — TLS will fail for this domain (disableDefaultCert is true)'
|
|
553
|
+
: ' — default self-signed fallback cert will be used'), { component: 'smart-proxy' });
|
|
554
|
+
}
|
|
503
555
|
}
|
|
504
556
|
}
|
|
505
557
|
}
|
|
506
558
|
|
|
559
|
+
/**
|
|
560
|
+
* Race a promise against a timeout. Rejects with the given message if the timeout fires first.
|
|
561
|
+
*/
|
|
562
|
+
private withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T> {
|
|
563
|
+
return new Promise<T>((resolve, reject) => {
|
|
564
|
+
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
565
|
+
promise.then(
|
|
566
|
+
(val) => { clearTimeout(timer); resolve(val); },
|
|
567
|
+
(err) => { clearTimeout(timer); reject(err); },
|
|
568
|
+
);
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
|
|
507
572
|
/**
|
|
508
573
|
* Normalize routing glob patterns into valid domain identifiers for cert provisioning.
|
|
509
574
|
* - `*nevermind.cloud` → `['nevermind.cloud', '*.nevermind.cloud']`
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async concurrency semaphore — limits the number of concurrent async operations.
|
|
3
|
+
*/
|
|
4
|
+
export class ConcurrencySemaphore {
|
|
5
|
+
private running = 0;
|
|
6
|
+
private waitQueue: Array<() => void> = [];
|
|
7
|
+
|
|
8
|
+
constructor(private readonly maxConcurrency: number) {}
|
|
9
|
+
|
|
10
|
+
async acquire(): Promise<void> {
|
|
11
|
+
if (this.running < this.maxConcurrency) {
|
|
12
|
+
this.running++;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
return new Promise<void>((resolve) => {
|
|
16
|
+
this.waitQueue.push(() => {
|
|
17
|
+
this.running++;
|
|
18
|
+
resolve();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
release(): void {
|
|
24
|
+
this.running--;
|
|
25
|
+
const next = this.waitQueue.shift();
|
|
26
|
+
if (next) next();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -17,6 +17,9 @@ export * from './route-utils.js';
|
|
|
17
17
|
// Export default certificate generator
|
|
18
18
|
export { generateDefaultCertificate } from './default-cert-generator.js';
|
|
19
19
|
|
|
20
|
+
// Export concurrency semaphore
|
|
21
|
+
export { ConcurrencySemaphore } from './concurrency-semaphore.js';
|
|
22
|
+
|
|
20
23
|
// Export additional functions from route-helpers that weren't already exported
|
|
21
24
|
export {
|
|
22
25
|
createApiGatewayRoute,
|