@bongos/core 1.21.181 → 1.22.1

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.
Files changed (138) hide show
  1. package/.bongos-core.json +159 -144
  2. package/docs/copy-inventory.md +165 -165
  3. package/docs/copy-registry.json +184 -184
  4. package/docs/file-map.md +1 -1
  5. package/docs/module-api-changelog.md +12 -0
  6. package/docs/page-inventory.json +35 -26
  7. package/docs/page-readings.json +389 -388
  8. package/modules/hall-ui/public/approval-queue.js +3 -3
  9. package/modules/hall-ui/public/atlas.html +2 -1
  10. package/modules/hall-ui/public/atlas.js +1 -1
  11. package/modules/hall-ui/public/blockers.html +3 -4
  12. package/modules/hall-ui/public/board-room.html +2 -2
  13. package/modules/hall-ui/public/brand-holes.js +3 -3
  14. package/modules/hall-ui/public/city.html +2 -1
  15. package/modules/hall-ui/public/collab.html +2 -2
  16. package/modules/hall-ui/public/commands.html +2 -2
  17. package/modules/hall-ui/public/commands.js +1 -2
  18. package/modules/hall-ui/public/copy-desk.html +2 -2
  19. package/modules/hall-ui/public/core-move-notice.js +1 -5
  20. package/modules/hall-ui/public/craft-mode.js +1 -5
  21. package/modules/hall-ui/public/criterion-uat-ui.js +3 -3
  22. package/modules/hall-ui/public/deploy.html +2 -2
  23. package/modules/hall-ui/public/diagrams.html +2 -1
  24. package/modules/hall-ui/public/dom-utils.js +15 -6
  25. package/modules/hall-ui/public/drachmae.html +2 -1
  26. package/modules/hall-ui/public/fleet-costs.js +3 -2
  27. package/modules/hall-ui/public/fleet.html +2 -2
  28. package/modules/hall-ui/public/founding-mark.js +1 -6
  29. package/modules/hall-ui/public/gate.html +2 -2
  30. package/modules/hall-ui/public/goal-map.html +2 -1
  31. package/modules/hall-ui/public/goals.html +2 -2
  32. package/modules/hall-ui/public/government.html +2 -2
  33. package/modules/hall-ui/public/hall-ask.js +3 -3
  34. package/modules/hall-ui/public/hall-kit.js +3 -5
  35. package/modules/hall-ui/public/hall-visual.js +3 -5
  36. package/modules/hall-ui/public/idea.html +2 -2
  37. package/modules/hall-ui/public/ideas.html +2 -2
  38. package/modules/hall-ui/public/index.html +2 -2
  39. package/modules/hall-ui/public/lightbulb.js +1 -5
  40. package/modules/hall-ui/public/md-reader.js +1 -8
  41. package/modules/hall-ui/public/modules.html +2 -2
  42. package/modules/hall-ui/public/my-agents.html +2 -2
  43. package/modules/hall-ui/public/my-agents.js +3 -1
  44. package/modules/hall-ui/public/page-notes.js +1 -5
  45. package/modules/hall-ui/public/paintbrush.js +1 -5
  46. package/modules/hall-ui/public/palette.js +1 -5
  47. package/modules/hall-ui/public/primer.html +2 -1
  48. package/modules/hall-ui/public/primer.js +1 -1
  49. package/modules/hall-ui/public/profile-crafts.js +3 -5
  50. package/modules/hall-ui/public/profile.html +2 -3
  51. package/modules/hall-ui/public/project-settings.html +2 -2
  52. package/modules/hall-ui/public/ranks.html +2 -3
  53. package/modules/hall-ui/public/roadmap.html +2 -2
  54. package/modules/hall-ui/public/roster.html +3 -4
  55. package/modules/hall-ui/public/sessions.html +2 -3
  56. package/modules/hall-ui/public/settings.html +2 -3
  57. package/modules/hall-ui/public/shell.js +1 -5
  58. package/modules/hall-ui/public/sky-data.js +1 -1
  59. package/modules/hall-ui/public/street.html +2 -1
  60. package/modules/hall-ui/public/studio.html +2 -2
  61. package/modules/hall-ui/public/task-detail.js +1 -6
  62. package/modules/hall-ui/public/task-lib.js +3 -8
  63. package/modules/hall-ui/public/task.html +2 -3
  64. package/modules/hall-ui/public/thinking.html +2 -1
  65. package/modules/hall-ui/public/tweak-editor.html +2 -2
  66. package/modules/hall-ui/public/vote-control.js +1 -1
  67. package/modules/hall-ui/public/watch.html +2 -3
  68. package/modules/hall-ui/public/welcome-boot.js +3 -5
  69. package/modules/hall-ui/public/work.html +2 -3
  70. package/modules/lifecycle/ship-card.js +1 -4
  71. package/modules/npm-release/preview/proxy.js +2 -3
  72. package/modules/npm-release/public/task-widget.js +1 -1
  73. package/modules/platform-identity/routes/sso.js +1 -3
  74. package/modules/provisioning/routes/callback-page.js +2 -6
  75. package/modules/public-landing/public/projects.html +3 -5
  76. package/modules/raise/raise.js +1 -1
  77. package/package-lock.json +2 -2
  78. package/package.json +1 -1
  79. package/release-notes.json +26 -12
  80. package/scripts/bongos/escape-helper-guard.js +147 -0
  81. package/scripts/bongos/fitness.js +3 -2
  82. package/scripts/bongos/key-card.js +1 -4
  83. package/scripts/bongos/review-sheet.js +1 -3
  84. package/scripts/bongos/start.js +1 -1
  85. package/scripts/bongos/status.js +1 -1
  86. package/scripts/gds/escape-helper-guard.js +6 -0
  87. package/scripts/status-mirror-sync.js +2 -3
  88. package/src/bongos/module-scope-map.js +1 -0
  89. package/src/bongos/platform-visibility-gate.js +2 -3
  90. package/src/bongos/prelaunch-gate.js +2 -3
  91. package/src/bongos/routes/auth.js +1 -5
  92. package/src/bongos/serve-internal.js +2 -6
  93. package/src/html-escape.js +26 -0
  94. package/src/module-api.js +9 -1
  95. package/tests/blockers_kit_pager.mjs +2 -2
  96. package/tests/blockers_page_ui.mjs +2 -2
  97. package/tests/core_move_notice.mjs +3 -1
  98. package/tests/core_update_banner.mjs +2 -0
  99. package/tests/core_upgrade_owner_door.mjs +2 -0
  100. package/tests/craft_mode.mjs +2 -0
  101. package/tests/craft_mode_nav.mjs +3 -0
  102. package/tests/craft_review_mode.mjs +3 -2
  103. package/tests/criterion_ref_status.mjs +7 -2
  104. package/tests/curve_kind_overlay.mjs +2 -1
  105. package/tests/founding_mode.mjs +10 -2
  106. package/tests/github_install_nudge.mjs +4 -1
  107. package/tests/goal_map_page.mjs +2 -2
  108. package/tests/government_protected_surfaces.mjs +15 -96
  109. package/tests/hall_approval_queue.mjs +2 -2
  110. package/tests/hall_city.mjs +1 -1
  111. package/tests/hall_fullscreen.mjs +2 -0
  112. package/tests/hall_gov_real_site.mjs +2 -0
  113. package/tests/hall_kit_charts.mjs +2 -1
  114. package/tests/hall_my_agents.mjs +3 -2
  115. package/tests/hall_nav.mjs +2 -0
  116. package/tests/hall_palette.mjs +2 -2
  117. package/tests/hall_reading_world.mjs +3 -1
  118. package/tests/hall_reference_world.mjs +2 -1
  119. package/tests/hall_signout.mjs +2 -0
  120. package/tests/hall_street.mjs +1 -1
  121. package/tests/hall_studio_world.mjs +3 -1
  122. package/tests/hall_tweak_editor.mjs +2 -1
  123. package/tests/helpers.mjs +17 -2
  124. package/tests/html_escape.mjs +149 -0
  125. package/tests/lightbulb.mjs +2 -0
  126. package/tests/module_api.mjs +1 -0
  127. package/tests/module_store_howto.mjs +2 -1
  128. package/tests/nav_permission_atoms.mjs +1 -1
  129. package/tests/npm_release_where.mjs +2 -1
  130. package/tests/page_notes_panel.mjs +4 -3
  131. package/tests/paintbrush.mjs +2 -1
  132. package/tests/profile_connections_ui.mjs +2 -1
  133. package/tests/profile_role_headline.mjs +2 -2
  134. package/tests/profile_role_tabs.mjs +2 -2
  135. package/tests/projects_hub_repo_private.mjs +4 -0
  136. package/tests/terms_acceptance.mjs +1 -0
  137. package/tests/welcome_boot_browser.mjs +1 -1
  138. package/modules/government/protected-surfaces.js +0 -88
