@authrim/setup 0.1.26 → 0.1.28
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 +1 -1
- package/dist/index.js +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +108 -49
- package/dist/web/ui.js.map +1 -1
- package/package.json +1 -1
|
@@ -24,7 +24,7 @@ function printBanner() {
|
|
|
24
24
|
console.log('');
|
|
25
25
|
console.log(chalk.blue('╔═══════════════════════════════════════════════════════════╗'));
|
|
26
26
|
console.log(chalk.blue('║') +
|
|
27
|
-
chalk.bold.white(' 🔐 Authrim Setup v0.1.
|
|
27
|
+
chalk.bold.white(' 🔐 Authrim Setup v0.1.28 ') +
|
|
28
28
|
chalk.blue('║'));
|
|
29
29
|
console.log(chalk.blue('║') +
|
|
30
30
|
chalk.gray(' OIDC Provider on Cloudflare Workers ') +
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ const program = new Command();
|
|
|
17
17
|
program
|
|
18
18
|
.name('authrim-setup')
|
|
19
19
|
.description('CLI tool for setting up Authrim OIDC Provider on Cloudflare Workers')
|
|
20
|
-
.version('0.1.
|
|
20
|
+
.version('0.1.28');
|
|
21
21
|
program
|
|
22
22
|
.command('init', { isDefault: true })
|
|
23
23
|
.description('Initialize a new Authrim setup')
|
package/dist/web/ui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wBAAgB,eAAe,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wBAAgB,eAAe,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAw9EnF"}
|
package/dist/web/ui.js
CHANGED
|
@@ -855,32 +855,54 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
855
855
|
<!-- 1. Components (shown in custom mode) -->
|
|
856
856
|
<div id="advanced-options" class="hidden">
|
|
857
857
|
<h3 style="margin: 0 0 1rem; font-size: 1rem;">📦 Components</h3>
|
|
858
|
-
|
|
859
|
-
|
|
858
|
+
|
|
859
|
+
<!-- API Component (required) -->
|
|
860
|
+
<div class="component-card" style="background: #f8fafc; border: 1px solid var(--border); border-radius: 8px; padding: 1rem; margin-bottom: 0.75rem;">
|
|
861
|
+
<label class="checkbox-item" style="font-weight: 600; margin-bottom: 0.25rem;">
|
|
860
862
|
<input type="checkbox" id="comp-api" checked disabled>
|
|
861
|
-
API (required)
|
|
863
|
+
🔐 API (required)
|
|
862
864
|
</label>
|
|
863
|
-
<
|
|
865
|
+
<p style="margin: 0.25rem 0 0.5rem 1.5rem; font-size: 0.85rem; color: var(--text-muted);">
|
|
866
|
+
OIDC Provider endpoints: authorize, token, userinfo, discovery, management APIs.
|
|
867
|
+
</p>
|
|
868
|
+
<div style="margin-left: 1.5rem; display: flex; flex-wrap: wrap; gap: 0.75rem;">
|
|
869
|
+
<label class="checkbox-item" style="font-size: 0.9rem;">
|
|
870
|
+
<input type="checkbox" id="comp-saml">
|
|
871
|
+
SAML IdP
|
|
872
|
+
</label>
|
|
873
|
+
<label class="checkbox-item" style="font-size: 0.9rem;">
|
|
874
|
+
<input type="checkbox" id="comp-async">
|
|
875
|
+
Device Flow / CIBA
|
|
876
|
+
</label>
|
|
877
|
+
<label class="checkbox-item" style="font-size: 0.9rem;">
|
|
878
|
+
<input type="checkbox" id="comp-vc">
|
|
879
|
+
Verifiable Credentials
|
|
880
|
+
</label>
|
|
881
|
+
</div>
|
|
882
|
+
</div>
|
|
883
|
+
|
|
884
|
+
<!-- Login UI Component -->
|
|
885
|
+
<div class="component-card" style="background: #f8fafc; border: 1px solid var(--border); border-radius: 8px; padding: 1rem; margin-bottom: 0.75rem;">
|
|
886
|
+
<label class="checkbox-item" style="font-weight: 600; margin-bottom: 0.25rem;">
|
|
864
887
|
<input type="checkbox" id="comp-login-ui" checked>
|
|
865
|
-
Login UI
|
|
888
|
+
🖥️ Login UI
|
|
866
889
|
</label>
|
|
867
|
-
<
|
|
890
|
+
<p style="margin: 0.25rem 0 0 1.5rem; font-size: 0.85rem; color: var(--text-muted);">
|
|
891
|
+
User-facing login, registration, consent, and account management pages.
|
|
892
|
+
</p>
|
|
893
|
+
</div>
|
|
894
|
+
|
|
895
|
+
<!-- Admin UI Component -->
|
|
896
|
+
<div class="component-card" style="background: #f8fafc; border: 1px solid var(--border); border-radius: 8px; padding: 1rem; margin-bottom: 0.75rem;">
|
|
897
|
+
<label class="checkbox-item" style="font-weight: 600; margin-bottom: 0.25rem;">
|
|
868
898
|
<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
|
|
899
|
+
⚙️ Admin UI
|
|
882
900
|
</label>
|
|
901
|
+
<p style="margin: 0.25rem 0 0 1.5rem; font-size: 0.85rem; color: var(--text-muted);">
|
|
902
|
+
Admin dashboard for managing tenants, clients, users, and system settings.
|
|
903
|
+
</p>
|
|
883
904
|
</div>
|
|
905
|
+
|
|
884
906
|
<hr style="margin: 1.5rem 0; border: none; border-top: 1px solid var(--border);">
|
|
885
907
|
</div>
|
|
886
908
|
|
|
@@ -897,9 +919,9 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
897
919
|
<h4>🌐 API / Issuer Domain</h4>
|
|
898
920
|
|
|
899
921
|
<div class="form-group" style="margin-bottom: 0.75rem;">
|
|
900
|
-
<label for="base-domain">Base Domain
|
|
922
|
+
<label for="base-domain">Base Domain</label>
|
|
901
923
|
<input type="text" id="base-domain" placeholder="authrim.com">
|
|
902
|
-
<small style="color: var(--text-muted)">
|
|
924
|
+
<small style="color: var(--text-muted)">Custom domain for Authrim. Leave empty to use workers.dev</small>
|
|
903
925
|
</div>
|
|
904
926
|
|
|
905
927
|
<!-- Naked Domain Option -->
|
|
@@ -930,14 +952,14 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
930
952
|
</div>
|
|
931
953
|
|
|
932
954
|
<!-- 3.2 UI Domains -->
|
|
933
|
-
<div class="domain-section">
|
|
955
|
+
<div class="domain-section" id="ui-domains-section">
|
|
934
956
|
<h4>🖥️ UI Domains (Optional)</h4>
|
|
935
957
|
<div class="section-hint">
|
|
936
958
|
Custom domains for Login/Admin UIs. Each can be set independently.
|
|
937
959
|
Leave empty to use Cloudflare Pages default.
|
|
938
960
|
</div>
|
|
939
961
|
|
|
940
|
-
<div class="domain-row">
|
|
962
|
+
<div class="domain-row" id="login-domain-row">
|
|
941
963
|
<span class="domain-label">Login UI</span>
|
|
942
964
|
<div class="domain-input-wrapper">
|
|
943
965
|
<input type="text" id="login-domain" placeholder="login.example.com">
|
|
@@ -945,7 +967,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
945
967
|
</div>
|
|
946
968
|
</div>
|
|
947
969
|
|
|
948
|
-
<div class="domain-row">
|
|
970
|
+
<div class="domain-row" id="admin-domain-row">
|
|
949
971
|
<span class="domain-label">Admin UI</span>
|
|
950
972
|
<div class="domain-input-wrapper">
|
|
951
973
|
<input type="text" id="admin-domain" placeholder="admin.example.com">
|
|
@@ -1473,6 +1495,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1473
1495
|
document.getElementById('advanced-options').classList.add('hidden');
|
|
1474
1496
|
setStep(2);
|
|
1475
1497
|
showSection('config');
|
|
1498
|
+
updatePreview();
|
|
1476
1499
|
});
|
|
1477
1500
|
|
|
1478
1501
|
document.getElementById('mode-custom').addEventListener('click', () => {
|
|
@@ -1482,6 +1505,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1482
1505
|
document.getElementById('advanced-options').classList.remove('hidden');
|
|
1483
1506
|
setStep(2);
|
|
1484
1507
|
showSection('config');
|
|
1508
|
+
updatePreview();
|
|
1485
1509
|
});
|
|
1486
1510
|
|
|
1487
1511
|
document.getElementById('btn-back-top').addEventListener('click', () => {
|
|
@@ -1575,32 +1599,43 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1575
1599
|
document.getElementById('preview-issuer').textContent = 'https://' + tenantName + '.' + baseDomain;
|
|
1576
1600
|
}
|
|
1577
1601
|
} else {
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
} else {
|
|
1581
|
-
document.getElementById('preview-issuer').textContent = 'https://{tenant}.{base-domain}';
|
|
1582
|
-
}
|
|
1602
|
+
// No custom domain - use workers.dev
|
|
1603
|
+
document.getElementById('preview-issuer').textContent = 'https://' + env + '-ar-router.workers.dev';
|
|
1583
1604
|
}
|
|
1584
1605
|
|
|
1585
|
-
// Login UI
|
|
1586
|
-
|
|
1587
|
-
|
|
1606
|
+
// Login UI - check if component is enabled (in custom mode)
|
|
1607
|
+
const loginUiEnabled = document.getElementById('comp-login-ui').checked;
|
|
1608
|
+
const previewLogin = document.getElementById('preview-login');
|
|
1609
|
+
if (!loginUiEnabled) {
|
|
1610
|
+
previewLogin.textContent = '(Not deployed)';
|
|
1611
|
+
previewLogin.style.color = 'var(--text-muted)';
|
|
1612
|
+
} else if (loginDomain) {
|
|
1613
|
+
previewLogin.textContent = 'https://' + loginDomain;
|
|
1614
|
+
previewLogin.style.color = '';
|
|
1588
1615
|
} else {
|
|
1589
|
-
|
|
1616
|
+
previewLogin.textContent = 'https://' + env + '-ar-ui.pages.dev';
|
|
1617
|
+
previewLogin.style.color = '';
|
|
1590
1618
|
}
|
|
1591
1619
|
document.getElementById('login-default').textContent = env + '-ar-ui.pages.dev';
|
|
1592
1620
|
|
|
1593
|
-
// Admin UI
|
|
1594
|
-
|
|
1595
|
-
|
|
1621
|
+
// Admin UI - check if component is enabled (in custom mode)
|
|
1622
|
+
const adminUiEnabled = document.getElementById('comp-admin-ui').checked;
|
|
1623
|
+
const previewAdmin = document.getElementById('preview-admin');
|
|
1624
|
+
if (!adminUiEnabled) {
|
|
1625
|
+
previewAdmin.textContent = '(Not deployed)';
|
|
1626
|
+
previewAdmin.style.color = 'var(--text-muted)';
|
|
1627
|
+
} else if (adminDomain) {
|
|
1628
|
+
previewAdmin.textContent = 'https://' + adminDomain;
|
|
1629
|
+
previewAdmin.style.color = '';
|
|
1596
1630
|
} else {
|
|
1597
|
-
|
|
1631
|
+
previewAdmin.textContent = 'https://' + env + '-ar-ui.pages.dev/admin';
|
|
1632
|
+
previewAdmin.style.color = '';
|
|
1598
1633
|
}
|
|
1599
1634
|
document.getElementById('admin-default').textContent = env + '-ar-ui.pages.dev/admin';
|
|
1600
1635
|
}
|
|
1601
1636
|
|
|
1602
1637
|
// Attach event listeners to all inputs
|
|
1603
|
-
['env', 'base-domain', 'naked-domain', 'tenant-name', 'login-domain', 'admin-domain'].forEach(id => {
|
|
1638
|
+
['env', 'base-domain', 'naked-domain', 'tenant-name', 'login-domain', 'admin-domain', 'comp-login-ui', 'comp-admin-ui'].forEach(id => {
|
|
1604
1639
|
const el = document.getElementById(id);
|
|
1605
1640
|
if (el) {
|
|
1606
1641
|
el.addEventListener('input', updatePreview);
|
|
@@ -1619,6 +1654,36 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1619
1654
|
updatePreview();
|
|
1620
1655
|
});
|
|
1621
1656
|
|
|
1657
|
+
// Login UI component toggle - grey out domain settings when unchecked
|
|
1658
|
+
document.getElementById('comp-login-ui').addEventListener('change', (e) => {
|
|
1659
|
+
const loginDomainRow = document.getElementById('login-domain-row');
|
|
1660
|
+
const loginDomainInput = document.getElementById('login-domain');
|
|
1661
|
+
if (e.target.checked) {
|
|
1662
|
+
loginDomainRow.style.opacity = '1';
|
|
1663
|
+
loginDomainInput.disabled = false;
|
|
1664
|
+
} else {
|
|
1665
|
+
loginDomainRow.style.opacity = '0.4';
|
|
1666
|
+
loginDomainInput.disabled = true;
|
|
1667
|
+
loginDomainInput.value = '';
|
|
1668
|
+
}
|
|
1669
|
+
updatePreview();
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1672
|
+
// Admin UI component toggle - grey out domain settings when unchecked
|
|
1673
|
+
document.getElementById('comp-admin-ui').addEventListener('change', (e) => {
|
|
1674
|
+
const adminDomainRow = document.getElementById('admin-domain-row');
|
|
1675
|
+
const adminDomainInput = document.getElementById('admin-domain');
|
|
1676
|
+
if (e.target.checked) {
|
|
1677
|
+
adminDomainRow.style.opacity = '1';
|
|
1678
|
+
adminDomainInput.disabled = false;
|
|
1679
|
+
} else {
|
|
1680
|
+
adminDomainRow.style.opacity = '0.4';
|
|
1681
|
+
adminDomainInput.disabled = true;
|
|
1682
|
+
adminDomainInput.value = '';
|
|
1683
|
+
}
|
|
1684
|
+
updatePreview();
|
|
1685
|
+
});
|
|
1686
|
+
|
|
1622
1687
|
document.getElementById('btn-back-mode').addEventListener('click', () => {
|
|
1623
1688
|
setStep(1);
|
|
1624
1689
|
showSection('mode');
|
|
@@ -1662,14 +1727,8 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1662
1727
|
const loginDomain = document.getElementById('login-domain').value.trim();
|
|
1663
1728
|
const adminDomain = document.getElementById('admin-domain').value.trim();
|
|
1664
1729
|
|
|
1665
|
-
//
|
|
1666
|
-
|
|
1667
|
-
alert('Base domain is required');
|
|
1668
|
-
return;
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
|
-
// API domain = base domain (wildcard subdomain routing)
|
|
1672
|
-
const apiDomain = baseDomain;
|
|
1730
|
+
// API domain = base domain or null (workers.dev fallback)
|
|
1731
|
+
const apiDomain = baseDomain || null;
|
|
1673
1732
|
|
|
1674
1733
|
config = {
|
|
1675
1734
|
env,
|
|
@@ -1679,9 +1738,9 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1679
1738
|
tenant: {
|
|
1680
1739
|
name: nakedDomain ? null : tenantName, // null for naked domain
|
|
1681
1740
|
displayName: tenantDisplayName,
|
|
1682
|
-
multiTenant: true, //
|
|
1683
|
-
baseDomain: baseDomain,
|
|
1684
|
-
nakedDomain: nakedDomain,
|
|
1741
|
+
multiTenant: baseDomain ? true : false, // Only multi-tenant with custom domain
|
|
1742
|
+
baseDomain: baseDomain || undefined,
|
|
1743
|
+
nakedDomain: baseDomain ? nakedDomain : false,
|
|
1685
1744
|
},
|
|
1686
1745
|
components: {
|
|
1687
1746
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAiuCoB,SAAS;0BACZ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAgvChC,CAAC;AACT,CAAC"}
|