@authrim/setup 0.1.22 → 0.1.24
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/dist/cli/commands/init.js +180 -46
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/core/config.d.ts +42 -4
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +13 -2
- package/dist/core/config.js.map +1 -1
- package/dist/core/wrangler.d.ts.map +1 -1
- package/dist/core/wrangler.js +14 -0
- package/dist/core/wrangler.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +2 -0
- package/dist/web/api.js.map +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +194 -23
- package/dist/web/ui.js.map +1 -1
- package/package.json +1 -1
package/dist/web/ui.js
CHANGED
|
@@ -197,6 +197,37 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
197
197
|
gap: 0.5rem;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
/* Infrastructure info section */
|
|
201
|
+
.infra-section {
|
|
202
|
+
background: #f8fafc;
|
|
203
|
+
border: 1px solid var(--border);
|
|
204
|
+
border-radius: 8px;
|
|
205
|
+
padding: 1rem;
|
|
206
|
+
margin-bottom: 1rem;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.infra-section h4 {
|
|
210
|
+
margin: 0 0 0.75rem 0;
|
|
211
|
+
font-size: 0.9rem;
|
|
212
|
+
color: var(--text-muted);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.infra-item {
|
|
216
|
+
display: flex;
|
|
217
|
+
justify-content: space-between;
|
|
218
|
+
padding: 0.25rem 0;
|
|
219
|
+
font-size: 0.85rem;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.infra-label {
|
|
223
|
+
color: var(--text-muted);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.infra-value {
|
|
227
|
+
font-family: monospace;
|
|
228
|
+
color: var(--primary);
|
|
229
|
+
}
|
|
230
|
+
|
|
200
231
|
/* Domain configuration section */
|
|
201
232
|
.domain-section {
|
|
202
233
|
background: var(--bg);
|
|
@@ -207,7 +238,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
207
238
|
}
|
|
208
239
|
|
|
209
240
|
.domain-section h4 {
|
|
210
|
-
margin: 0 0
|
|
241
|
+
margin: 0 0 0.5rem 0;
|
|
211
242
|
font-size: 0.95rem;
|
|
212
243
|
color: var(--text);
|
|
213
244
|
display: flex;
|
|
@@ -215,6 +246,15 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
215
246
|
gap: 0.5rem;
|
|
216
247
|
}
|
|
217
248
|
|
|
249
|
+
.domain-section .section-hint {
|
|
250
|
+
font-size: 0.8rem;
|
|
251
|
+
color: var(--text-muted);
|
|
252
|
+
margin-bottom: 1rem;
|
|
253
|
+
padding: 0.5rem;
|
|
254
|
+
background: #f0f9ff;
|
|
255
|
+
border-radius: 4px;
|
|
256
|
+
}
|
|
257
|
+
|
|
218
258
|
.domain-row {
|
|
219
259
|
display: grid;
|
|
220
260
|
grid-template-columns: 90px 1fr;
|
|
@@ -257,6 +297,25 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
257
297
|
white-space: nowrap;
|
|
258
298
|
}
|
|
259
299
|
|
|
300
|
+
.issuer-preview {
|
|
301
|
+
margin-top: 0.75rem;
|
|
302
|
+
padding: 0.5rem;
|
|
303
|
+
background: #f0fdf4;
|
|
304
|
+
border-radius: 4px;
|
|
305
|
+
font-size: 0.85rem;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.issuer-preview .label {
|
|
309
|
+
color: var(--text-muted);
|
|
310
|
+
font-size: 0.75rem;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.issuer-preview .value {
|
|
314
|
+
color: #16a34a;
|
|
315
|
+
font-family: monospace;
|
|
316
|
+
word-break: break-all;
|
|
317
|
+
}
|
|
318
|
+
|
|
260
319
|
button {
|
|
261
320
|
padding: 0.75rem 1.5rem;
|
|
262
321
|
border-radius: 8px;
|
|
@@ -799,30 +858,88 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
799
858
|
<small style="color: var(--text-muted)">Lowercase letters, numbers, and hyphens only</small>
|
|
800
859
|
</div>
|
|
801
860
|
|
|
802
|
-
<!--
|
|
803
|
-
<div class="
|
|
804
|
-
<
|
|
805
|
-
<
|
|
806
|
-
|
|
861
|
+
<!-- Infrastructure Info (read-only, auto-generated) -->
|
|
862
|
+
<div class="infra-section">
|
|
863
|
+
<h4>🔧 Infrastructure (Auto-generated from Environment)</h4>
|
|
864
|
+
<div class="infra-item">
|
|
865
|
+
<span class="infra-label">Worker Prefix:</span>
|
|
866
|
+
<span class="infra-value" id="infra-worker-prefix">{env}-ar-*</span>
|
|
867
|
+
</div>
|
|
868
|
+
<div class="infra-item">
|
|
869
|
+
<span class="infra-label">Default API:</span>
|
|
870
|
+
<span class="infra-value" id="infra-default-api">{env}-ar-router.workers.dev</span>
|
|
871
|
+
</div>
|
|
872
|
+
<div class="infra-item">
|
|
873
|
+
<span class="infra-label">Default UI:</span>
|
|
874
|
+
<span class="infra-value" id="infra-default-ui">{env}-ar-ui.pages.dev</span>
|
|
875
|
+
</div>
|
|
807
876
|
</div>
|
|
808
877
|
|
|
878
|
+
<!-- Tenant Mode Selection -->
|
|
809
879
|
<div class="form-group">
|
|
810
|
-
<label
|
|
811
|
-
|
|
812
|
-
|
|
880
|
+
<label class="checkbox-item" style="display: flex; align-items: center; gap: 0.5rem;">
|
|
881
|
+
<input type="checkbox" id="multi-tenant">
|
|
882
|
+
<span>Enable multi-tenant mode</span>
|
|
883
|
+
</label>
|
|
884
|
+
<small style="color: var(--text-muted); margin-left: 1.5rem;">
|
|
885
|
+
Subdomain-based tenant isolation: https://{tenant}.{base-domain}
|
|
886
|
+
</small>
|
|
813
887
|
</div>
|
|
814
888
|
|
|
815
|
-
<!-- Domain
|
|
816
|
-
<div class="domain-section">
|
|
817
|
-
<h4>🌐 Domain
|
|
818
|
-
|
|
889
|
+
<!-- Single-tenant: Issuer/API Domain -->
|
|
890
|
+
<div id="single-tenant-domain" class="domain-section">
|
|
891
|
+
<h4>🌐 Issuer & API Domain</h4>
|
|
892
|
+
<div class="section-hint">
|
|
893
|
+
In single-tenant mode, Issuer URL = API domain. Leave empty to use workers.dev.
|
|
894
|
+
</div>
|
|
819
895
|
<div class="domain-row">
|
|
820
|
-
<span class="domain-label">API</span>
|
|
896
|
+
<span class="domain-label">Issuer/API</span>
|
|
821
897
|
<div class="domain-input-wrapper">
|
|
822
898
|
<input type="text" id="api-domain" placeholder="auth.example.com">
|
|
823
899
|
<span class="domain-default" id="api-default">{env}-ar-router.workers.dev</span>
|
|
824
900
|
</div>
|
|
825
901
|
</div>
|
|
902
|
+
<div class="issuer-preview" id="issuer-preview-single">
|
|
903
|
+
<span class="label">Issuer URL:</span>
|
|
904
|
+
<span class="value" id="issuer-url-single">https://{env}-ar-router.workers.dev</span>
|
|
905
|
+
</div>
|
|
906
|
+
</div>
|
|
907
|
+
|
|
908
|
+
<!-- Multi-tenant: Base Domain (issuer derived from subdomain) -->
|
|
909
|
+
<div id="multi-tenant-domain" class="domain-section" style="display: none;">
|
|
910
|
+
<h4>🌐 Multi-tenant Base Domain</h4>
|
|
911
|
+
<div class="section-hint">
|
|
912
|
+
Each tenant gets a subdomain. The base domain points to the router Worker.
|
|
913
|
+
</div>
|
|
914
|
+
<div class="form-group" style="margin-bottom: 0.75rem;">
|
|
915
|
+
<label for="base-domain">Base Domain <span style="color: var(--error);">*</span></label>
|
|
916
|
+
<input type="text" id="base-domain" placeholder="authrim.com">
|
|
917
|
+
</div>
|
|
918
|
+
<div class="issuer-preview" id="issuer-preview-multi">
|
|
919
|
+
<span class="label">Issuer URL format:</span>
|
|
920
|
+
<span class="value" id="issuer-url-multi">https://{tenant}.{base-domain}</span>
|
|
921
|
+
</div>
|
|
922
|
+
</div>
|
|
923
|
+
|
|
924
|
+
<!-- Tenant Details -->
|
|
925
|
+
<div class="form-group">
|
|
926
|
+
<label for="tenant-name">Default Tenant Name</label>
|
|
927
|
+
<input type="text" id="tenant-name" placeholder="default" value="default">
|
|
928
|
+
<small style="color: var(--text-muted)">Identifier for default tenant (lowercase, no spaces)</small>
|
|
929
|
+
</div>
|
|
930
|
+
|
|
931
|
+
<div class="form-group">
|
|
932
|
+
<label for="tenant-display">Organization / Display Name</label>
|
|
933
|
+
<input type="text" id="tenant-display" placeholder="My Organization" value="Default Tenant">
|
|
934
|
+
<small style="color: var(--text-muted)">Human-readable name shown to users</small>
|
|
935
|
+
</div>
|
|
936
|
+
|
|
937
|
+
<!-- UI Domain Configuration -->
|
|
938
|
+
<div class="domain-section">
|
|
939
|
+
<h4>🖥️ UI Domains (Optional)</h4>
|
|
940
|
+
<div class="section-hint">
|
|
941
|
+
Custom domains for Login and Admin UIs. Leave empty to use Cloudflare Pages default.
|
|
942
|
+
</div>
|
|
826
943
|
|
|
827
944
|
<div class="domain-row">
|
|
828
945
|
<span class="domain-label">Login UI</span>
|
|
@@ -839,10 +956,6 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
839
956
|
<span class="domain-default" id="admin-default">{env}-ar-ui.pages.dev/admin</span>
|
|
840
957
|
</div>
|
|
841
958
|
</div>
|
|
842
|
-
|
|
843
|
-
<small style="color: var(--text-muted); display: block; margin-top: 0.75rem;">
|
|
844
|
-
💡 Leave empty to use default Cloudflare domains (shown on right)
|
|
845
|
-
</small>
|
|
846
959
|
</div>
|
|
847
960
|
|
|
848
961
|
<!-- Advanced options (shown in custom mode) -->
|
|
@@ -1456,12 +1569,53 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1456
1569
|
});
|
|
1457
1570
|
|
|
1458
1571
|
// Configuration handlers
|
|
1459
|
-
// Update domain defaults when environment name changes
|
|
1460
|
-
|
|
1461
|
-
|
|
1572
|
+
// Update infrastructure info and domain defaults when environment name changes
|
|
1573
|
+
function updateEnvDisplay(env) {
|
|
1574
|
+
env = env || '{env}';
|
|
1575
|
+
// Infrastructure section
|
|
1576
|
+
document.getElementById('infra-worker-prefix').textContent = env + '-ar-*';
|
|
1577
|
+
document.getElementById('infra-default-api').textContent = env + '-ar-router.workers.dev';
|
|
1578
|
+
document.getElementById('infra-default-ui').textContent = env + '-ar-ui.pages.dev';
|
|
1579
|
+
// Domain defaults
|
|
1462
1580
|
document.getElementById('api-default').textContent = env + '-ar-router.workers.dev';
|
|
1463
1581
|
document.getElementById('login-default').textContent = env + '-ar-ui.pages.dev';
|
|
1464
1582
|
document.getElementById('admin-default').textContent = env + '-ar-ui.pages.dev/admin';
|
|
1583
|
+
// Single-tenant issuer preview
|
|
1584
|
+
const apiDomain = document.getElementById('api-domain').value.trim();
|
|
1585
|
+
document.getElementById('issuer-url-single').textContent =
|
|
1586
|
+
apiDomain ? 'https://' + apiDomain : 'https://' + env + '-ar-router.workers.dev';
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
document.getElementById('env').addEventListener('input', (e) => {
|
|
1590
|
+
const env = e.target.value.trim().toLowerCase().replace(/[^a-z0-9-]/g, '') || '{env}';
|
|
1591
|
+
updateEnvDisplay(env);
|
|
1592
|
+
});
|
|
1593
|
+
|
|
1594
|
+
// Update issuer preview when API domain changes
|
|
1595
|
+
document.getElementById('api-domain').addEventListener('input', (e) => {
|
|
1596
|
+
const env = document.getElementById('env').value.trim().toLowerCase().replace(/[^a-z0-9-]/g, '') || '{env}';
|
|
1597
|
+
const apiDomain = e.target.value.trim();
|
|
1598
|
+
document.getElementById('issuer-url-single').textContent =
|
|
1599
|
+
apiDomain ? 'https://' + apiDomain : 'https://' + env + '-ar-router.workers.dev';
|
|
1600
|
+
});
|
|
1601
|
+
|
|
1602
|
+
// Update multi-tenant issuer preview when base domain changes
|
|
1603
|
+
document.getElementById('base-domain').addEventListener('input', (e) => {
|
|
1604
|
+
const baseDomain = e.target.value.trim() || '{base-domain}';
|
|
1605
|
+
document.getElementById('issuer-url-multi').textContent = 'https://{tenant}.' + baseDomain;
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
// Multi-tenant mode toggle - show/hide relevant domain sections
|
|
1609
|
+
document.getElementById('multi-tenant').addEventListener('change', (e) => {
|
|
1610
|
+
const singleTenantDomain = document.getElementById('single-tenant-domain');
|
|
1611
|
+
const multiTenantDomain = document.getElementById('multi-tenant-domain');
|
|
1612
|
+
if (e.target.checked) {
|
|
1613
|
+
singleTenantDomain.style.display = 'none';
|
|
1614
|
+
multiTenantDomain.style.display = 'block';
|
|
1615
|
+
} else {
|
|
1616
|
+
singleTenantDomain.style.display = 'block';
|
|
1617
|
+
multiTenantDomain.style.display = 'none';
|
|
1618
|
+
}
|
|
1465
1619
|
});
|
|
1466
1620
|
|
|
1467
1621
|
document.getElementById('btn-back-mode').addEventListener('click', () => {
|
|
@@ -1500,20 +1654,37 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1500
1654
|
configureBtn.disabled = false;
|
|
1501
1655
|
}
|
|
1502
1656
|
|
|
1503
|
-
const apiDomain = document.getElementById('api-domain').value.trim();
|
|
1504
1657
|
const loginDomain = document.getElementById('login-domain').value.trim();
|
|
1505
1658
|
const adminDomain = document.getElementById('admin-domain').value.trim();
|
|
1659
|
+
const multiTenant = document.getElementById('multi-tenant').checked;
|
|
1660
|
+
const baseDomain = document.getElementById('base-domain').value.trim();
|
|
1506
1661
|
const tenantName = document.getElementById('tenant-name').value.trim() || 'default';
|
|
1507
1662
|
const tenantDisplayName = document.getElementById('tenant-display').value.trim() || 'Default Tenant';
|
|
1508
1663
|
|
|
1664
|
+
// Validate base domain if multi-tenant is enabled
|
|
1665
|
+
if (multiTenant && !baseDomain) {
|
|
1666
|
+
const result = document.getElementById('provision-output');
|
|
1667
|
+
result.textContent = '';
|
|
1668
|
+
result.appendChild(createAlert('error', 'Base domain is required for multi-tenant mode'));
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
// In multi-tenant mode, API domain = base domain (wildcard subdomain routing)
|
|
1673
|
+
// In single-tenant mode, API domain = custom domain or null (workers.dev fallback)
|
|
1674
|
+
const apiDomain = multiTenant
|
|
1675
|
+
? baseDomain
|
|
1676
|
+
: (document.getElementById('api-domain').value.trim() || null);
|
|
1677
|
+
|
|
1509
1678
|
config = {
|
|
1510
1679
|
env,
|
|
1511
|
-
apiDomain
|
|
1680
|
+
apiDomain,
|
|
1512
1681
|
loginUiDomain: loginDomain || null,
|
|
1513
1682
|
adminUiDomain: adminDomain || null,
|
|
1514
1683
|
tenant: {
|
|
1515
1684
|
name: tenantName,
|
|
1516
1685
|
displayName: tenantDisplayName,
|
|
1686
|
+
multiTenant,
|
|
1687
|
+
baseDomain: multiTenant ? baseDomain : undefined,
|
|
1517
1688
|
},
|
|
1518
1689
|
components: {
|
|
1519
1690
|
api: true,
|
package/dist/web/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,UAAU,eAAe,CAAC,YAAqB,EAAE,UAAoB;IACzE,gDAAgD;IAChD,MAAM,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,MAAM,cAAc,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAErD,OAAO
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,UAAU,eAAe,CAAC,YAAqB,EAAE,UAAoB;IACzE,gDAAgD;IAChD,MAAM,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,MAAM,cAAc,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAErD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA2tCoB,SAAS;0BACZ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8rChC,CAAC;AACT,CAAC"}
|