@@ -43,11 +43,7 @@
43
43
 
44
44
  var bar = null, st = null;
45
45
 
46
- function esc(s) {
47
- return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
48
- return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
49
- });
50
- }
46
+ function esc(s) { return window.OTB.escapeHtml(s); }
51
47
  function tidy(s) { return String(s == null ? '' : s).replace(/\s+/g, ' ').trim(); }
52
48
  function plural(n, one, many) { return n + ' ' + (n === 1 ? one : many); }
53
49
  function clip(s, n) { s = tidy(s); return s.length > n ? s.slice(0, n - 1) + '…' : s; }
@@ -105,11 +105,7 @@
105
105
  var searchAbort = null; // the in-flight request's AbortController, if the runtime has one
106
106
  var searchCache = {}; // query (trimmed+lowercased) -> raw hits[], this OPEN only
107
107
 
108
- function esc(s) {
109
- return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
110
- return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
111
- });
112
- }
108
+ function esc(s) { return window.OTB.escapeHtml(s); }
113
109
  function visible(id) {
114
110
  var el = document.getElementById(id);
115
111
  return !!(el && !el.hidden);
@@ -110,9 +110,10 @@
110
110
 
111
111
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
112
112
 
113
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
113
114
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
114
115
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
115
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
116
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
116
117
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
117
118
  <script src="/builders/palette.js" defer></script>
118
119
  <!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
@@ -55,7 +55,7 @@
55
55
  function $(sel) { return document.querySelector(sel); }
56
56
 
57
57
  const MD = window.OTBMdReader;
58
- const escapeHtml = (MD && MD.escapeHtml) || ((s) => String(s));
58
+ const escapeHtml = (s) => window.OTB.escapeHtml(s); // task 1004876: the old fallback escaped nothing
59
59
 
60
60
  // The shared resolver, plus the one rewrite that is the primer's alone: the
61
61
  // economy doc has an in-hall reader page (task 1472), so a link to it stays in
@@ -33,11 +33,9 @@
33
33
  (function () {
34
34
  'use strict';
35
35
 
36
- function esc(s) {
37
- return String(s == null ? '' : s).replace(/[&<>"']/g, (c) => ({
38
- '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
39
- }[c]));
40
- }
36
+ // The shared escaper: window.OTB in the page; under Node (this file is also require()d as a
37
+ // CommonJS lib — see its module.exports and its tests) dom-utils.js's own export (task 1004876).
38
+ function esc(s) { return (typeof window !== 'undefined' ? window.OTB : require('./dom-utils.js')).escapeHtml(s); }
41
39
  function objects() {
42
40
  return (typeof window !== 'undefined' && window.OTBIdeaObjects) || null;
43
41
  }
@@ -205,13 +205,12 @@
205
205
 
206
206
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
207
207
  chrome exists before the page script runs. -->
208
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
208
209
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
209
210
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
210
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
211
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
211
212
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
212
213
  <script src="/builders/palette.js" defer></script>
213
- <!-- shared frontend toolkit (window.OTB) — loaded before profile.js, which pulls from it. -->
214
- <script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
215
214
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
216
215
  <script src="/builders/vote-control.js"></script>
217
216
  <script src="/builders/brand-apply.js?v=2026-06-27-r75"></script>
@@ -69,12 +69,12 @@
69
69
 
70
70
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
71
71
 
72
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
72
73
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
73
74
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
74
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
75
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
75
76
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
76
77
  <script src="/builders/palette.js" defer></script>
77
- <script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
78
78
  <script src="/builders/hall-kit.js"></script>
79
79
  <script src="/builders/project-settings.js?v=2026-10-08-ship-mode"></script>
80
80
  </body>
@@ -71,13 +71,12 @@
71
71
 
72
72
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
73
73
  chrome exists before the page script runs. -->
74
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
74
75
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
75
76
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
76
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
77
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
77
78
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
78
79
  <script src="/builders/palette.js" defer></script>
79
- <!-- shared frontend toolkit (window.OTB) — loaded before ranks.js, which pulls from it. -->
80
- <script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
81
80
  <script src="/builders/founding-mark.js?v=2026-09-30-founding"></script>
82
81
  <script src="/builders/ranks.js?v=2026-09-30-founding"></script>
83
82
  </body>
@@ -41,12 +41,12 @@
41
41
 
42
42
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
43
43
 
44
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
44
45
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
45
46
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
46
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
47
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
47
48
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
48
49
  <script src="/builders/palette.js" defer></script>
49
- <script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
50
50
  <!-- The chart kit and the timeline's series builders (task 1003447). Both MUST
51
51
  precede roadmap.js: the page reads window.OTBKit / window.OTBRoadmapTimeline
52
52
  at render time, so loading either after would be a blank section in
@@ -53,14 +53,13 @@
53
53
 
54
54
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
55
55
  chrome exists before the page script runs. -->
56
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
56
57
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
57
58
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
58
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
59
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
59
60
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
60
61
  <script src="/builders/palette.js" defer></script>
61
- <!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
62
- branding pack's currency label, loaded BEFORE roster.js. -->
63
- <script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
62
+ <!-- the branding pack's currency label, loaded BEFORE roster.js. -->
64
63
  <script src="/builders/brand-apply.js"></script>
65
64
  <script src="/builders/hall-kit.js"></script>
66
65
  <script src="/builders/founding-mark.js?v=2026-09-30-founding"></script>
@@ -109,13 +109,12 @@
109
109
 
110
110
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
111
111
  chrome exists before the page script runs. -->
112
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
112
113
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
113
114
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
114
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
115
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
115
116
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
116
117
  <script src="/builders/palette.js" defer></script>
117
- <!-- shared frontend toolkit (window.OTB) — loaded BEFORE sessions.js so it can pull from it. -->
118
- <script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
119
118
  <script src="/builders/task-detail.js?v=2026-07-03-taskdetail"></script>
120
119
  <!-- The shared component kit — the pager contract this page now uses instead of
121
120
  its own (task 1003279-sibling follow-up of 1003281). Must load BEFORE
@@ -470,13 +470,12 @@
470
470
 
471
471
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
472
472
  chrome exists before the page scripts run. -->
473
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
473
474
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
474
475
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
475
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
476
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
476
477
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
477
478
  <script src="/builders/palette.js" defer></script>
478
- <!-- shared frontend toolkit (window.OTB) — loaded before consuming IIFEs. -->
479
- <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
480
479
  <!-- shared component kit (window.OTBKit) — section rail (task 2166). -->
481
480
  <script src="/builders/hall-kit.js"></script>
482
481
  <!-- shared record overlay (#/idea|blocker/N) — every hall page carries it; this
@@ -64,11 +64,7 @@
64
64
  // builders.js owns these hashes (deep-link overlay) — never route them.
65
65
  var DEEPLINK_RE = /^#\/(task|idea|blocker)\/(\d+)$/;
66
66
 
67
- function esc(s) {
68
- return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
69
- return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
70
- });
71
- }
67
+ function esc(s) { return window.OTB.escapeHtml(s); }
72
68
  function moduleOn(name) {
73
69
  var m = window.__MODULES__;
74
70
  if (!m || !m.enabled || !(name in m.enabled)) return true;
@@ -87,7 +87,7 @@
87
87
  }
88
88
  // esc — the prototype builds HTML strings, and every title, criterion and
89
89
  // goal name passes through here first, so a title never becomes markup.
90
- function esc(s) { return String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c])); }
90
+ function esc(s) { return window.OTB.escapeHtml(s); }
91
91
  function fmtDate(iso) { if (!iso) return '—'; const d = new Date(iso); return d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short' }).toLowerCase(); }
92
92
  function fmtDay(t) { return new Date(t).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }).toLowerCase(); }
