@bongos/core 1.19.400 → 1.19.401

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/src/module-api.js CHANGED
@@ -1240,8 +1240,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
1240
1240
  // landed since 1.19.398 with no explicit bump. run 33201243958. (task 1002620)
1241
1241
  // 1.19.400 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1242
1242
  // landed since 1.19.399 with no explicit bump. run 33201979306. (task 1002620)
1243
+ // 1.19.401 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1244
+ // landed since 1.19.400 with no explicit bump. run 33205015717. (task 1002620)
1243
1245
  // ---------------------------------------------------------------------------
1244
- const CORE_VERSION = '1.19.400'; // CI auto-patch carrier (ADR 0161); see changelog above
1246
+ const CORE_VERSION = '1.19.401'; // CI auto-patch carrier (ADR 0161); see changelog above
1245
1247
 
1246
1248
  // A namespaced logger so a module's log lines are attributable + consistent.
1247
1249
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -475,14 +475,17 @@ test('the bar is markup: the mark, the Projects trigger, the mode glyph, the sig
475
475
  assert.match(PAGE_CSS, /\.topbar\{position:relative;z-index:50;height:var\(--top-h\);display:flex;align-items:center;gap:22px;\s*padding:0 26px;background:var\(--bg\);border-bottom:1px solid var\(--rule\)\}/, 'the page ground closed by one hairline, z-index unified with the hub');
476
476
  });
477
477
 
