@bongos/core 1.21.51 → 1.21.53
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/.bongos-core.json +88 -73
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +7 -0
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/api/openapi.json +135 -3
- package/docs/api-reference.md +4 -2
- package/docs/copy-inventory.md +195 -150
- package/docs/copy-registry.json +740 -281
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +8 -4
- package/docs/page-readings.json +336 -333
- package/modules/hall-ui/public/atlas.html +3 -3
- package/modules/hall-ui/public/blockers.html +3 -3
- package/modules/hall-ui/public/board-room.html +3 -3
- package/modules/hall-ui/public/city.html +3 -3
- package/modules/hall-ui/public/collab.html +3 -3
- package/modules/hall-ui/public/commands.html +3 -3
- package/modules/hall-ui/public/copy-desk.html +3 -3
- package/modules/hall-ui/public/craft-mode.js +7 -1
- package/modules/hall-ui/public/deploy.html +3 -3
- package/modules/hall-ui/public/diagrams.html +3 -3
- package/modules/hall-ui/public/drachmae.html +3 -3
- package/modules/hall-ui/public/fleet.html +3 -3
- package/modules/hall-ui/public/gate.html +3 -3
- package/modules/hall-ui/public/goal-map.html +3 -3
- package/modules/hall-ui/public/goals.html +3 -3
- package/modules/hall-ui/public/government.html +3 -3
- package/modules/hall-ui/public/idea.html +3 -3
- package/modules/hall-ui/public/ideas.html +3 -3
- package/modules/hall-ui/public/index.html +3 -3
- package/modules/hall-ui/public/lightbulb.js +213 -0
- package/modules/hall-ui/public/modules.html +3 -3
- package/modules/hall-ui/public/primer.html +3 -3
- package/modules/hall-ui/public/profile.html +3 -3
- package/modules/hall-ui/public/project-settings.html +3 -3
- package/modules/hall-ui/public/ranks.html +3 -3
- package/modules/hall-ui/public/roadmap.html +3 -3
- package/modules/hall-ui/public/roster.html +3 -3
- package/modules/hall-ui/public/sessions.html +3 -3
- package/modules/hall-ui/public/settings.html +3 -3
- package/modules/hall-ui/public/street.html +3 -3
- package/modules/hall-ui/public/studio.html +3 -3
- package/modules/hall-ui/public/style.css +1 -0
- package/modules/hall-ui/public/task.html +3 -3
- package/modules/hall-ui/public/thinking.html +3 -3
- package/modules/hall-ui/public/tweak-editor.html +3 -3
- package/modules/hall-ui/public/watch.html +3 -3
- package/modules/hall-ui/public/work.html +3 -3
- package/modules/hall-ui/records/craft-mode.md +8 -0
- package/modules/page-notes/db.js +34 -0
- package/modules/page-notes/notes.js +54 -8
- package/modules/page-notes/routes/page-notes.js +49 -1
- package/modules/page-notes/split.js +39 -8
- package/modules/public-landing/public/account.html +12 -101
- package/modules/public-landing/public/assets/hub-bar.js +127 -0
- package/modules/public-landing/public/contact.html +60 -9
- package/modules/public-landing/public/privacy.html +60 -9
- package/modules/public-landing/public/terms.html +60 -9
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/src/module-api.js +1 -1
- package/tests/hall_tweak_editor.mjs +1 -1
- package/tests/hub_account_page.mjs +22 -5
- package/tests/landing_legal_pages.mjs +275 -0
- package/tests/lightbulb.mjs +385 -0
- package/tests/page_notes_panel.mjs +3 -1
- package/tests/paintbrush.mjs +2 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* assets/hub-bar.js — THE HUB'S BAR on the pages that have no views of their own
|
|
2
|
+
(task 1004489, after task 1004455).
|
|
3
|
+
|
|
4
|
+
/projects draws the bar and its two bands — the Projects band and the profile
|
|
5
|
+
band behind the identity chip. The apex pages a person reaches from it wear the
|
|
6
|
+
same bar so the chrome does not swap under them: the account page (/account) and
|
|
7
|
+
the three footer doors (/privacy, /terms, /contact). Each page keeps the bar's
|
|
8
|
+
MARKUP (real markup, pinned byte-equal to the hub's); this file is the one copy
|
|
9
|
+
of what makes it work there:
|
|
10
|
+
- the chip, filled from GET /me — signed out there is no chip and no
|
|
11
|
+
Your-projects item, and the sign-in door takes the chip's slot;
|
|
12
|
+
- the bands' disclosure: a trigger toggles its band, opening one closes the
|
|
13
|
+
other, Escape closes and returns focus to the trigger, a click outside or
|
|
14
|
+
Tab out closes, the arrows walk the items, an item activated closes it;
|
|
15
|
+
- sign out: the profile band's plain anchor upgraded to the hub's logout POST.
|
|
16
|
+
|
|
17
|
+
projects.html keeps its own copy (its bands also fold the map search and switch
|
|
18
|
+
views), so a change to the disclosure is made there AND here.
|
|
19
|
+
tests/hub_account_page.mjs and tests/landing_legal_pages.mjs execute this file.
|
|
20
|
+
|
|
21
|
+
A page that knows more about who is signed in than GET /me says paints the chip
|
|
22
|
+
itself through window.HubBar.paint(me); window.HubBar.ready settles with what
|
|
23
|
+
GET /me answered (null when nobody is signed in or the read failed). */
|
|
24
|
+
(function () {
|
|
25
|
+
'use strict';
|
|
26
|
+
var API = '/api/bongos';
|
|
27
|
+
function $(id) { return document.getElementById(id); }
|
|
28
|
+
|
|
29
|
+
// ---- the chip: who is signed in (null = nobody) ----
|
|
30
|
+
function paint(me) {
|
|
31
|
+
var login = me ? (me.github_login || 'signed in') : '';
|
|
32
|
+
var avatar = me && me.avatar_url;
|
|
33
|
+
$('who').hidden = !me;
|
|
34
|
+
$('signinTop').hidden = !!me;
|
|
35
|
+
$('mineCell').hidden = !me;
|
|
36
|
+
[['whoImg', 'whoLogin'], ['bandWhoImg', 'bandWhoLogin']].forEach(function (ids) {
|
|
37
|
+
$(ids[0]).hidden = !avatar;
|
|
38
|
+
if (avatar) $(ids[0]).src = avatar;
|
|
39
|
+
$(ids[1]).textContent = login;
|
|
40
|
+
});
|
|
41
|
+
if (me) $('who').setAttribute('aria-label', 'Account, ' + login);
|
|
42
|
+
else closeMenus();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---- the bands: the hub's disclosure (projects.html "THE BANDS") without the map search ----
|
|
46
|
+
function openBand() { return document.querySelector('.menuBand:not([hidden])'); }
|
|
47
|
+
function closeMenus() {
|
|
48
|
+
var open = openBand();
|
|
49
|
+
if (!open) return;
|
|
50
|
+
var trig = document.querySelector('.trig[aria-expanded="true"]');
|
|
51
|
+
var inside = open.contains(document.activeElement);
|
|
52
|
+
open.hidden = true;
|
|
53
|
+
if (trig) trig.setAttribute('aria-expanded', 'false');
|
|
54
|
+
if (inside && trig) trig.focus();
|
|
55
|
+
}
|
|
56
|
+
function openMenu(trig, byKeyboard) {
|
|
57
|
+
closeMenus();
|
|
58
|
+
trig.setAttribute('aria-expanded', 'true');
|
|
59
|
+
var band = $(trig.getAttribute('aria-controls'));
|
|
60
|
+
band.hidden = false;
|
|
61
|
+
// Enter/Space on the trigger lands on the first item; a pointer stays put
|
|
62
|
+
if (byKeyboard) { var first = band.querySelector('.bandCell:not([hidden]) a'); if (first) first.focus(); }
|
|
63
|
+
}
|
|
64
|
+
document.querySelectorAll('.trig').forEach(function (t) {
|
|
65
|
+
t.addEventListener('click', function (ev) {
|
|
66
|
+
if (t.getAttribute('aria-expanded') === 'true') closeMenus();
|
|
67
|
+
else openMenu(t, ev.detail === 0);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
document.addEventListener('click', function (ev) {
|
|
71
|
+
if (!openBand()) return;
|
|
72
|
+
if (ev.target.closest && (ev.target.closest('.menuBand') || ev.target.closest('.trig'))) return;
|
|
73
|
+
closeMenus();
|
|
74
|
+
});
|
|
75
|
+
document.addEventListener('keydown', function (ev) {
|
|
76
|
+
var open = openBand();
|
|
77
|
+
if (!open) return;
|
|
78
|
+
if (ev.key === 'Escape') { closeMenus(); return; }
|
|
79
|
+
if (ev.key !== 'ArrowDown' && ev.key !== 'ArrowUp') return;
|
|
80
|
+
var items = Array.prototype.filter.call(open.querySelectorAll('a'), function (a) { return !a.closest('[hidden]'); });
|
|
81
|
+
if (!items.length) return;
|
|
82
|
+
var at = items.indexOf(document.activeElement);
|
|
83
|
+
ev.preventDefault();
|
|
84
|
+
items[(at + (ev.key === 'ArrowDown' ? 1 : items.length - 1) + items.length) % items.length].focus();
|
|
85
|
+
});
|
|
86
|
+
document.querySelectorAll('.menuBand').forEach(function (b) {
|
|
87
|
+
b.addEventListener('click', function (ev) {
|
|
88
|
+
if (ev.target.closest && ev.target.closest('a')) closeMenus();
|
|
89
|
+
});
|
|
90
|
+
// Tab out of the band closes it; a null relatedTarget is the window losing
|
|
91
|
+
// focus or a click on nothing, which the document click covers
|
|
92
|
+
b.addEventListener('focusout', function (ev) {
|
|
93
|
+
var to = ev.relatedTarget;
|
|
94
|
+
if (!to || b.contains(to) || (to.closest && to.closest('.trig'))) return;
|
|
95
|
+
closeMenus();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// ---- the profile band: the current place, and sign out (projects.html doLogout) ----
|
|
100
|
+
$('menuProfile').addEventListener('click', function (ev) {
|
|
101
|
+
if (!ev.target.closest) return;
|
|
102
|
+
// an item marked as this page: a reload would only throw away what was typed
|
|
103
|
+
if (ev.target.closest('[aria-current="page"]')) { ev.preventDefault(); return; }
|
|
104
|
+
if (!ev.target.closest('a.signout')) return;
|
|
105
|
+
ev.preventDefault();
|
|
106
|
+
var finish = function () {
|
|
107
|
+
var apex = location.hostname.split('.').slice(-2).join('.');
|
|
108
|
+
['bongos_session', 'gds_session', 'pms_session'].forEach(function (name) {
|
|
109
|
+
document.cookie = name + '=; Max-Age=0; path=/';
|
|
110
|
+
document.cookie = name + '=; Max-Age=0; path=/; Domain=.' + apex;
|
|
111
|
+
});
|
|
112
|
+
window.location.assign(location.pathname);
|
|
113
|
+
};
|
|
114
|
+
fetch(API + '/auth/logout', { method: 'POST', credentials: 'same-origin' }).then(finish, finish);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ---- the identity read: the hub's own GET /me ----
|
|
118
|
+
var ready = fetch(API + '/me', { method: 'GET', credentials: 'same-origin', headers: { Accept: 'application/json' } })
|
|
119
|
+
.then(function (res) { return res.ok ? res.json().catch(function () { return null; }) : null; })
|
|
120
|
+
.then(function (d) {
|
|
121
|
+
var me = d ? (d.builder || d.me || d) : null;
|
|
122
|
+
paint(me);
|
|
123
|
+
return me;
|
|
124
|
+
}, function () { paint(null); return null; });
|
|
125
|
+
|
|
126
|
+
window.HubBar = { paint: paint, ready: ready };
|
|
127
|
+
})();
|
|
@@ -20,9 +20,14 @@
|
|
|
20
20
|
<!-- Dark only, like every apex page (task 1003336, ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
21
21
|
<style>
|
|
22
22
|
/* The reading column only. No :root block — the pack's injected theme owns the
|
|
23
|
-
tokens (landing direction C1); everything below reads them.
|
|
23
|
+
tokens (landing direction C1); everything below reads them. The bar and the
|
|
24
|
+
bands are cosmos.css's own rules, not restyled here. */
|
|
25
|
+
/* the skip link un-hides itself on focus, reversing everything .vh sets (the hub's rule) */
|
|
26
|
+
.skip:focus{position:fixed;top:8px;left:8px;z-index:100;width:auto;height:auto;margin:0;overflow:visible;clip-path:none;white-space:normal;
|
|
27
|
+
font-size:14px;font-weight:800;text-decoration:none;color:var(--ink);background:var(--bg-card);
|
|
28
|
+
border:2px solid var(--ctl-line);border-radius:12px;padding:10px 16px;}
|
|
24
29
|
.legal{position:relative;z-index:1;max-width:720px;margin:0 auto;padding:clamp(6px,1.5vw,18px) clamp(16px,4vw,40px) 48px;}
|
|
25
|
-
.legal h1{font-family:var(--font-display);font-weight:700;font-size:clamp(26px,3.8vw,36px);letter-spacing:-0.5px;color:var(--chrome-ink);text-shadow:var(--chrome-glow);margin
|
|
30
|
+
.legal h1{font-family:var(--font-display);font-weight:700;font-size:clamp(26px,3.8vw,36px);letter-spacing:-0.5px;color:var(--chrome-ink);text-shadow:var(--chrome-glow);margin:18px 0 6px;}
|
|
26
31
|
.legal .lede{color:var(--hero-lede);text-shadow:var(--chrome-glow);margin-bottom:18px;}
|
|
27
32
|
.legal h2{font-family:var(--font-display);font-weight:700;font-size:var(--fs-h3);letter-spacing:-0.3px;line-height:1.25;margin:22px 0 8px;}
|
|
28
33
|
.legal .card h2:first-child{margin-top:0;}
|
|
@@ -34,15 +39,60 @@
|
|
|
34
39
|
</style>
|
|
35
40
|
</head>
|
|
36
41
|
<body>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
<!-- THE BAR and its two bands are the hub's own (projects.html; task 1004489,
|
|
43
|
+
after the account page's task 1004455): same markup, same cosmos.css rules,
|
|
44
|
+
so following a footer link from /projects or /account keeps the chrome. No
|
|
45
|
+
map search (it only filters the map), the band links name /projects in full
|
|
46
|
+
because this page has no views of its own, and no item is the current place.
|
|
47
|
+
assets/hub-bar.js fills the chip from GET /me and runs the bands. -->
|
|
48
|
+
<a class="skip vh" href="#legal">Skip to content</a>
|
|
49
|
+
<header class="shell bar">
|
|
50
|
+
<div class="barLeft">
|
|
51
|
+
<a class="barMark" id="barMark" href="/">
|
|
52
|
+
<svg viewBox="0 0 26 20" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.4"
|
|
53
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
54
|
+
<ellipse cx="8.4" cy="5.6" rx="6.6" ry="2.9"/>
|
|
55
|
+
<path d="M1.8 5.6v6.6a6.6 2.9 0 0 0 13.2 0V5.6"/>
|
|
56
|
+
<ellipse cx="19.6" cy="8.4" rx="4.7" ry="2.1"/>
|
|
57
|
+
<path d="M14.9 8.4v4.6a4.7 2.1 0 0 0 9.4 0V8.4"/>
|
|
58
|
+
</svg>
|
|
59
|
+
<span>{{productName}}</span>
|
|
60
|
+
</a>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="barRight">
|
|
63
|
+
<button class="trig" type="button" id="projTrig" data-menu="projects" aria-expanded="false" aria-controls="menuProjects">Projects<svg class="cv" viewBox="0 0 20 20" aria-hidden="true"><path d="m5.6 8 4.4 4.4L14.4 8"/></svg></button>
|
|
64
|
+
<!-- the identity chip IS the profile trigger; assets/hub-bar.js fills it from
|
|
65
|
+
GET /me. Signed out it stays hidden and the sign-in door takes its slot. -->
|
|
66
|
+
<button class="trig who" type="button" id="who" data-menu="profile" aria-expanded="false" aria-controls="menuProfile" hidden><img id="whoImg" alt="" hidden><span class="whoName" id="whoLogin"></span><svg class="cv" viewBox="0 0 20 20" aria-hidden="true"><path d="m5.6 8 4.4 4.4L14.4 8"/></svg></button>
|
|
67
|
+
<a class="signinDoor" id="signinTop" hidden href="/api/bongos/auth/web/start?return=/contact&repos=1">Sign in</a>
|
|
42
68
|
</div>
|
|
43
69
|
</header>
|
|
44
70
|
|
|
45
|
-
<
|
|
71
|
+
<div class="menuBand" id="menuProjects" data-menu="projects" hidden>
|
|
72
|
+
<div class="bandIn">
|
|
73
|
+
<ul class="bandRow">
|
|
74
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=map">Sky</a><span class="bandC">the sky of every project</span></li>
|
|
75
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=map&sub=table">Table</a><span class="bandC">the same projects as rows</span></li>
|
|
76
|
+
<li class="bandCell" id="mineCell"><a class="bandT" href="/projects?view=mine">Your projects</a><span class="bandC">the ones you own or build on</span></li>
|
|
77
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=new">New project</a><span class="bandC">five questions and a home</span></li>
|
|
78
|
+
</ul>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="menuBand" id="menuProfile" data-menu="profile" hidden>
|
|
82
|
+
<div class="bandIn bandProfile">
|
|
83
|
+
<p class="bandWho" id="bandWho"><img id="bandWhoImg" alt="" hidden><span class="whoName" id="bandWhoLogin"></span></p>
|
|
84
|
+
<ul class="bandRow">
|
|
85
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=mine">My projects</a></li>
|
|
86
|
+
<!-- apex-only because /account is: it is served only where platform-identity
|
|
87
|
+
runs, so a child instance loses the item whole (as on projects.html) -->
|
|
88
|
+
<!--apex-only:start--><li class="bandCell"><a class="bandT" href="/account">Account</a></li><!--apex-only:end-->
|
|
89
|
+
<!-- the honest hover target; assets/hub-bar.js upgrades the click to the POST -->
|
|
90
|
+
<li class="bandCell"><a class="bandT signout" href="/api/bongos/auth/logout">Sign out</a></li>
|
|
91
|
+
</ul>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<main class="legal" id="legal">
|
|
46
96
|
<h1>Contact</h1>
|
|
47
97
|
<p class="lede">Two doors. Both are public, and both are how {{productName}} itself works day to day.</p>
|
|
48
98
|
|
|
@@ -68,6 +118,7 @@
|
|
|
68
118
|
</section>
|
|
69
119
|
</main>
|
|
70
120
|
|
|
71
|
-
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.npmjs.com/package/@bongos/core" target="_blank" rel="noopener">Open source</a> (<a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">AGPL-3.0</a>).<nav class="footNav" aria-label="Site"><a href="/privacy">Privacy</a><a href="/terms">Terms</a><a href="/contact" aria-current="page">Contact</a></nav></footer>
|
|
121
|
+
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.npmjs.com/package/@bongos/core" target="_blank" rel="noopener">Open source</a> (<a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">AGPL-3.0</a>).<nav class="footNav" aria-label="Site"><a href="/privacy">Privacy</a><a href="/terms">Terms</a><a href="/contact" aria-current="page">Contact</a><a data-link-hall href="{{buildersOrigin}}">Builders' hall</a></nav></footer>
|
|
122
|
+
<script src="/assets/hub-bar.js"></script>
|
|
72
123
|
</body>
|
|
73
124
|
</html>
|
|
@@ -18,9 +18,14 @@
|
|
|
18
18
|
<!-- Dark only, like every apex page (task 1003336, ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
19
19
|
<style>
|
|
20
20
|
/* The reading column only. No :root block — the pack's injected theme owns the
|
|
21
|
-
tokens (landing direction C1); everything below reads them.
|
|
21
|
+
tokens (landing direction C1); everything below reads them. The bar and the
|
|
22
|
+
bands are cosmos.css's own rules, not restyled here. */
|
|
23
|
+
/* the skip link un-hides itself on focus, reversing everything .vh sets (the hub's rule) */
|
|
24
|
+
.skip:focus{position:fixed;top:8px;left:8px;z-index:100;width:auto;height:auto;margin:0;overflow:visible;clip-path:none;white-space:normal;
|
|
25
|
+
font-size:14px;font-weight:800;text-decoration:none;color:var(--ink);background:var(--bg-card);
|
|
26
|
+
border:2px solid var(--ctl-line);border-radius:12px;padding:10px 16px;}
|
|
22
27
|
.legal{position:relative;z-index:1;max-width:720px;margin:0 auto;padding:clamp(6px,1.5vw,18px) clamp(16px,4vw,40px) 48px;}
|
|
23
|
-
.legal h1{font-family:var(--font-display);font-weight:700;font-size:clamp(26px,3.8vw,36px);letter-spacing:-0.5px;color:var(--chrome-ink);text-shadow:var(--chrome-glow);margin
|
|
28
|
+
.legal h1{font-family:var(--font-display);font-weight:700;font-size:clamp(26px,3.8vw,36px);letter-spacing:-0.5px;color:var(--chrome-ink);text-shadow:var(--chrome-glow);margin:18px 0 6px;}
|
|
24
29
|
.legal .lede{color:var(--hero-lede);text-shadow:var(--chrome-glow);margin-bottom:18px;}
|
|
25
30
|
.legal h2{font-family:var(--font-display);font-weight:700;font-size:var(--fs-h3);letter-spacing:-0.3px;line-height:1.25;margin:22px 0 8px;}
|
|
26
31
|
.legal .card h2:first-child{margin-top:0;}
|
|
@@ -33,15 +38,60 @@
|
|
|
33
38
|
</style>
|
|
34
39
|
</head>
|
|
35
40
|
<body>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
<!-- THE BAR and its two bands are the hub's own (projects.html; task 1004489,
|
|
42
|
+
after the account page's task 1004455): same markup, same cosmos.css rules,
|
|
43
|
+
so following a footer link from /projects or /account keeps the chrome. No
|
|
44
|
+
map search (it only filters the map), the band links name /projects in full
|
|
45
|
+
because this page has no views of its own, and no item is the current place.
|
|
46
|
+
assets/hub-bar.js fills the chip from GET /me and runs the bands. -->
|
|
47
|
+
<a class="skip vh" href="#legal">Skip to content</a>
|
|
48
|
+
<header class="shell bar">
|
|
49
|
+
<div class="barLeft">
|
|
50
|
+
<a class="barMark" id="barMark" href="/">
|
|
51
|
+
<svg viewBox="0 0 26 20" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.4"
|
|
52
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
53
|
+
<ellipse cx="8.4" cy="5.6" rx="6.6" ry="2.9"/>
|
|
54
|
+
<path d="M1.8 5.6v6.6a6.6 2.9 0 0 0 13.2 0V5.6"/>
|
|
55
|
+
<ellipse cx="19.6" cy="8.4" rx="4.7" ry="2.1"/>
|
|
56
|
+
<path d="M14.9 8.4v4.6a4.7 2.1 0 0 0 9.4 0V8.4"/>
|
|
57
|
+
</svg>
|
|
58
|
+
<span>{{productName}}</span>
|
|
59
|
+
</a>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="barRight">
|
|
62
|
+
<button class="trig" type="button" id="projTrig" data-menu="projects" aria-expanded="false" aria-controls="menuProjects">Projects<svg class="cv" viewBox="0 0 20 20" aria-hidden="true"><path d="m5.6 8 4.4 4.4L14.4 8"/></svg></button>
|
|
63
|
+
<!-- the identity chip IS the profile trigger; assets/hub-bar.js fills it from
|
|
64
|
+
GET /me. Signed out it stays hidden and the sign-in door takes its slot. -->
|
|
65
|
+
<button class="trig who" type="button" id="who" data-menu="profile" aria-expanded="false" aria-controls="menuProfile" hidden><img id="whoImg" alt="" hidden><span class="whoName" id="whoLogin"></span><svg class="cv" viewBox="0 0 20 20" aria-hidden="true"><path d="m5.6 8 4.4 4.4L14.4 8"/></svg></button>
|
|
66
|
+
<a class="signinDoor" id="signinTop" hidden href="/api/bongos/auth/web/start?return=/privacy&repos=1">Sign in</a>
|
|
41
67
|
</div>
|
|
42
68
|
</header>
|
|
43
69
|
|
|
44
|
-
<
|
|
70
|
+
<div class="menuBand" id="menuProjects" data-menu="projects" hidden>
|
|
71
|
+
<div class="bandIn">
|
|
72
|
+
<ul class="bandRow">
|
|
73
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=map">Sky</a><span class="bandC">the sky of every project</span></li>
|
|
74
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=map&sub=table">Table</a><span class="bandC">the same projects as rows</span></li>
|
|
75
|
+
<li class="bandCell" id="mineCell"><a class="bandT" href="/projects?view=mine">Your projects</a><span class="bandC">the ones you own or build on</span></li>
|
|
76
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=new">New project</a><span class="bandC">five questions and a home</span></li>
|
|
77
|
+
</ul>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="menuBand" id="menuProfile" data-menu="profile" hidden>
|
|
81
|
+
<div class="bandIn bandProfile">
|
|
82
|
+
<p class="bandWho" id="bandWho"><img id="bandWhoImg" alt="" hidden><span class="whoName" id="bandWhoLogin"></span></p>
|
|
83
|
+
<ul class="bandRow">
|
|
84
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=mine">My projects</a></li>
|
|
85
|
+
<!-- apex-only because /account is: it is served only where platform-identity
|
|
86
|
+
runs, so a child instance loses the item whole (as on projects.html) -->
|
|
87
|
+
<!--apex-only:start--><li class="bandCell"><a class="bandT" href="/account">Account</a></li><!--apex-only:end-->
|
|
88
|
+
<!-- the honest hover target; assets/hub-bar.js upgrades the click to the POST -->
|
|
89
|
+
<li class="bandCell"><a class="bandT signout" href="/api/bongos/auth/logout">Sign out</a></li>
|
|
90
|
+
</ul>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<main class="legal" id="legal">
|
|
45
95
|
<h1>Privacy</h1>
|
|
46
96
|
<p class="lede">What {{productName}} keeps about you and your project, who else sees it, and how long it stays — in plain words. The code that does all of this is open, so every sentence here can be checked.</p>
|
|
47
97
|
|
|
@@ -77,6 +127,7 @@
|
|
|
77
127
|
</section>
|
|
78
128
|
</main>
|
|
79
129
|
|
|
80
|
-
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.npmjs.com/package/@bongos/core" target="_blank" rel="noopener">Open source</a> (<a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">AGPL-3.0</a>).<nav class="footNav" aria-label="Site"><a href="/privacy" aria-current="page">Privacy</a><a href="/terms">Terms</a><a href="/contact">Contact</a></nav></footer>
|
|
130
|
+
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.npmjs.com/package/@bongos/core" target="_blank" rel="noopener">Open source</a> (<a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">AGPL-3.0</a>).<nav class="footNav" aria-label="Site"><a href="/privacy" aria-current="page">Privacy</a><a href="/terms">Terms</a><a href="/contact">Contact</a><a data-link-hall href="{{buildersOrigin}}">Builders' hall</a></nav></footer>
|
|
131
|
+
<script src="/assets/hub-bar.js"></script>
|
|
81
132
|
</body>
|
|
82
133
|
</html>
|
|
@@ -18,9 +18,14 @@
|
|
|
18
18
|
<!-- Dark only, like every apex page (task 1003336, ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
19
19
|
<style>
|
|
20
20
|
/* The reading column only. No :root block — the pack's injected theme owns the
|
|
21
|
-
tokens (landing direction C1); everything below reads them.
|
|
21
|
+
tokens (landing direction C1); everything below reads them. The bar and the
|
|
22
|
+
bands are cosmos.css's own rules, not restyled here. */
|
|
23
|
+
/* the skip link un-hides itself on focus, reversing everything .vh sets (the hub's rule) */
|
|
24
|
+
.skip:focus{position:fixed;top:8px;left:8px;z-index:100;width:auto;height:auto;margin:0;overflow:visible;clip-path:none;white-space:normal;
|
|
25
|
+
font-size:14px;font-weight:800;text-decoration:none;color:var(--ink);background:var(--bg-card);
|
|
26
|
+
border:2px solid var(--ctl-line);border-radius:12px;padding:10px 16px;}
|
|
22
27
|
.legal{position:relative;z-index:1;max-width:720px;margin:0 auto;padding:clamp(6px,1.5vw,18px) clamp(16px,4vw,40px) 48px;}
|
|
23
|
-
.legal h1{font-family:var(--font-display);font-weight:700;font-size:clamp(26px,3.8vw,36px);letter-spacing:-0.5px;color:var(--chrome-ink);text-shadow:var(--chrome-glow);margin
|
|
28
|
+
.legal h1{font-family:var(--font-display);font-weight:700;font-size:clamp(26px,3.8vw,36px);letter-spacing:-0.5px;color:var(--chrome-ink);text-shadow:var(--chrome-glow);margin:18px 0 6px;}
|
|
24
29
|
.legal .lede{color:var(--hero-lede);text-shadow:var(--chrome-glow);margin-bottom:18px;}
|
|
25
30
|
.legal h2{font-family:var(--font-display);font-weight:700;font-size:var(--fs-h3);letter-spacing:-0.3px;line-height:1.25;margin:22px 0 8px;}
|
|
26
31
|
.legal .card h2:first-child{margin-top:0;}
|
|
@@ -33,15 +38,60 @@
|
|
|
33
38
|
</style>
|
|
34
39
|
</head>
|
|
35
40
|
<body>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
<!-- THE BAR and its two bands are the hub's own (projects.html; task 1004489,
|
|
42
|
+
after the account page's task 1004455): same markup, same cosmos.css rules,
|
|
43
|
+
so following a footer link from /projects or /account keeps the chrome. No
|
|
44
|
+
map search (it only filters the map), the band links name /projects in full
|
|
45
|
+
because this page has no views of its own, and no item is the current place.
|
|
46
|
+
assets/hub-bar.js fills the chip from GET /me and runs the bands. -->
|
|
47
|
+
<a class="skip vh" href="#legal">Skip to content</a>
|
|
48
|
+
<header class="shell bar">
|
|
49
|
+
<div class="barLeft">
|
|
50
|
+
<a class="barMark" id="barMark" href="/">
|
|
51
|
+
<svg viewBox="0 0 26 20" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.4"
|
|
52
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
53
|
+
<ellipse cx="8.4" cy="5.6" rx="6.6" ry="2.9"/>
|
|
54
|
+
<path d="M1.8 5.6v6.6a6.6 2.9 0 0 0 13.2 0V5.6"/>
|
|
55
|
+
<ellipse cx="19.6" cy="8.4" rx="4.7" ry="2.1"/>
|
|
56
|
+
<path d="M14.9 8.4v4.6a4.7 2.1 0 0 0 9.4 0V8.4"/>
|
|
57
|
+
</svg>
|
|
58
|
+
<span>{{productName}}</span>
|
|
59
|
+
</a>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="barRight">
|
|
62
|
+
<button class="trig" type="button" id="projTrig" data-menu="projects" aria-expanded="false" aria-controls="menuProjects">Projects<svg class="cv" viewBox="0 0 20 20" aria-hidden="true"><path d="m5.6 8 4.4 4.4L14.4 8"/></svg></button>
|
|
63
|
+
<!-- the identity chip IS the profile trigger; assets/hub-bar.js fills it from
|
|
64
|
+
GET /me. Signed out it stays hidden and the sign-in door takes its slot. -->
|
|
65
|
+
<button class="trig who" type="button" id="who" data-menu="profile" aria-expanded="false" aria-controls="menuProfile" hidden><img id="whoImg" alt="" hidden><span class="whoName" id="whoLogin"></span><svg class="cv" viewBox="0 0 20 20" aria-hidden="true"><path d="m5.6 8 4.4 4.4L14.4 8"/></svg></button>
|
|
66
|
+
<a class="signinDoor" id="signinTop" hidden href="/api/bongos/auth/web/start?return=/terms&repos=1">Sign in</a>
|
|
41
67
|
</div>
|
|
42
68
|
</header>
|
|
43
69
|
|
|
44
|
-
<
|
|
70
|
+
<div class="menuBand" id="menuProjects" data-menu="projects" hidden>
|
|
71
|
+
<div class="bandIn">
|
|
72
|
+
<ul class="bandRow">
|
|
73
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=map">Sky</a><span class="bandC">the sky of every project</span></li>
|
|
74
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=map&sub=table">Table</a><span class="bandC">the same projects as rows</span></li>
|
|
75
|
+
<li class="bandCell" id="mineCell"><a class="bandT" href="/projects?view=mine">Your projects</a><span class="bandC">the ones you own or build on</span></li>
|
|
76
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=new">New project</a><span class="bandC">five questions and a home</span></li>
|
|
77
|
+
</ul>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="menuBand" id="menuProfile" data-menu="profile" hidden>
|
|
81
|
+
<div class="bandIn bandProfile">
|
|
82
|
+
<p class="bandWho" id="bandWho"><img id="bandWhoImg" alt="" hidden><span class="whoName" id="bandWhoLogin"></span></p>
|
|
83
|
+
<ul class="bandRow">
|
|
84
|
+
<li class="bandCell"><a class="bandT" href="/projects?view=mine">My projects</a></li>
|
|
85
|
+
<!-- apex-only because /account is: it is served only where platform-identity
|
|
86
|
+
runs, so a child instance loses the item whole (as on projects.html) -->
|
|
87
|
+
<!--apex-only:start--><li class="bandCell"><a class="bandT" href="/account">Account</a></li><!--apex-only:end-->
|
|
88
|
+
<!-- the honest hover target; assets/hub-bar.js upgrades the click to the POST -->
|
|
89
|
+
<li class="bandCell"><a class="bandT signout" href="/api/bongos/auth/logout">Sign out</a></li>
|
|
90
|
+
</ul>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<main class="legal" id="legal">
|
|
45
95
|
<h1>Terms</h1>
|
|
46
96
|
<p class="lede">The short version: this is free software run by its owner, your code stays yours, and you are responsible for what you ship.</p>
|
|
47
97
|
|
|
@@ -82,6 +132,7 @@
|
|
|
82
132
|
</section>
|
|
83
133
|
</main>
|
|
84
134
|
|
|
85
|
-
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.npmjs.com/package/@bongos/core" target="_blank" rel="noopener">Open source</a> (<a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">AGPL-3.0</a>).<nav class="footNav" aria-label="Site"><a href="/privacy">Privacy</a><a href="/terms" aria-current="page">Terms</a><a href="/contact">Contact</a></nav></footer>
|
|
135
|
+
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.npmjs.com/package/@bongos/core" target="_blank" rel="noopener">Open source</a> (<a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">AGPL-3.0</a>).<nav class="footNav" aria-label="Site"><a href="/privacy">Privacy</a><a href="/terms" aria-current="page">Terms</a><a href="/contact">Contact</a><a data-link-hall href="{{buildersOrigin}}">Builders' hall</a></nav></footer>
|
|
136
|
+
<script src="/assets/hub-bar.js"></script>
|
|
86
137
|
</body>
|
|
87
138
|
</html>
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.53",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.53",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.53",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -9074,5 +9074,17 @@
|
|
|
9074
9074
|
"id": "1004531",
|
|
9075
9075
|
"text": "We walked the whole Governor City against its design, in light and dark and on a phone: the home city, the street, every building, a vote decided from its card, and an amendment drafted from a building. Fixes: drafting a chang"
|
|
9076
9076
|
}
|
|
9077
|
+
],
|
|
9078
|
+
"1.21.52": [
|
|
9079
|
+
{
|
|
9080
|
+
"id": "1004489",
|
|
9081
|
+
"text": "The Privacy, Terms and Contact pages now have the same top bar as the projects and account pages — the Projects menu and your profile menu — so following a footer link no longer swaps the whole header."
|
|
9082
|
+
}
|
|
9083
|
+
],
|
|
9084
|
+
"1.21.53": [
|
|
9085
|
+
{
|
|
9086
|
+
"id": "1004498",
|
|
9087
|
+
"text": "Ideating mode now has its own tool. An \"Ideas\" button in the top bar opens a side panel where an ideator writes about the page they are on. \"Turn into ideas\" has an AI split the text into separate ideas, each filed under their"
|
|
9088
|
+
}
|
|
9077
9089
|
]
|
|
9078
9090
|
}
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.53'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -635,7 +635,7 @@ try {
|
|
|
635
635
|
assert.match(HTML, /<body data-page="tweak-editor"[^>]*data-fullscreen-switch/);
|
|
636
636
|
assert.equal((HTML.match(/<h1\b/g) || []).length, 1);
|
|
637
637
|
assert.ok(HTML.indexOf('tweak-editor-lib.js') < HTML.indexOf('tweak-editor.js?'), 'the lib loads first');
|
|
638
|
-
assert.match(HTML, /shell\.js\?v=2026-10-04-
|
|
638
|
+
assert.match(HTML, /shell\.js\?v=2026-10-04-lightbulb/);
|
|
639
639
|
assert.equal(/fonts\.googleapis\.com\/css/.test(HTML), false);
|
|
640
640
|
for (const id of ['tw-bar', 'tw-counter', 'tw-open', 'tw-open-more', 'tw-divider', 'tw-openwrap', 'tw-open-menu', 'tw-open-tab', 'tw-open-split', 'tw-frame', 'tw-frame-view', 'tw-split-close', 'tw-download', 'tw-upload', 'tw-file', 'tw-submit', 'tw-paper', 'tw-doc', 'tw-notice', 'tw-done', 'tw-take', 'tw-pick', 'tw-picks', 'tw-refused', 'tw-refused-title', 'tw-refused-list', 'tw-refused-next', 'tw-refused-download', 'tw-refused-close']) {
|
|
641
641
|
assert.ok(new RegExp(`id="${id}"`).test(HTML), `#${id} is in the markup`);
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
// header of its own — the same markup as projects.html, cosmos.css's rules,
|
|
19
19
|
// no map search, links into /projects, Account as the current place — and
|
|
20
20
|
// the bands EXECUTED: the chip from GET /me, open/close, Escape, the arrows,
|
|
21
|
-
// a click outside, and sign out as the POST.
|
|
21
|
+
// a click outside, and sign out as the POST. The bands script is the shared
|
|
22
|
+
// assets/hub-bar.js since task 1004489 (the three footer doors load it too),
|
|
23
|
+
// so the page's own script and that file run together here, as in a browser.
|
|
22
24
|
//
|
|
23
25
|
// DB-free. Run: node --test tests/hub_account_page.mjs
|
|
24
26
|
|
|
@@ -37,6 +39,7 @@ const LANDING = path.join(ROOT, 'modules', 'public-landing', 'public');
|
|
|
37
39
|
const HALL = path.join(ROOT, 'modules', 'hall-ui', 'public');
|
|
38
40
|
const PAGE = fs.readFileSync(path.join(LANDING, 'account.html'), 'utf8').replace(/\r\n/g, '\n');
|
|
39
41
|
const SCRIPT = PAGE.split('<script>').pop().split('</script>')[0];
|
|
42
|
+
const BAR = fs.readFileSync(path.join(LANDING, 'assets', 'hub-bar.js'), 'utf8');
|
|
40
43
|
const HUB = fs.readFileSync(path.join(LANDING, 'projects.html'), 'utf8').replace(/\r\n/g, '\n');
|
|
41
44
|
const COSMOS = fs.readFileSync(path.join(LANDING, 'assets', 'cosmos.css'), 'utf8');
|
|
42
45
|
|
|
@@ -62,7 +65,9 @@ test('portable and dark only: stamped title, balanced sentinels, no :root, no ba
|
|
|
62
65
|
});
|
|
63
66
|
|
|
64
67
|
test('the script builds no markup from strings, and the controls are real markup', () => {
|
|
65
|
-
|
|
68
|
+
for (const [name, src] of [['the page', SCRIPT], ['assets/hub-bar.js', BAR]]) {
|
|
69
|
+
assert.doesNotMatch(src, /innerHTML|insertAdjacentHTML|outerHTML/, `${name}: live DOM is textContent / createElement only`);
|
|
70
|
+
}
|
|
66
71
|
for (const id of ['fName', 'fBio', 'fHandle', 'fLinkWebsite', 'fLinkX', 'fLinkLinkedin', 'fLinkDiscord', 'profileSave', 'handleSave', 'privacySave', 'recordSave']) {
|
|
67
72
|
assert.match(PAGE, new RegExp(`id="${id}"`), `#${id} ships in the markup`);
|
|
68
73
|
}
|
|
@@ -76,8 +81,17 @@ test('the script builds no markup from strings, and the controls are real markup
|
|
|
76
81
|
test('the door returns here, and the page calls only its own-scoped routes', () => {
|
|
77
82
|
assert.match(PAGE, /href="\/api\/bongos\/auth\/web\/start\?return=\/account&repos=1"/, 'sign-in lands back on /account, asking for repo access once (task 1004549)');
|
|
78
83
|
const calls = [...SCRIPT.matchAll(/request\('(GET|PATCH|POST)', '([^']+)'/g)].map((m) => `${m[1]} ${m[2]}`);
|
|
79
|
-
assert.deepEqual([...new Set(calls)].sort(), ['GET /me
|
|
80
|
-
|
|
84
|
+
assert.deepEqual([...new Set(calls)].sort(), ['GET /me/cross-project', 'PATCH /me/cross-project', 'PATCH /me/handle', 'PATCH /me/profile'].sort());
|
|
85
|
+
// the bar's two calls live in the shared asset: the identity read and sign out
|
|
86
|
+
const barCalls = [...BAR.matchAll(/fetch\(API \+ '([^']+)', \{ method: '(GET|POST)'/g)].map((m) => `${m[2]} ${m[1]}`);
|
|
87
|
+
assert.deepEqual(barCalls.sort(), ['GET /me', 'POST /auth/logout']);
|
|
88
|
+
assert.match(BAR, /var API = '\/api\/bongos';/);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('the bar’s script is the shared asset, loaded before the page’s own (task 1004489)', () => {
|
|
92
|
+
assert.match(PAGE, /<script src="\/assets\/hub-bar\.js"><\/script>\n<script>\n\(function \(\) \{/, 'hub-bar.js runs first, so window.HubBar exists when the page script asks for it');
|
|
93
|
+
assert.doesNotMatch(SCRIPT, /function (openBand|closeMenus|openMenu|paintBar)\b|\/auth\/logout/, 'no second copy of the bands, the chip or sign out in the page');
|
|
94
|
+
assert.match(SCRIPT, /var bar = window\.HubBar \|\| \{ paint: function \(\) \{\}, ready: Promise\.resolve\(null\) \};/, 'and the page survives the asset failing to load');
|
|
81
95
|
});
|
|
82
96
|
|
|
83
97
|
// ── 1b. the bar is the hub's (task 1004455) ──────────────────────────────────
|
|
@@ -259,7 +273,10 @@ function boot(answer) {
|
|
|
259
273
|
return Promise.resolve({ status, ok: status >= 200 && status < 300, json: () => Promise.resolve(data) });
|
|
260
274
|
};
|
|
261
275
|
const location = { hostname: 'cloudbongos.com', pathname: '/account' };
|
|
262
|
-
|
|
276
|
+
// one global, as in the browser: hub-bar.js sets window.HubBar, then the page reads it
|
|
277
|
+
const ctx = vm.createContext({ document, fetch, location, window: { location: { assign(p) { dom.assigned = p; } } }, URL, Array, Object, JSON, Promise });
|
|
278
|
+
vm.runInContext(BAR, ctx);
|
|
279
|
+
vm.runInContext(SCRIPT, ctx);
|
|
263
280
|
return dom;
|
|
264
281
|
}
|
|
265
282
|
const settle = async () => { for (let i = 0; i < 8; i++) await new Promise((r) => setImmediate(r)); };
|