93
93
  function ageDays(iso) { return iso ? (NOW - new Date(iso).getTime()) / 86400000 : 0; }
@@ -228,9 +228,10 @@
228
228
  </div>
229
229
  </div>
230
230
 
231
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
231
232
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
232
233
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
233
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
234
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
234
235
  <script src="/builders/palette.js" defer></script>
235
236
  <script src="/builders/gov-living.js?v=2026-10-09-positions1"></script>
236
237
  <script src="/builders/gov-feeds.js?v=2026-10-09-living3"></script>
@@ -188,11 +188,11 @@
188
188
 
189
189
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
190
190
 
191
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
191
192
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
192
193
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
193
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
194
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
194
195
  <script src="/builders/palette.js" defer></script>
195
- <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
196
196
  <!-- The kit, for its ONE renderer: the full-size ship-time visual figure
197
197
  (task 1003829), which the older reviews still show. -->
198
198
  <script src="/builders/hall-visual.js?v=2026-09-16-visual1"></script>
@@ -75,12 +75,7 @@
75
75
  // dom-utils.js already publishes escapeHtml on window.OTB (this file uses
76
76
  // OTB.toast the same way) — a local copy would be its 9th byte-identical twin.
77
77
  // The fallback keeps the file's self-contained contract if OTB hasn't loaded.