478
- test('the Projects band deep-links the hub\'s views; About and Docs sit under a hairline; no item is current here', () => {
478
+ test('the Projects band deep-links the hub\'s views; About and Docs are footer links, not band items; no item is current here', () => {
479
479
  const body = HTML.split('<body>')[1].split('<script>')[0];
480
480
  assert.match(body, /<div class="menuBand" id="menuProjects" data-menu="projects" hidden>/);
481
481
  assert.match(body, /<a class="bandT" href="\/projects\?view=map">Sky<\/a>/);
482
482
  assert.match(body, /<a class="bandT" href="\/projects\?view=map&amp;sub=table">Table<\/a>/, 'the Table item lands on the table (the hub reads sub=table on boot)');
483
483
  assert.match(body, /<li class="bandCell" id="mineCell" hidden><a class="bandT" href="\/projects\?view=mine">Your projects<\/a>/, 'member-gated: hidden until the session resolves');
484
484
  assert.match(body, /<a class="bandT" href="\/projects\?view=new">New project<\/a>/);
485
- assert.match(body, /<ul class="bandRow bandMore">\s*<li class="bandCell"><a class="bandT" href="#about">About<\/a><\/li>\s*<li class="bandCell"><a class="bandT" href="\/docs">Docs<\/a><\/li>/, 'About and Docs under the hairline (§5)');
485
+ // About and Docs are site pages, not project views (owner, 2026-08-28; task 1003332):
486
+ // they left the band for the footer row, beside Privacy / Terms / Contact
487
+ assert.doesNotMatch(body, /bandMore|<a class="bandT" href="#about">|<a class="bandT" href="\/docs">/, 'About and Docs are not in the Projects band');
488
+ assert.match(body, /<a href="#about">About<\/a>\s*<a href="\/docs">Docs<\/a>\s*<a href="\/privacy">Privacy<\/a>/, 'About and Docs open the footer row');
486
489
  assert.match(body, /<section class="band" id="about">/, 'About has a real anchor: the lifecycle band');
487
490
  assert.doesNotMatch(body.split('<main')[0], /aria-current/, 'the landing is none of those views');
488
491
  // the profile band: the person, My projects, the deferred slot, Sign out as the honest anchor
@@ -496,9 +499,10 @@ test('the Projects band deep-links the hub\'s views; About and Docs sit under a
496
499
  assert.ok(body.includes('>' + copy + '<'), `"${copy}" is only reachable with scripting on`);
497
500
  }
498
501
  // the menu container is .menuBand: .band is this page's own content sections
499
- assert.match(PAGE_CSS, /\.menuBand\{position:relative;z-index:49;background:var\(--bg-card\);border-bottom:1px solid var\(--rule\);/);
502
+ // the bands OVERLAY the page (task 1003332): absolute at the bar's height, so the hero never reflows
503
+ assert.match(PAGE_CSS, /\.menuBand\{position:absolute;left:0;right:0;top:var\(--top-h\);z-index:49;background:var\(--bg-card\);border-bottom:1px solid var\(--rule\);/);
500
504
  assert.doesNotMatch(PAGE_CSS, /\.band\{[^}]*animation:bandIn/, 'the menu rule never lands on the content bands');
501
- assert.match(PAGE_CSS, /\.bandMore \.bandT\{font-size:17px;font-weight:400;min-height:24px;display:inline-flex;align-items:center\}/, 'the About/Docs links clear the 24px floor');
505
+ assert.doesNotMatch(PAGE_CSS, /\.bandMore/, 'the About/Docs rung left the sheet with the markup');
502
506
  });
503
507
 
504
508
  test('the bands are one disclosure: Escape returns focus, arrows cycle, Tab out closes, one closes the other', () => {
@@ -49,7 +49,14 @@ test('the Projects band carries the views; Home is gone and the mark is the way
49
49
  assert.match(HUB, /href="\?view=map" data-nav="map" data-sub="sky" aria-current="page">Sky<\/a>/);
50
50
  assert.match(HUB, /href="\?view=map&amp;sub=table" data-nav="map" data-sub="table">Table<\/a>/);
51
51
  assert.match(SCRIPT, /get\('sub'\) === 'table'\) setSub\('table'\);/, 'the Table deep link lands on the table');
52
- assert.match(HUB, /<a class="bandT" href="\/#about">About<\/a>/, 'About sits under the hairline (§5)');
52
+ // setView rewrites the URL to ?view=<v>; the boot query must be captured BEFORE it
53
+ // runs or sub=table is gone by the time it is read (task 1003332)
54
+ assert.match(SCRIPT, /var bootQ = new URLSearchParams\(location\.search\);\s*setView\(viewFromUrl\(\)\);[\s\S]{0,400}?if \(bootQ\.get\('sub'\) === 'table'\) setSub\('table'\);/, 'the boot query is read once, before setView');
55
+ // About and Docs are site pages, not project views (owner, 2026-08-28; task 1003332):
56
+ // they left the band for the footer row, beside Privacy / Terms / Contact
57
+ assert.doesNotMatch(HUB, /bandMore|<a class="bandT" href="\/#about">|<a class="bandT" href="\/docs">/, 'About and Docs are not in the Projects band');
58
+ assert.match(HUB, /<nav class="footNav" aria-label="Site"><a href="\/#about">About<\/a><a href="\/docs">Docs<\/a><a href="\/privacy">Privacy<\/a>/, 'About and Docs open the footer row');
59
+ assert.doesNotMatch(COSMOS, /\.bandMore/, 'the About/Docs rung left cosmos with the markup');
53
60
  assert.doesNotMatch(HUB, /id="sub-sky"|id="sub-table"|class="toolbar"|class="seg"/, 'the toolbar and its seg are retired');
54
61
  assert.doesNotMatch(SCRIPT, /dataset\.nav === 'home'/, 'no dead Home branch in the router');
55
62
  assert.match(SCRIPT, /if \(a\.dataset\.sub\) setSub\(a\.dataset\.sub\);/, 'a band item may pick the map sub-view');
@@ -138,6 +145,10 @@ test('the bands: one disclosure, two instances — Escape returns focus, the car
138
145
  assert.match(HUB, /<div class="menuBand" id="menuProjects" data-menu="projects" hidden>/);
139
146
  assert.match(HUB, /<div class="menuBand" id="menuProfile" data-menu="profile" hidden>/);
140
147
  assert.doesNotMatch(HUB, /role="menu"|aria-haspopup/, 'plain links in a disclosure — announced as what ships');
148
+ // the bands OVERLAY the page (owner, 2026-08-28; task 1003332): absolute against the
149
+ // document at the bar's height, so opening one never reflows the sky, the table or the footer
150
+ assert.match(COSMOS, /\.menuBand\{position:absolute;left:0;right:0;top:var\(--top-h\);z-index:49;background:var\(--bg-card\);border-bottom:1px solid var\(--rule\);/, 'a band is a layer under the bar, never a push');
151
+ assert.doesNotMatch(COSMOS, /PUSHES the page/, 'the sheet no longer describes the push-down');
141
152
  assert.match(SCRIPT, /function closeMenus\(\) \{[\s\S]*?if \(inside && trig\) trig\.focus\(\);/, 'focus restoration is a property of the close, not of one caller');
142
153
  assert.match(SCRIPT, /if \(ev\.key === 'Escape' && !openBand\(\)\) closeCard\(\);/, 'the card yields Escape to an open band');
143
154
  assert.match(SCRIPT, /function setView\(v, focus\) \{[\s\S]*?\$\('qWrap'\)\.hidden = \$\('qToggle'\)\.hidden = \(v !== 'map'\);/, 'the search hides off the map in the router');
@@ -525,6 +536,22 @@ test('no sort control is offered for a column that cannot sort', () => {
525
536
  }
526
537
  });
527
538
 
539
+ test('the table is in the world: a masthead, then ledger rows — no panel, no shadow, the accent only on the row\'s door (task 1003332)', () => {
540
+ // the owner's pick from three rendered treatments: the view title in the display
541
+ // voice and one chrome object; the title is two words ("every project", the owner cut ", as rows")
542
+ assert.match(HUB, /<div class="tblMast">\s*<h2 class="tblTitle">every project<\/h2>\s*<span class="mastObj" aria-hidden="true"><\/span>\s*<\/div>\s*<table class="tbl">/, 'one masthead ahead of the table');
543
+ assert.doesNotMatch(HUB, /tblMast--t[123]|data-tbl|mastCallouts|mastField/, 'the render round\'s switch and the unpicked mastheads left with the pick');
544
+ assert.match(COSMOS, /\.tbl\{width:100%;border-collapse:collapse;background:none;\}/, 'the rows sit on the page ground — no card');
545
+ assert.doesNotMatch((COSMOS.match(/\.tbl\{[^}]*\}/) || [''])[0], /border-radius|box-shadow/, 'no radius, no shadow on the table');
546
+ assert.match(COSMOS, /\.tbl td\{padding:17px 14px;border-bottom:1px solid var\(--rule-soft\);/, 'ledger rows: hairlines');
547
+ assert.match(COSMOS, /\.tbl tbody tr:hover td\{border-bottom-color:var\(--ink-faint\);\}/, 'hover is the hairline taking ink, not an accent tint');
548
+ assert.match(COSMOS, /\.rowGo\{[^}]*color:var\(--accent-deep\);text-decoration:underline;/, 'the row\'s door is accent text');
549
+ assert.match(HUB, /<a class="rowGo" href="\/">Open →<\/a>/, 'the self row\'s door');
550
+ assert.match(SCRIPT, /'<a class="rowGo" href="' \+ esc\(safeHttpUrl\(p\.origin\)\)/, 'the generated rows\' door');
551
+ assert.match(COSMOS, /\.mastObj\{display:block;--dia:clamp\(96px,10vw,132px\);[^}]*background-image:var\(--plate-set-cut\);/, 'the object is the set\'s small bead by token, in both modes');
552
+ assert.doesNotMatch(COSMOS, /mix-blend-mode:var\(--mast/, 'the cutout needs no blend — the view wrap is a stacking context');
553
+ });
554
+
528
555
  test('every live column keeps its styling — .colType included since task 1002772', () => {
529
556
  for (const live of ['.tbl .num', '.tbl .tags', '.colTags', '.colType', '.typeTag']) {
530
557
  assert.ok(COSMOS.includes(live), `${live} styles a column that renders real data`);
@@ -85,10 +85,11 @@ test('auditFloors names each floor a render breaks, and says nothing about a cle
85
85
  });
86
86
 
87
87
  test('expectProblems compares every expectation the vocabulary allows', () => {
88
- const facts = { ...clean(), expanded: ['projTrig'], dialog: true, sel: { '#a': true, '#b': false }, activeId: 'x', activeText: 'Sky', param: { view: 'mine' }, attr: ['dark'], text: ['Table'], href: ['/docs'], count: [3], checked: false, value: ['demo'], hasClass: [true] };
89
- assert.deepEqual(lib.expectProblems(facts, { expanded: true, dialog: true, visible: ['#a'], hidden: ['#b'], open: '#a', closed: '#b', activeId: 'x', activeText: 'Sky', expandedIds: ['projTrig'], param: { view: 'mine' }, attr: [['html', 'data-mode', 'dark']], text: [['h1', 'Table']], href: [['a', '/docs']], count: [['li', 3]], value: [['#q', 'demo']], hasClass: [['#w', 'is-open']] }), []);
90
- const p = lib.expectProblems(facts, { expanded: false, dialog: false, visible: ['#b'], hidden: ['#a'], activeText: 'Table', param: { view: null }, count: [['li', 5]], checked: '#c', hasClass: [['#w', 'is-open', false]], noOverflow: true });
91
- for (const needle of ['expected no expanded trigger', 'expected no visible dialog', 'expected visible: #b', 'expected hidden: #a', 'active text "Sky", expected "Table"', '?view=mine, expected null', '3 × li, expected 5', '#c is not checked', 'still has class "is-open"']) {
88
+ const facts = { ...clean(), expanded: ['projTrig'], dialog: true, sel: { '#a': true, '#b': false }, activeId: 'x', activeText: 'Sky', param: { view: 'mine' }, attr: ['dark'], text: ['Table'], href: ['/docs'], count: [3], checked: false, value: ['demo'], hasClass: [true], top: [64] };
89
+ assert.deepEqual(lib.expectProblems(facts, { expanded: true, dialog: true, visible: ['#a'], hidden: ['#b'], open: '#a', closed: '#b', activeId: 'x', activeText: 'Sky', expandedIds: ['projTrig'], param: { view: 'mine' }, attr: [['html', 'data-mode', 'dark']], text: [['h1', 'Table']], href: [['a', '/docs']], count: [['li', 3]], value: [['#q', 'demo']], hasClass: [['#w', 'is-open']], top: [['main', 64]] }), []);
90
+ // `top` is how a probe proves an overlay did not push the page (task 1003332)
91
+ const p = lib.expectProblems(facts, { expanded: false, dialog: false, visible: ['#b'], hidden: ['#a'], activeText: 'Table', param: { view: null }, count: [['li', 5]], checked: '#c', hasClass: [['#w', 'is-open', false]], noOverflow: true, top: [['main', 185]] });
92
+ for (const needle of ['expected no expanded trigger', 'expected no visible dialog', 'expected visible: #b', 'expected hidden: #a', 'active text "Sky", expected "Table"', '?view=mine, expected null', '3 × li, expected 5', '#c is not checked', 'still has class "is-open"', 'main top 64px, expected 185px']) {
92
93
  assert.ok(p.some((x) => x.includes(needle)), `missing: ${needle} in ${p.join(' | ')}`);
93
94
  }
94
95
  });