@bongos/core 1.25.29 → 1.25.30

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.
@@ -265,6 +265,10 @@
265
265
  return false;
266
266
  }
267
267
  function homeHref(mode) {
268
+ // In game mode the street is not a place (shell-game.js would send it back
269
+ // home), so the patron's home there is Home itself (task 1005307).
270
+ var g = window.OTBShellGame;
271
+ if (g && typeof g.on === 'function' && g.on() === true) return '/';
268
272
  var street = mode === 'patron' && window.GovCity && window.GovCity.STREET_HREF;
269
273
  return street || '/';
270
274
  }
@@ -355,9 +355,17 @@
355
355
  }
356
356
 
357
357
  // ---- the governor's Home is the street; tests swap the hop for a recorder ----
358
+ // Classic view only (task 1005307): in game mode the street is not a place, so
359
+ // shell-game.js sends it straight back here — hopping from Home made an endless
360
+ // reload loop. The game's patron stays on the city and walks on from the visor.
361
+ // shell-game.js is the one answer: a page without it is never in game mode.
362
+ function gameOn() {
363
+ const g = window.OTBShellGame;
364
+ return !!(g && typeof g.on === 'function' && g.on() === true);
365
+ }
358
366
  function streetHop() {
359
367
  const href = window.GovCity && window.GovCity.STREET_HREF;
360
- if (document.documentElement.dataset.craft !== 'patron' || !href) return false;
368
+ if (document.documentElement.dataset.craft !== 'patron' || !href || gameOn()) return false;
361
369
  (window.OTBHomeStreetHop || ((h) => window.location.assign(h)))(href);
362
370
  return true;
363
371
  }
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.25.29",
3
+ "version": "1.25.30",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.25.29",
9
+ "version": "1.25.30",
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.25.29",
3
+ "version": "1.25.30",
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",
@@ -11949,5 +11949,11 @@
11949
11949
  "id": "1005299",
11950
11950
  "text": "The city's sky clock no longer says \"hour 24\" in the last three minutes before midnight, and its check no longer fails at random late at night."
11951
11951
  }
11952
+ ],
11953
+ "1.25.30": [
11954
+ {
11955
+ "id": "1005307",
11956
+ "text": "Choosing Patron in game mode no longer reloads the hall endlessly; it settles on the city."
11957
+ }
11952
11958
  ]
11953
11959
  }
package/src/module-api.js CHANGED
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
81
81
  // MAJOR (see allowBoxScope below): passes the request through untouched.
82
82
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
83
83
 
84
- const CORE_VERSION = '1.25.29'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
84
+ const CORE_VERSION = '1.25.30'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
85
85
 
86
86
  // A namespaced logger so a module's log lines are attributable + consistent.
87
87
  // Usage: const log = api.logger('discord'); log.info('mounted');
@@ -31,7 +31,7 @@ function el() {
31
31
  }
32
32
 
33
33
  // One page: `page` is body[data-page], `active` the nav item the shell marked.
34
- function boot({ page, active = null, govCity = false }) {
34
+ function boot({ page, active = null, govCity = false, game = false }) {
35
35
  const hops = [];
36
36
  const body = el();
37
37
  body.setAttribute('data-page', page);
@@ -55,6 +55,7 @@ function boot({ page, active = null, govCity = false }) {
55
55
  location: { search: '' },
56
56
  };
57
57
  if (govCity) window.GovCity = { STREET_HREF: '/builders/street' };
58
+ if (game) window.OTBShellGame = { on: () => true };
58
59
  window.window = window;
59
60
  vm.runInNewContext(CRAFT, { window, document, CustomEvent: function () {}, Promise, console });
60
61
  return { CM: window.OTBCraftMode, hops };
@@ -83,6 +84,16 @@ test('choosing Patron on another craft\'s page lands on the street', () => {
83
84
  assert.equal(boot({ page: 'studio' }).CM.homeHref('patron'), '/');
84
85
  });
85
86
 
87
+ // task 1005307: in game mode the street is not a place (shell-game.js sends it back
88
+ // Home, and Home sent a patron to the street again: an endless reload loop), so a
89
+ // stranded page goes straight Home.
90
+ test('in game mode, choosing Patron on another craft\'s page goes Home, never the street', () => {
91
+ const { CM, hops } = boot({ page: 'studio', active: 'studio', govCity: true, game: true });
92
+ assert.equal(CM.homeHref('patron'), '/');
93
+ CM.choose('patron');
94
+ assert.deepEqual(hops, ['/']);
95
+ });
96
+
86
97
  test('a page the chosen craft also shows stays put', () => {
87
98
  // goals is in Artisan and Artist alike
88
99
  const { CM, hops } = boot({ page: 'goals', active: 'goals' });
@@ -48,12 +48,13 @@ const stubs = (status, craft) => `
48
48
  window.OTBCraftMode = { iconHtml: (k, cls) => '<svg class="' + cls + '" data-icon="' + k + '"></svg>', choose: (m) => window.__chosen.push(m), current: () => window.__craft };
49
49
  window.OTB = { contributeHallWidget(w) { window.__widget = w; } };`;
50
50
 
51
- async function boot({ status = 200, width = 1280, still = true, craft = null } = {}) {
51
+ async function boot({ status = 200, width = 1280, still = true, craft = null, game = false } = {}) {
52
52
  const ctx = await browser.newContext({ viewport: { width, height: 900 }, reducedMotion: still ? 'reduce' : 'no-preference' });
53
53
  const page = await ctx.newPage();
54
54
  await page.clock.install();
55
55
  await page.setContent(`<!doctype html><html><head><style>${CSS}</style></head><body><section id="home-city" class="scroll home-city" hidden></section></body></html>`);
56
56
  await page.addScriptTag({ content: stubs(status, craft) });
57
+ if (game) await page.addScriptTag({ content: 'window.OTBShellGame = { on: () => true };' });
57
58
  await page.addScriptTag({ content: GOV });
58
59
  await page.addScriptTag({ content: DRAW });
59
60
  await page.addScriptTag({ content: WIDGET });
@@ -184,6 +185,16 @@ test('the governor\'s Home is the street: Patron on Home goes there, opened or c
184
185
  await ctx.close();
185
186
  });
186
187
 
188
+ // task 1005307: in game mode the street is not a place — shell-game.js sends it back to
189
+ // Home — so hopping there from Home was an endless reload loop. The game's patron stays.
190
+ test('in game mode the patron stays on Home: no hop to the street, opened or chosen', { skip }, async () => {
191
+ const { ctx, page } = await boot({ craft: 'patron', game: true });
192
+ await page.waitForFunction(() => document.querySelectorAll('.hc-rung').length > 0);
193
+ const flipped = await page.evaluate(() => { document.dispatchEvent(new CustomEvent('otb:craft', { detail: { mode: 'patron' } })); return window.__hops.slice(); });
194
+ await ctx.close();
195
+ assert.deepEqual(flipped, [], 'Home with Patron in game mode builds the city and never leaves for the street');
196
+ });
197
+
187
198
  test('a refused read leaves the section hidden: never an empty plate', { skip }, async () => {
188
199
  const { ctx, page } = await boot({ status: 401 });
189
200
  await page.evaluate(() => new Promise((r) => setTimeout(r, 0)));