@authrim/setup 0.1.22 → 0.1.26
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 +235 -67
- 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;
|
|
@@ -793,37 +852,91 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
793
852
|
<div id="section-config" class="card hidden">
|
|
794
853
|
<h2 class="card-title">Configuration</h2>
|
|
795
854
|
|
|
855
|
+
<!-- 1. Components (shown in custom mode) -->
|
|
856
|
+
<div id="advanced-options" class="hidden">
|
|
857
|
+
<h3 style="margin: 0 0 1rem; font-size: 1rem;">📦 Components</h3>
|
|
858
|
+
<div class="checkbox-group">
|
|
859
|
+
<label class="checkbox-item">
|
|
860
|
+
<input type="checkbox" id="comp-api" checked disabled>
|
|
861
|
+
API (required)
|
|
862
|
+
</label>
|
|
863
|
+
<label class="checkbox-item">
|
|
864
|
+
<input type="checkbox" id="comp-login-ui" checked>
|
|
865
|
+
Login UI
|
|
866
|
+
</label>
|
|
867
|
+
<label class="checkbox-item">
|
|
868
|
+
<input type="checkbox" id="comp-admin-ui" checked>
|
|
869
|
+
Admin UI
|
|
870
|
+
</label>
|
|
871
|
+
<label class="checkbox-item">
|
|
872
|
+
<input type="checkbox" id="comp-saml">
|
|
873
|
+
SAML IdP
|
|
874
|
+
</label>
|
|
875
|
+
<label class="checkbox-item">
|
|
876
|
+
<input type="checkbox" id="comp-async">
|
|
877
|
+
Device Flow / CIBA
|
|
878
|
+
</label>
|
|
879
|
+
<label class="checkbox-item">
|
|
880
|
+
<input type="checkbox" id="comp-vc">
|
|
881
|
+
Verifiable Credentials
|
|
882
|
+
</label>
|
|
883
|
+
</div>
|
|
884
|
+
<hr style="margin: 1.5rem 0; border: none; border-top: 1px solid var(--border);">
|
|
885
|
+
</div>
|
|
886
|
+
|
|
887
|
+
<!-- 2. Environment Name -->
|
|
796
888
|
<div class="form-group">
|
|
797
889
|
<label for="env">Environment Name <span style="color: var(--error);">*</span></label>
|
|
798
|
-
<input type="text" id="env" placeholder="e.g., prod, staging, dev
|
|
890
|
+
<input type="text" id="env" placeholder="e.g., prod, staging, dev" required>
|
|
799
891
|
<small style="color: var(--text-muted)">Lowercase letters, numbers, and hyphens only</small>
|
|
800
892
|
</div>
|
|
801
893
|
|
|
802
|
-
<!--
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
<
|
|
806
|
-
<small style="color: var(--text-muted)">Identifier for your tenant (lowercase, no spaces)</small>
|
|
807
|
-
</div>
|
|
894
|
+
<!-- 3. Domain Configuration -->
|
|
895
|
+
<!-- 3.1 API / Issuer Domain -->
|
|
896
|
+
<div class="domain-section">
|
|
897
|
+
<h4>🌐 API / Issuer Domain</h4>
|
|
808
898
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
899
|
+
<div class="form-group" style="margin-bottom: 0.75rem;">
|
|
900
|
+
<label for="base-domain">Base Domain <span style="color: var(--error);">*</span></label>
|
|
901
|
+
<input type="text" id="base-domain" placeholder="authrim.com">
|
|
902
|
+
<small style="color: var(--text-muted)">Your domain for Authrim (e.g., authrim.com)</small>
|
|
903
|
+
</div>
|
|
814
904
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
905
|
+
<!-- Naked Domain Option -->
|
|
906
|
+
<div class="form-group" style="margin-bottom: 0.75rem;">
|
|
907
|
+
<label class="checkbox-item" style="display: flex; align-items: center; gap: 0.5rem;">
|
|
908
|
+
<input type="checkbox" id="naked-domain">
|
|
909
|
+
<span>Start with naked domain</span>
|
|
910
|
+
</label>
|
|
911
|
+
<small style="color: var(--text-muted); margin-left: 1.5rem;">
|
|
912
|
+
Use https://example.com as initial Issuer. You can add subdomain tenants (tenant1.example.com) later.
|
|
913
|
+
</small>
|
|
914
|
+
</div>
|
|
818
915
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
<div class="
|
|
822
|
-
<
|
|
823
|
-
<
|
|
916
|
+
<!-- Default Tenant (hidden when naked domain is checked) -->
|
|
917
|
+
<div id="tenant-fields">
|
|
918
|
+
<div class="form-group" style="margin-bottom: 0.5rem;">
|
|
919
|
+
<label for="tenant-name">Default Tenant Name</label>
|
|
920
|
+
<input type="text" id="tenant-name" placeholder="default" value="default">
|
|
921
|
+
<small style="color: var(--text-muted)">First tenant identifier (lowercase, no spaces)</small>
|
|
824
922
|
</div>
|
|
825
923
|
</div>
|
|
826
924
|
|
|
925
|
+
<div class="form-group" style="margin-bottom: 0;">
|
|
926
|
+
<label for="tenant-display">Organization Name</label>
|
|
927
|
+
<input type="text" id="tenant-display" placeholder="My Organization" value="Default Tenant">
|
|
928
|
+
<small style="color: var(--text-muted)">Display name shown to users</small>
|
|
929
|
+
</div>
|
|
930
|
+
</div>
|
|
931
|
+
|
|
932
|
+
<!-- 3.2 UI Domains -->
|
|
933
|
+
<div class="domain-section">
|
|
934
|
+
<h4>🖥️ UI Domains (Optional)</h4>
|
|
935
|
+
<div class="section-hint">
|
|
936
|
+
Custom domains for Login/Admin UIs. Each can be set independently.
|
|
937
|
+
Leave empty to use Cloudflare Pages default.
|
|
938
|
+
</div>
|
|
939
|
+
|
|
827
940
|
<div class="domain-row">
|
|
828
941
|
<span class="domain-label">Login UI</span>
|
|
829
942
|
<div class="domain-input-wrapper">
|
|
@@ -840,45 +953,29 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
840
953
|
</div>
|
|
841
954
|
</div>
|
|
842
955
|
|
|
843
|
-
<
|
|
844
|
-
💡
|
|
845
|
-
</
|
|
956
|
+
<div class="section-hint" style="margin-top: 0.75rem; background: #fef3c7;">
|
|
957
|
+
💡 CORS: Cross-origin requests from Login/Admin UI to API are automatically allowed.
|
|
958
|
+
</div>
|
|
846
959
|
</div>
|
|
847
960
|
|
|
848
|
-
<!--
|
|
849
|
-
<div
|
|
850
|
-
<
|
|
851
|
-
<div class="
|
|
852
|
-
<
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
<
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
<
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
<
|
|
865
|
-
|
|
866
|
-
SAML IdP
|
|
867
|
-
</label>
|
|
868
|
-
<label class="checkbox-item">
|
|
869
|
-
<input type="checkbox" id="comp-async">
|
|
870
|
-
Device Flow / CIBA
|
|
871
|
-
</label>
|
|
872
|
-
<label class="checkbox-item">
|
|
873
|
-
<input type="checkbox" id="comp-vc">
|
|
874
|
-
Verifiable Credentials
|
|
875
|
-
</label>
|
|
876
|
-
<div class="checkbox-item" style="color: var(--text-muted); font-style: italic;">
|
|
877
|
-
✓ External IdP (Social Login) - included
|
|
878
|
-
</div>
|
|
879
|
-
<div class="checkbox-item" style="color: var(--text-muted); font-style: italic;">
|
|
880
|
-
✓ ReBAC Policy Engine - included
|
|
881
|
-
</div>
|
|
961
|
+
<!-- 4. Preview Section (at the bottom) -->
|
|
962
|
+
<div class="infra-section" id="config-preview">
|
|
963
|
+
<h4>📋 Configuration Preview</h4>
|
|
964
|
+
<div class="infra-item">
|
|
965
|
+
<span class="infra-label">Workers:</span>
|
|
966
|
+
<span class="infra-value" id="preview-workers">{env}-ar-router, {env}-ar-auth, ...</span>
|
|
967
|
+
</div>
|
|
968
|
+
<div class="infra-item">
|
|
969
|
+
<span class="infra-label">Issuer URL:</span>
|
|
970
|
+
<span class="infra-value" id="preview-issuer">https://{tenant}.{base-domain}</span>
|
|
971
|
+
</div>
|
|
972
|
+
<div class="infra-item">
|
|
973
|
+
<span class="infra-label">Login UI:</span>
|
|
974
|
+
<span class="infra-value" id="preview-login">{env}-ar-ui.pages.dev</span>
|
|
975
|
+
</div>
|
|
976
|
+
<div class="infra-item">
|
|
977
|
+
<span class="infra-label">Admin UI:</span>
|
|
978
|
+
<span class="infra-value" id="preview-admin">{env}-ar-ui.pages.dev/admin</span>
|
|
882
979
|
</div>
|
|
883
980
|
</div>
|
|
884
981
|
|
|
@@ -1456,12 +1553,70 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1456
1553
|
});
|
|
1457
1554
|
|
|
1458
1555
|
// Configuration handlers
|
|
1459
|
-
// Update
|
|
1460
|
-
|
|
1461
|
-
const env =
|
|
1462
|
-
document.getElementById('
|
|
1556
|
+
// Update preview section when any input changes
|
|
1557
|
+
function updatePreview() {
|
|
1558
|
+
const env = document.getElementById('env').value.trim().toLowerCase().replace(/[^a-z0-9-]/g, '') || '{env}';
|
|
1559
|
+
const baseDomain = document.getElementById('base-domain').value.trim();
|
|
1560
|
+
const nakedDomain = document.getElementById('naked-domain').checked;
|
|
1561
|
+
const tenantName = document.getElementById('tenant-name').value.trim() || 'default';
|
|
1562
|
+
const loginDomain = document.getElementById('login-domain').value.trim();
|
|
1563
|
+
const adminDomain = document.getElementById('admin-domain').value.trim();
|
|
1564
|
+
|
|
1565
|
+
// Workers
|
|
1566
|
+
document.getElementById('preview-workers').textContent = env + '-ar-router, ' + env + '-ar-auth, ...';
|
|
1567
|
+
|
|
1568
|
+
// Issuer URL
|
|
1569
|
+
if (baseDomain) {
|
|
1570
|
+
if (nakedDomain) {
|
|
1571
|
+
// Naked domain: https://example.com
|
|
1572
|
+
document.getElementById('preview-issuer').textContent = 'https://' + baseDomain;
|
|
1573
|
+
} else {
|
|
1574
|
+
// Subdomain: https://tenant.example.com
|
|
1575
|
+
document.getElementById('preview-issuer').textContent = 'https://' + tenantName + '.' + baseDomain;
|
|
1576
|
+
}
|
|
1577
|
+
} else {
|
|
1578
|
+
if (nakedDomain) {
|
|
1579
|
+
document.getElementById('preview-issuer').textContent = 'https://{base-domain}';
|
|
1580
|
+
} else {
|
|
1581
|
+
document.getElementById('preview-issuer').textContent = 'https://{tenant}.{base-domain}';
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// Login UI
|
|
1586
|
+
if (loginDomain) {
|
|
1587
|
+
document.getElementById('preview-login').textContent = 'https://' + loginDomain;
|
|
1588
|
+
} else {
|
|
1589
|
+
document.getElementById('preview-login').textContent = 'https://' + env + '-ar-ui.pages.dev';
|
|
1590
|
+
}
|
|
1463
1591
|
document.getElementById('login-default').textContent = env + '-ar-ui.pages.dev';
|
|
1592
|
+
|
|
1593
|
+
// Admin UI
|
|
1594
|
+
if (adminDomain) {
|
|
1595
|
+
document.getElementById('preview-admin').textContent = 'https://' + adminDomain;
|
|
1596
|
+
} else {
|
|
1597
|
+
document.getElementById('preview-admin').textContent = 'https://' + env + '-ar-ui.pages.dev/admin';
|
|
1598
|
+
}
|
|
1464
1599
|
document.getElementById('admin-default').textContent = env + '-ar-ui.pages.dev/admin';
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// Attach event listeners to all inputs
|
|
1603
|
+
['env', 'base-domain', 'naked-domain', 'tenant-name', 'login-domain', 'admin-domain'].forEach(id => {
|
|
1604
|
+
const el = document.getElementById(id);
|
|
1605
|
+
if (el) {
|
|
1606
|
+
el.addEventListener('input', updatePreview);
|
|
1607
|
+
el.addEventListener('change', updatePreview);
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1611
|
+
// Naked domain toggle - show/hide tenant name field
|
|
1612
|
+
document.getElementById('naked-domain').addEventListener('change', (e) => {
|
|
1613
|
+
const tenantFields = document.getElementById('tenant-fields');
|
|
1614
|
+
if (e.target.checked) {
|
|
1615
|
+
tenantFields.style.display = 'none';
|
|
1616
|
+
} else {
|
|
1617
|
+
tenantFields.style.display = 'block';
|
|
1618
|
+
}
|
|
1619
|
+
updatePreview();
|
|
1465
1620
|
});
|
|
1466
1621
|
|
|
1467
1622
|
document.getElementById('btn-back-mode').addEventListener('click', () => {
|
|
@@ -1500,20 +1655,33 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1500
1655
|
configureBtn.disabled = false;
|
|
1501
1656
|
}
|
|
1502
1657
|
|
|
1503
|
-
const
|
|
1504
|
-
const
|
|
1505
|
-
const adminDomain = document.getElementById('admin-domain').value.trim();
|
|
1658
|
+
const baseDomain = document.getElementById('base-domain').value.trim();
|
|
1659
|
+
const nakedDomain = document.getElementById('naked-domain').checked;
|
|
1506
1660
|
const tenantName = document.getElementById('tenant-name').value.trim() || 'default';
|
|
1507
1661
|
const tenantDisplayName = document.getElementById('tenant-display').value.trim() || 'Default Tenant';
|
|
1662
|
+
const loginDomain = document.getElementById('login-domain').value.trim();
|
|
1663
|
+
const adminDomain = document.getElementById('admin-domain').value.trim();
|
|
1664
|
+
|
|
1665
|
+
// Validate base domain is required
|
|
1666
|
+
if (!baseDomain) {
|
|
1667
|
+
alert('Base domain is required');
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
// API domain = base domain (wildcard subdomain routing)
|
|
1672
|
+
const apiDomain = baseDomain;
|
|
1508
1673
|
|
|
1509
1674
|
config = {
|
|
1510
1675
|
env,
|
|
1511
|
-
apiDomain
|
|
1676
|
+
apiDomain,
|
|
1512
1677
|
loginUiDomain: loginDomain || null,
|
|
1513
1678
|
adminUiDomain: adminDomain || null,
|
|
1514
1679
|
tenant: {
|
|
1515
|
-
name: tenantName,
|
|
1680
|
+
name: nakedDomain ? null : tenantName, // null for naked domain
|
|
1516
1681
|
displayName: tenantDisplayName,
|
|
1682
|
+
multiTenant: true, // Always multi-tenant capable
|
|
1683
|
+
baseDomain: baseDomain,
|
|
1684
|
+
nakedDomain: nakedDomain,
|
|
1517
1685
|
},
|
|
1518
1686
|
components: {
|
|
1519
1687
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA2sCoB,SAAS;0BACZ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA2sChC,CAAC;AACT,CAAC"}
|