78
- function esc(s) {
79
- if (window.OTB && window.OTB.escapeHtml) return window.OTB.escapeHtml(s);
80
- return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
81
- return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
82
- });
83
- }
78
+ function esc(s) { return window.OTB.escapeHtml(s); }
84
79
 
85
80
  async function fetchRef(kind, id) {
86
81
  if (kind === 'blocker') {
@@ -23,14 +23,9 @@
23
23
  (() => {
24
24
  'use strict';
25
25
 
26
- // Local HTML-escape — deliberately NOT window.OTB.escapeHtml, so this file
27
- // keeps hall-kit's dependency-free property (same reasoning, same duplication
28
- // of ~4 lines being cheaper than a load-order constraint).
29
- function esc(s) {
30
- return String(s ?? '').replace(/[&<>"']/g, (c) => ({
31
- '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
32
- }[c]));
33
- }
26
+ // The shared escaper: window.OTB in the page; under Node (this file is also require()d as a
27
+ // CommonJS lib — see its module.exports and its tests) dom-utils.js's own export (task 1004876).
28
+ function esc(s) { return (typeof window !== 'undefined' ? window.OTB : require('./dom-utils.js')).escapeHtml(s); }
34
29
 
35
30
  // Sort key for a "P3"-style priority string. Unparseable/absent sorts last.
36
31
  function priorityRank(p) {
@@ -55,13 +55,12 @@
55
55
 
56
56
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
57
57
  time so the chrome exists before the deferred page scripts execute. -->
58
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
58
59
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
59
60
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
60
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
61
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
61
62
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
62
63
  <script src="/builders/palette.js" defer></script>
63
- <!-- shared frontend toolkit (window.OTB) — loaded FIRST so task.js can pull from it. -->
64
- <script src="/builders/dom-utils.js?v=2026-06-14-domutils" defer></script>
65
64
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N deep-link overlay
66
65
  (and the #/task/N → /task/N redirect, task 751); load it here too so those
67
66
  links still resolve from this page. -->
@@ -138,9 +138,10 @@
138
138
  </div>
139
139
  </div>
140
140
 
141
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
141
142
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
142
143
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
143
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
144
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
144
145
  <script src="/builders/palette.js" defer></script>
145
146
 
146
147
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
@@ -142,11 +142,11 @@
142
142
 
143
143
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
144
144
 
145
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
145
146
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
146
147
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
147
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
148
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
148
149
  <script src="/builders/palette.js" defer></script>
149
- <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
150
150
  <script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
151
151
  <script src="/builders/tweak-editor-lib.js?v=2026-09-29-tw27"></script>
152
152
  <script src="/builders/tweak-editor.js?v=2026-09-29-tw27-aqfix"></script>
@@ -30,7 +30,7 @@
30
30
  insufficient_rank: 'Voting opens at Metic rank.',
31
31
  };
32
32
 
33
- const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
33
+ const esc = (s) => window.OTB.escapeHtml(s);
34
34
  const netText = (n) => { const v = Number(n) || 0; return v > 0 ? `+${v}` : `${v}`; };
35
35
  const validId = (id) => { const n = Number(id); return Number.isInteger(n) && n > 0 ? n : null; };
36
36
 
@@ -163,13 +163,12 @@
163
163
 
164
164
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
165
165
  chrome exists before the page script runs. -->
166
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
166
167
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
167
168
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
168
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
169
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
169
170
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
170
171
  <script src="/builders/palette.js" defer></script>
171
- <!-- shared frontend toolkit (window.OTB) — loaded before watch.js, which pulls from it. -->
172
- <script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
173
172
  <!-- shared component kit (window.OTBKit) — tabs, ledger, pager (task 2166). -->
174
173
  <script src="/builders/hall-kit.js"></script>
175
174
  <script src="/builders/task-detail.js?v=2026-07-03-taskdetail"></script>
@@ -139,11 +139,9 @@
139
139
  }
140
140
 
141
141
  // ---------- the overlay ----------
142
- function esc(s) {
143
- return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
144
- return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
145
- });
146
- }
142
+ // The shared escaper: window.OTB in the page; under Node (this file is also require()d as a
143
+ // CommonJS lib — see its module.exports and its tests) dom-utils.js's own export (task 1004876).
144
+ function esc(s) { return (typeof window !== 'undefined' ? window.OTB : require('./dom-utils.js')).escapeHtml(s); }
147
145
  function craft() { return window.OTBCraftMode || null; }
148
146
  function reducedMotion() {
149
147
  return !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
@@ -171,13 +171,12 @@
171
171
 
172
172
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
173
173
  time so the chrome exists before the deferred page scripts execute. -->
174
+ <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
174
175
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
175
176
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
176
- <script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
177
+ <script src="/builders/shell.js?v=2026-10-25-escape"></script>
177
178
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
178
179
  <script src="/builders/palette.js" defer></script>
179
- <!-- shared frontend toolkit (window.OTB) — loaded FIRST so work.js can pull from it. -->
180
- <script src="/builders/dom-utils.js?v=2026-06-14-domutils" defer></script>
181
180
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
182
181
  <script src="/builders/vote-control.js"></script>
183
182
  <!-- shared component kit (window.OTBKit) — tabs, pager, filter bar (task 2166). -->
@@ -587,10 +587,7 @@ function renderMonospaceCard(model) {
587
587
  }
588
588
 
589
589
  // ---- HTML renderer (the in-feed branded card) ------------------------------
590
- function esc(s) {
591
- return String(s == null ? '' : s).replace(/[&<>"']/g, (c) =>
592
- ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
593
- }
590
+ function esc(s) { return require('../../src/module-api').escapeHtml(s); } // the one server escaper (task 1004876)
594
591
  function factHtml(f) {
595
592
  let inner = esc(f.value);
596
593
  if (f.mono) inner = `<span class="cc-mono">${inner}</span>`;
@@ -29,9 +29,8 @@ function withoutPreviewCookie(cookieHeader) {
29
29
  .join('; ');
30
30
  }
31
31
 
32
- function escapeHtml(s) {
33
- return String(s).replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
34
- }
32
+ // The one server escaper, through the doorway (task 1004876); this copy used to skip '.
33
+ const { escapeHtml } = require('../../../src/module-api');
35
34
 
36
35
  /** The banner markup: which version this is, which one the hall runs, and the way out. */
37
36
  function bannerHtml({ viewing, running, exitHref }) {
@@ -16,7 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const API = '/api/bongos';
19
- const esc = (s) => String(s == null ? '' : s).replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
19
+ const esc = (s) => window.OTB.escapeHtml(s); // the hall's shared escaper (dom-utils.js, task 1004876)
20
20
  const client = window.BongosClient && window.BongosClient.createClient({ baseUrl: '', credentials: 'same-origin', throwOnError: false });
21
21
  const DONE = ['completed', 'confirmed', 'shipped'];
22
22
 
@@ -118,9 +118,7 @@ function reportAsOf(req) {
118
118
  // The /sso/authorize failures are pre-redirect (bad client / bad redirect_uri), so
119
119
  // we must render our OWN page rather than bounce to an unvalidated URI.
120
120
  function errorPage(detail) {
121
- const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => (
122
- { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]
123
- ));
121
+ const esc = api.escapeHtml; // the one server escaper (task 1004876)
124
122
  return `<!doctype html><html lang="en"><head><meta charset="utf-8">`
125
123
  + `<meta name="viewport" content="width=device-width, initial-scale=1">`
126
124
  + `<title>Sign-in handoff failed</title></head><body>`
@@ -20,12 +20,8 @@
20
20
 
21
21
  const api = require('../../../src/module-api');
22
22
 
23
- // Minimal HTML escaper for the callback landing page (no template engine here).
24
- function esc(s) {
25
- return String(s == null ? '' : s).replace(/[&<>"']/g, (c) => (
26
- { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]
27
- ));
28
- }
23
+ // The one server escaper, through the doorway (task 1004876).
24
+ function esc(s) { return api.escapeHtml(s); }
29
25
 
30
26
  // The way back to the project (task 1004650): the hub's return route, keyed on the attempt's
31
27
  // one-time state. The SERVER resolves that state to the project it was minted for and lands
@@ -2173,6 +2173,7 @@ summary{min-height:24px;padding:3px 0;}
2173
2173
 
2174
2174
  <footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on <span data-brand-product-inline>Cloud&nbsp;Bongos</span>. <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="/#about">About</a><a href="/docs">Docs</a><a href="/privacy">Privacy</a><a href="/terms">Terms</a><a href="/contact">Contact</a><!--apex-only:start--><a data-link-hall href="{{buildersOrigin}}">Builders' hall</a><!--apex-only:end--></nav></footer>
2175
2175
 
2176
+ <script src="/modules/hall-ui/public/dom-utils.js"></script>
2176
2177
  <script>
2177
2178
  /* Brand-pack fill (R57): the server injects window.__BRANDING__ ahead of this
2178
2179
  script; baked markup above is the standalone fallback. */
@@ -2199,11 +2200,8 @@ summary{min-height:24px;padding:3px 0;}
2199
2200
 
2200
2201
  function $(id) { return document.getElementById(id); }
2201
2202
 
2202
- function esc(s) {
2203
- return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
2204
- return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
2205
- });
2206
- }
2203
+ /* the hall's shared escaper (dom-utils.js, loaded just above; task 1004876) */
2204
+ function esc(s) { return window.OTB.escapeHtml(s); }
2207
2205
 
2208
2206
  /* ── the app's setup on Render (task 1004353) ──
2209
2207
  GET /provisioning/instances/:id/render is the one source, on both the wizard's done
@@ -55,7 +55,7 @@ function isEditableKey(key) {
55
55
  return !!m && Number(m[1]) >= 1 && Number(m[1]) <= (DEFAULT_SETTINGS.founders || []).length;
56
56
  }
57
57
 
58
- const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
58
+ const { escapeHtml: esc } = require('../../src/module-api'); // the one server escaper (task 1004876)
59
59
  const usd = (n) => '$' + Math.round(n).toLocaleString('en-US');
60
60
  const TBA = '<span class="tba">TBA</span>';
61
61
 
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.21.181",
3
+ "version": "1.22.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.21.181",
9
+ "version": "1.22.1",
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.181",
3
+ "version": "1.22.1",
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",
@@ -10331,18 +10331,6 @@
10331
10331
  {
10332
10332
  "id": "1004889",
10333
10333
  "text": "An automatic security check on search results works again, and it now really catches a private note leaking to the wrong person instead of passing by luck."
10334
- },
10335
- {
10336
- "id": "1004886",
10337
- "text": "Core releases can no longer be labelled as containing code that never actually went out."
10338
- },
10339
- {
10340
- "id": "1004859",
10341
- "text": "The Gate page is now called Autobongos. It keeps the runner controls and drops the list of held pull requests, which was always empty because the check it waited on never ran. Old links to /gate still work. The two permissions"
10342
- },
10343
- {
10344
- "id": "1004863",
10345
- "text": "Eight database tests that had quietly stopped working are repaired and now run on every change, including the one that proves an idea's author is never paid twice and the one that proves search never shows someone a page above"
10346
10334
  }
10347
10335
  ],
10348
10336
  "1.21.179": [
@@ -10362,5 +10350,31 @@
10362
10350
  "id": "1004892",
10363
10351
  "text": "The repo health dashboard's dead-code number now counts the same way the CI check does, so it no longer jumps by about 1,800 false alarms from modules/."
10364
10352
  }
10353
+ ],
10354
+ "1.21.182": [
10355
+ {
10356
+ "id": "1004890",
10357
+ "text": "Removed a leftover permission-list checker that nothing in the product used; the real protections and their tests are unchanged."
10358
+ }
10359
+ ],
10360
+ "1.22.0": [
10361
+ {
10362
+ "id": "1004886",
10363
+ "text": "Core releases can no longer be labelled as containing code that never actually went out."
10364
+ },
10365
+ {
10366
+ "id": "1004859",
10367
+ "text": "The Gate page is now called Autobongos. It keeps the runner controls and drops the list of held pull requests, which was always empty because the check it waited on never ran. Old links to /gate still work. The two permissions"
10368
+ },
10369
+ {
10370
+ "id": "1004863",
10371
+ "text": "Eight database tests that had quietly stopped working are repaired and now run on every change, including the one that proves an idea's author is never paid twice and the one that proves search never shows someone a page above"
10372
+ }
10373
+ ],
10374
+ "1.22.1": [
10375
+ {
10376
+ "id": "1004876",
10377
+ "text": "Builder-written text now goes through one safety filter in the browser and one on the server instead of 42 hand-made copies, six of which let a stray quote through — and a check stops a new copy from creeping back."
10378
+ }
10365
10379
  ]
10366
10380
  }