@bongos/core 1.19.407 → 1.19.409
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 +47 -27
- package/clients/bongos-client/index.d.ts +2 -2
- package/docs/api/openapi.json +5 -1
- package/docs/api-reference.md +2 -2
- package/docs/copy-inventory.md +172 -172
- package/docs/copy-registry.json +219 -219
- package/docs/design/mocks/hub/planets.html +232 -0
- package/docs/design/projects-hub-direction-v2.md +10 -0
- package/modules/platform-identity/migrations/platform_identity_012_project_template.sql +26 -0
- package/modules/platform-identity/migrations/platform_identity_012_recent_ships.sql +40 -0
- package/modules/platform-identity/platform-identity.js +105 -14
- package/modules/platform-identity/routes/projects.js +4 -0
- package/modules/platform-identity/routes/sso.js +10 -0
- package/modules/platform-identity/tests/platform-identity.mjs +36 -2
- package/modules/ui-design/kit/fixtures/featured.json +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/cosmos.css +107 -68
- package/public-landing/projects.html +40 -40
- package/public-landing/projects.states.json +1 -1
- package/src/bongos/auth-admission.js +89 -8
- package/src/bongos/routes/auth.js +21 -0
- package/src/module-api.js +14 -19
- package/tests/hub_recent_ships.mjs +373 -0
- package/tests/projects_hub.mjs +109 -0
package/tests/projects_hub.mjs
CHANGED
|
@@ -1399,3 +1399,112 @@ test('the joinability in-flight line shares the visibility section’s push pred
|
|
|
1399
1399
|
assert.match(SCRIPT, /\$\('mJoin'\)\.addEventListener\('click'[\s\S]{0,200}data-join-save[\s\S]{0,40}doJoinSave\(\)/,
|
|
1400
1400
|
'the Save control is wired to ITS save — a copy that left the listener on #mVis ships a dead button');
|
|
1401
1401
|
});
|
|
1402
|
+
|
|
1403
|
+
/* ── the planets and the field (task 1003319) — the map's orbs as the landing's
|
|
1404
|
+
chrome objects in the project's accent, a per-project template with a
|
|
1405
|
+
deterministic default walk, and the field brought to the world. ── */
|
|
1406
|
+
test('the planet templates: nine names, the walk changes shape and style each step, the catalog and the migration agree', () => {
|
|
1407
|
+
const list = (SCRIPT.match(/var ORB_TEMPLATES = \[([^\]]*)\]/) || [, ''])[1];
|
|
1408
|
+
const names = [...list.matchAll(/'([a-z-]+)'/g)].map((m) => m[1]);
|
|
1409
|
+
assert.equal(names.length, 9);
|
|
1410
|
+
assert.equal(new Set(names).size, 9, 'nine distinct templates');
|
|
1411
|
+
for (const t of names) assert.match(t, /^(blob|pair|bead)-(tint|wash|chrome)$/);
|
|
1412
|
+
for (let i = 1; i < names.length; i++) {
|
|
1413
|
+
const [s0, y0] = names[i - 1].split('-');
|
|
1414
|
+
const [s1, y1] = names[i].split('-');
|
|
1415
|
+
assert.ok(s0 !== s1 && y0 !== y1, `walk step ${i} changes both shape and style`);
|
|
1416
|
+
}
|
|
1417
|
+
const pi = fs.readFileSync(path.join(ROOT, 'modules', 'platform-identity', 'platform-identity.js'), 'utf8');
|
|
1418
|
+
const server = [...((pi.match(/const PROJECT_TEMPLATES = \[([\s\S]*?)\];/) || [, ''])[1]).matchAll(/'([a-z-]+)'/g)].map((m) => m[1]);
|
|
1419
|
+
assert.deepEqual(server, names, 'the hub and the catalog walk the same list in the same order');
|
|
1420
|
+
const mig = fs.readFileSync(path.join(ROOT, 'modules', 'platform-identity', 'migrations', 'platform_identity_012_project_template.sql'), 'utf8');
|
|
1421
|
+
for (const t of names) assert.ok(mig.includes(`'${t}'`), `migration 012 names ${t}`);
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
test('the walk assigns the template by canonical position beside the accent; a curated template wins; the platform is the pair in chrome', () => {
|
|
1425
|
+
assert.match(SCRIPT, /function assignAccents[\s\S]*?p\._acc = ORB_ACCENTS\[i % ORB_ACCENTS\.length\];\s*p\._tpl = ORB_TEMPLATES\[i % ORB_TEMPLATES\.length\];/,
|
|
1426
|
+
'one walk, two assignments');
|
|
1427
|
+
assert.match(SCRIPT, /function templateOf\(p\) \{\s*return ORB_TEMPLATES\.indexOf\(p\.template\) !== -1 \? p\.template : \(p\._tpl \|\| ORB_TEMPLATES\[0\]\);/,
|
|
1428
|
+
'a curated name wins; an unknown name is not a choice');
|
|
1429
|
+
assert.match(SCRIPT, /_acc: ORB_ACCENTS\[slugAccent\(inst\.slug\)\], _tpl: slugTemplate\(inst\.slug\)/, 'an owned-only entry walks by its slug');
|
|
1430
|
+
assert.match(SCRIPT, /var tpl = templateOf\(p\), tplParts = tpl\.split\('-'\);\s*a\.setAttribute\('data-template', tpl\);\s*a\.setAttribute\('data-shape', tplParts\[0\]\);\s*a\.setAttribute\('data-style', tplParts\[1\]\);/,
|
|
1431
|
+
'the orb carries its template as data attributes; the sheet does the rest');
|
|
1432
|
+
assert.match(HUB, /<a class="worldOrb" id="selfOrb" href="\/" data-shape="pair" data-style="chrome" data-template="pair-chrome"/, 'the platform is its own mark');
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
test("the planet is the landing's object on the landing's crop maths: the three rectangles are byte-equal across the two sheets", () => {
|
|
1436
|
+
const LANDING = fs.readFileSync(path.join(ROOT, 'public-landing', 'index.html'), 'utf8');
|
|
1437
|
+
const landingCrop = (cls) => {
|
|
1438
|
+
const m = LANDING.match(new RegExp(`\\.${cls}\\{[^}]*--bead-ar:([^;]+);--bead-zoom:([^;]+);--bead-px:([^;]+);--bead-py:([^;}]+)`));
|
|
1439
|
+
assert.ok(m, `the landing declares .${cls}`);
|
|
1440
|
+
return m.slice(1, 5).map((s) => s.trim());
|
|
1441
|
+
};
|
|
1442
|
+
const hubCrop = (shape) => {
|
|
1443
|
+
const m = COSMOS.match(new RegExp(`\\.worldOrb\\[data-shape="${shape}"\\]\\{--tpl-src:var\\((--plate-[a-z-]+)\\);--tpl-ar:([^;]+);--tpl-zoom:([^;]+);--tpl-px:([^;]+);--tpl-py:([^;}]+)`));
|
|
1444
|
+
assert.ok(m, `the hub declares the ${shape}`);
|
|
1445
|
+
return { src: m[1], crop: m.slice(2, 6).map((s) => s.trim()) };
|
|
1446
|
+
};
|
|
1447
|
+
assert.deepEqual(hubCrop('blob').crop, landingCrop('o1'));
|
|
1448
|
+
assert.equal(hubCrop('blob').src, '--plate-bead-cut');
|
|
1449
|
+
assert.deepEqual(hubCrop('pair').crop, landingCrop('o2'));
|
|
1450
|
+
assert.equal(hubCrop('pair').src, '--plate-bongos-cut');
|
|
1451
|
+
assert.deepEqual(hubCrop('bead').crop, landingCrop('o3'));
|
|
1452
|
+
assert.equal(hubCrop('bead').src, '--plate-set-cut');
|
|
1453
|
+
// the plates by root-absolute token in the hub's own sheet (a url() in a custom property resolves against the document)
|
|
1454
|
+
assert.match(COSMOS, /--plate-bead-cut:url\(\/assets\/world\/r2-chrome-blob-one-cut\.webp\);/);
|
|
1455
|
+
assert.match(COSMOS, /--plate-bongos-cut:url\(\/assets\/world\/r2-bongos-chrome-a-cut\.webp\);/);
|
|
1456
|
+
});
|
|
1457
|
+
|
|
1458
|
+
test('the style is a blend on a masked colour layer; the accent never paints the box; the ring and the spotlight follow the silhouette', () => {
|
|
1459
|
+
assert.match(COSMOS, /\.worldOrb \.disc::before\{content:"";position:absolute;inset:0;\s*background-image:linear-gradient\(var\(--tpl-ink\),var\(--tpl-ink\)\),var\(--tpl-src\);/);
|
|
1460
|
+
assert.match(COSMOS, /background-blend-mode:var\(--tpl-blend\),normal;\s*-webkit-mask-image:var\(--tpl-src\);mask-image:var\(--tpl-src\);/, 'the mask is the plate itself');
|
|
1461
|
+
assert.match(COSMOS, /\.worldOrb\[data-style="tint"\]\{--tpl-ink:var\(--acc\);--tpl-blend:color;\}/);
|
|
1462
|
+
assert.match(COSMOS, /\.worldOrb\[data-style="wash"\]\{--tpl-ink:var\(--acc\);--tpl-blend:overlay;\}/);
|
|
1463
|
+
assert.match(COSMOS, /\.worldOrb\[data-style="chrome"\]\{--tpl-ink:transparent;--tpl-blend:normal;\}/);
|
|
1464
|
+
assert.match(COSMOS, /\.worldOrb \.disc\{--dd:calc\(var\(--d,96px\) \* var\(--orb-scale,1\)\);width:var\(--dd\);aspect-ratio:var\(--tpl-ar,1\);position:relative;isolation:isolate;\s*filter:var\(--orb-shadow\);/,
|
|
1465
|
+
"the box is --d (scaled) by the object's own aspect");
|
|
1466
|
+
assert.doesNotMatch(COSMOS, /var\(--d,96px\) \* var\(--tpl-/, 'every crop calc reads the scaled width');
|
|
1467
|
+
assert.match(COSMOS, /@media \(max-width:900px\)\{\s*\.field\{--orb-scale:\.8;\}/, 'narrow: the object is four fifths of its box; the physics keeps the box');
|
|
1468
|
+
assert.doesNotMatch(COSMOS, /\.worldOrb \.disc\{[^}]*radial-gradient/, 'no painted ball');
|
|
1469
|
+
assert.doesNotMatch(COSMOS, /\.worldOrb\[data-rel="owner"\] \.disc\{box-shadow/, 'a mask clips a box-shadow away');
|
|
1470
|
+
assert.match(COSMOS, /\.worldOrb\[data-rel="owner"\] \.disc::after\{border:1\.5px solid var\(--accent\);\}/, 'the ring is a circle behind the object');
|
|
1471
|
+
assert.match(COSMOS, /\.worldOrb\.match \.disc\{filter:var\(--orb-shadow\) drop-shadow\(/, 'the spotlight composes after the contact shadow');
|
|
1472
|
+
assert.doesNotMatch(COSMOS, /\.worldOrb\.match \.disc\{outline/);
|
|
1473
|
+
assert.match(COSMOS, /--orb-shadow:drop-shadow\(0 12px 14px color-mix\(in srgb,var\(--ink\) 22%,transparent\)\);/);
|
|
1474
|
+
assert.equal((COSMOS.match(/--orb-shadow:opacity\(1\);/g) || []).length, 2, 'dark states the no-op filter in both twins');
|
|
1475
|
+
assert.match(COSMOS, /\.worldOrb:hover \.disc\{translate:0 -6px;\}/, "the landing's hover rise");
|
|
1476
|
+
assert.match(SCRIPT, /var discOff = disc \? \(disc\.offsetTop \+ disc\.offsetHeight \/ 2\) - el\.offsetHeight \/ 2 : 0;/,
|
|
1477
|
+
"the physics measures the disc's own height: a planet's box is not square");
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
test("the field is the void: no drawn grid, no stars, no twinkle; the labels, the badge and the hint in the world's type", () => {
|
|
1481
|
+
assert.doesNotMatch(COSMOS, /--grid-ink/);
|
|
1482
|
+
assert.doesNotMatch(COSMOS, /\.field \.ground::after/);
|
|
1483
|
+
assert.doesNotMatch(COSMOS, /--star-display/);
|
|
1484
|
+
assert.doesNotMatch(COSMOS, /@keyframes twinkle/);
|
|
1485
|
+
assert.doesNotMatch(HUB, /class="star"/);
|
|
1486
|
+
assert.doesNotMatch(HUB, /id="mapStars"/);
|
|
1487
|
+
assert.doesNotMatch(SCRIPT, /function stars\(\)/);
|
|
1488
|
+
assert.match(HUB, /<div class="sky" aria-hidden="true"><\/div>/, 'the ground layer stays, empty');
|
|
1489
|
+
assert.match(COSMOS, /\.worldOrb \.nm\{font-family:var\(--font-body\);font-weight:500;font-size:15px;letter-spacing:-\.015em;color:var\(--ink\);\s*text-shadow:var\(--type-halo\);/,
|
|
1490
|
+
"the name in the ledger's title voice, the world's black halo");
|
|
1491
|
+
assert.match(COSMOS, /\.worldOrb \.sub\{font-family:var\(--label-face\);/);
|
|
1492
|
+
assert.match(COSMOS, /\.worldOrb \.badge\{[^}]*background:var\(--bg\);color:var\(--ink-faint\);border:1px solid var\(--rule\);\}/, 'a hairline pill');
|
|
1493
|
+
assert.doesNotMatch(COSMOS, /\.worldOrb \.badge\{[^}]*--gold-soft/);
|
|
1494
|
+
assert.doesNotMatch(COSMOS, /\.worldOrb\[data-rel="owner"\] \.badge\{background:var\(--accent\)/, 'a fact is not an accent fill (the One Warm Thing Rule)');
|
|
1495
|
+
assert.match(COSMOS, /\.hintbar\{[^}]*font-family:var\(--label-face\);/);
|
|
1496
|
+
assert.doesNotMatch(COSMOS, /\.hintbar\{[^}]*border-radius:999px/, 'no pill');
|
|
1497
|
+
assert.doesNotMatch(COSMOS, /\.hintbar kbd\{[^}]*--accent/, 'no accent on a hint');
|
|
1498
|
+
assert.doesNotMatch(COSMOS, /\.field \.hoverCard\{[^}]*box-shadow/, 'no shadow vocabulary for UI');
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
test('the kit proves the field path: the fixture carries a curated template and the states file reads it back', () => {
|
|
1502
|
+
const fixture = JSON.parse(fs.readFileSync(path.join(ROOT, 'modules', 'ui-design', 'kit', 'fixtures', 'featured.json'), 'utf8'));
|
|
1503
|
+
const otb = fixture.projects.find((p) => p.origin === 'https://amazonprimea.com');
|
|
1504
|
+
assert.equal(otb.template, 'bead-wash');
|
|
1505
|
+
assert.ok(fixture.projects.every((p) => 'template' in p), 'the wire never drops the key');
|
|
1506
|
+
const states = JSON.parse(fs.readFileSync(path.join(ROOT, 'public-landing', 'projects.states.json'), 'utf8'));
|
|
1507
|
+
const attrs = states.states['in-map'].expect.attr.map((a) => a.join(' '));
|
|
1508
|
+
assert.ok(attrs.includes('#selfOrb data-template pair-chrome'));
|
|
1509
|
+
assert.ok(attrs.some((a) => a.includes('amazonprimea.com') && a.endsWith('data-template bead-wash')), 'the curated template reaches the orb');
|
|
1510
|
+
});
|