@bongos/core 1.21.11 → 1.21.13

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.
@@ -0,0 +1,191 @@
1
+ {
2
+ "_": "THE MATTER CARD THAT ACTS (task 1004528, goal 1000125) on the street page, through modules/ui-design/kit/render.js. Kept beside street.states.json rather than inside it, so the street and its interiors (task 1004526) and the card each own their states. The populated card needs the hall-preview harness, whose government__docket.json puts the review-seat amendment on the board source (board:47) and whose government__board__items.json carries its ballot (item 47), with a canned answer for a vote:\n\n node scripts/hall-preview/server.js --port 4625 --fixture-me\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/street-card.states.json --base-in http://localhost:4625 --base-out http://localhost:4625\n\nAgainst the kit stub there is no docket and no pin, so these states are for the harness.",
3
+ "page": "/builders/street",
4
+ "surface": "hall-ui",
5
+ "stub": {
6
+ "prefix": "/builders/"
7
+ },
8
+ "modeQuery": false,
9
+ "ignoreRequests": [
10
+ "/api/(gds|bongos)/",
11
+ "fonts.g(oogleapis|static).com"
12
+ ],
13
+ "ignoreConsole": [
14
+ "/api/(gds|bongos)/",
15
+ "\\b401\\b",
16
+ "\\b403\\b",
17
+ "\\b404\\b",
18
+ "\\b501\\b",
19
+ "Unauthorized",
20
+ "Forbidden",
21
+ "Not Found",
22
+ "load failed",
23
+ "fonts.g(oogleapis|static).com"
24
+ ],
25
+ "states": {
26
+ "sitting": {
27
+ "auth": true,
28
+ "_": "The review-seat amendment pin pressed: the proposer in their own words, the change struck and inserted, the seats with the tally, counsel, vote for / object and abstain.",
29
+ "actions": [
30
+ [
31
+ "wait",
32
+ 900
33
+ ],
34
+ [
35
+ "click",
36
+ ".street-pin[data-matter=\"board:47\"]"
37
+ ],
38
+ [
39
+ "wait",
40
+ 700
41
+ ]
42
+ ],
43
+ "expect": {
44
+ "visible": [
45
+ "#streetCard",
46
+ "#streetCardBody",
47
+ "[data-act=\"for\"]",
48
+ "[data-act=\"object\"]",
49
+ "[data-act=\"abstain\"]"
50
+ ]
51
+ }
52
+ },
53
+ "object": {
54
+ "auth": true,
55
+ "_": "object pressed: the reason (on the record) and the part it objects to.",
56
+ "actions": [
57
+ [
58
+ "wait",
59
+ 900
60
+ ],
61
+ [
62
+ "click",
63
+ ".street-pin[data-matter=\"board:47\"]"
64
+ ],
65
+ [
66
+ "wait",
67
+ 700
68
+ ],
69
+ [
70
+ "click",
71
+ "[data-act=\"object\"]"
72
+ ],
73
+ [
74
+ "wait",
75
+ 300
76
+ ]
77
+ ],
78
+ "expect": {
79
+ "visible": [
80
+ "#mcardReason",
81
+ "#mcardSection",
82
+ "[data-act=\"object-send\"]"
83
+ ]
84
+ }
85
+ },
86
+ "voted": {
87
+ "auth": true,
88
+ "_": "vote for pressed: the canned vote answers, the toast says so and the Charter lights; the harness changes nothing, so the card reads the same ballot again.",
89
+ "actions": [
90
+ [
91
+ "wait",
92
+ 900
93
+ ],
94
+ [
95
+ "click",
96
+ ".street-pin[data-matter=\"board:47\"]"
97
+ ],
98
+ [
99
+ "wait",
100
+ 700
101
+ ],
102
+ [
103
+ "click",
104
+ "[data-act=\"for\"]"
105
+ ],
106
+ [
107
+ "wait",
108
+ 500
109
+ ]
110
+ ],
111
+ "expect": {
112
+ "visible": [
113
+ "#streetToast",
114
+ "#streetCard"
115
+ ]
116
+ }
117
+ },
118
+ "admission": {
119
+ "auth": true,
120
+ "_": "An admission at People: fine by me, or object.",
121
+ "actions": [
122
+ [
123
+ "wait",
124
+ 900
125
+ ],
126
+ [
127
+ "click",
128
+ ".street-pin[data-matter=\"access_requests:7\"]"
129
+ ],
130
+ [
131
+ "wait",
132
+ 400
133
+ ]
134
+ ],
135
+ "expect": {
136
+ "visible": [
137
+ "[data-act=\"fine\"]",
138
+ "[data-act=\"refuse\"]"
139
+ ]
140
+ }
141
+ },
142
+ "settled": {
143
+ "auth": true,
144
+ "_": "A release that is not yours yet: read-only, no acts.",
145
+ "actions": [
146
+ [
147
+ "wait",
148
+ 900
149
+ ],
150
+ [
151
+ "click",
152
+ ".street-pin[data-matter=\"override_requests:9\"]"
153
+ ],
154
+ [
155
+ "wait",
156
+ 400
157
+ ]
158
+ ],
159
+ "expect": {
160
+ "visible": [
161
+ "#streetCard"
162
+ ]
163
+ }
164
+ }
165
+ },
166
+ "reducedMotion": {
167
+ "auth": true,
168
+ "actions": [
169
+ [
170
+ "wait",
171
+ 900
172
+ ],
173
+ [
174
+ "click",
175
+ ".street-pin[data-matter=\"board:47\"]"
176
+ ],
177
+ [
178
+ "wait",
179
+ 700
180
+ ],
181
+ [
182
+ "click",
183
+ "[data-act=\"for\"]"
184
+ ],
185
+ [
186
+ "wait",
187
+ 200
188
+ ]
189
+ ]
190
+ }
191
+ }
@@ -12,6 +12,7 @@
12
12
  (tests/hall_tokens.mjs: no hall page hardcodes a web-font stylesheet). -->
13
13
  <link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
14
14
  <link rel="stylesheet" href="/builders/street.css?v=2026-10-02-inside1">
15
+ <link rel="stylesheet" href="/builders/street-card.css?v=2026-10-02-card1">
15
16
  </head>
16
17
  <!-- THE STREET (task 1004525, goal 1000125, R03). The street view of
17
18
  docs/design/mocks/governor-city/Street.dc.html (renderVals' street branch,
@@ -177,16 +178,17 @@
177
178
  </button>
178
179
  </div>
179
180
  <h2 class="street-card__title" id="streetCardTitle"></h2>
180
- <p class="street-card__meta" id="streetCardMeta" hidden></p>
181
- <ul class="street-card__lines" id="streetCardLines" hidden></ul>
182
- <p class="street-card__result" id="streetCardResult"></p>
181
+ <div class="street-card__body" id="streetCardBody"></div>
183
182
  <a class="street-card__open" id="streetCardOpen" href="/builders/street" hidden>open it where it lives</a>
184
183
  </div>
185
184
 
185
+ <div class="street-toast" id="streetToast" role="status" aria-live="polite" hidden></div>
186
+
186
187
  </div>
187
188
 
188
189
  <script src="/builders/city-draw.js?v=2026-10-02-street1"></script>
189
190
  <script src="/builders/street-draw.js?v=2026-10-02-street1"></script>
191
+ <script src="/builders/street-card.js?v=2026-10-02-card1"></script>
190
192
  <script src="/builders/street-inside.js?v=2026-10-02-inside1"></script>
191
193
  <script src="/builders/street.js?v=2026-10-02-inside1"></script>
192
194
  </body>
@@ -23,6 +23,8 @@
23
23
  // /api/bongos/goals the open goals: one district each
24
24
  // /api/bongos/goals/rollups each goal's task counts: its buildings
25
25
  // /api/bongos/builders/<viewer>/earnings your share this month (after the docket names the viewer)
26
+ // /api/bongos/government/board/items a sitting's ballot, read when its card opens (task 1004528)
27
+ // /api/bongos/government/constitution the constitution in force, for an amendment's wording change
26
28
  // A feed that fails leaves its part out (no pins, no districts, "—" for the
27
29
  // share) and the page says the docket could not be read; it never draws sample data.
28
30
  //
@@ -33,6 +35,12 @@
33
35
  //
34
36
  // MOTION is CSS only (street.css); nothing here animates. The scale write on
35
37
  // resize is layout, not motion.
38
+ //
39
+ // THE CARD ACTS (task 1004528). GovMatterCard (street-card.js) builds the full
40
+ // card and maps each act to the existing route that does it; this file sends the
41
+ // request, says the answer in a toast, re-reads the feeds so the pins, the
42
+ // readings and the evening follow, and marks the building that answered (a CSS
43
+ // class for 1.6 s: it rises or lights, and the matter's pin settles).
36
44
 
37
45
  (function () {
38
46
  'use strict';
@@ -47,6 +55,8 @@
47
55
  rollups: '/api/bongos/goals/rollups',
48
56
  earnings: function (id) { return '/api/bongos/builders/' + encodeURIComponent(id) + '/earnings'; },
49
57
  tasks: function (goalId) { return '/api/bongos/tasks?goal_id=' + encodeURIComponent(goalId); },
58
+ room: '/api/bongos/government/board/items',
59
+ constitution: '/api/bongos/government/constitution',
50
60
  };
51
61
  var NARROW = 900;
52
62
  var I = window.GovInside;
@@ -57,7 +67,8 @@
57
67
 
58
68
  var $ = function (id) { return document.getElementById(id); };
59
69
  var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
60
- var data = { docket: null, goals: [], rollups: [], earnings: null, loaded: false, tasks: {} };
70
+ var data = { docket: null, goals: [], rollups: [], earnings: null, loaded: false, tasks: {}, room: null, constitution: null };
71
+ var card = { cv: null, form: null, error: '', busy: false, fresh: null, answer: null };
61
72
  var view = { open: null, opener: null, model: null, scale: 1, mode: null, inside: null, insideModel: null };
62
73
 
63
74
  // ---------- light / dark ----------
@@ -336,6 +347,7 @@
336
347
  view.insideModel = null;
337
348
  }
338
349
 
350
+ markAnswer();
339
351
  fit();
340
352
  if (view.open) fillCard();
341
353
  return m;
@@ -587,22 +599,16 @@
587
599
  }
588
600
 
589
601
 
590
- // ---------- the short matter card ----------
602
+ // ---------- the matter card (task 1004528: it acts) ----------
591
603
  function fillCard() {
592
- var cv = S.cardFor(data.docket, view.open, Date.now());
604
+ var C = window.GovMatterCard;
605
+ var cv = C ? C.model({ docket: data.docket, key: view.open, room: data.room, constitution: data.constitution, now: Date.now() }) : null;
593
606
  if (!cv) { closeCard(false); return null; }
607
+ card.cv = cv;
594
608
  $('streetCardWhere').textContent = cv.where;
595
609
  $('streetCardWhen').textContent = cv.when;
596
610
  $('streetCardTitle').textContent = cv.title;
597
- var meta = $('streetCardMeta');
598
- var metaText = [cv.proposer, cv.tally].filter(Boolean).join(' · ');
599
- meta.textContent = metaText;
600
- meta.hidden = !metaText;
601
- var lines = $('streetCardLines');
602
- clear(lines);
603
- cv.lines.forEach(function (t) { lines.appendChild(node('li', '', t)); });
604
- lines.hidden = !cv.lines.length;
605
- $('streetCardResult').textContent = cv.result;
611
+ C.render(document, $('streetCardBody'), cv, { act: actOn, openForm: openForm }, { form: card.form, error: card.error, busy: card.busy, fresh: card.fresh });
606
612
  var open = $('streetCardOpen');
607
613
  if (cv.href) open.setAttribute('href', cv.href);
608
614
  open.hidden = !cv.href;
@@ -624,19 +630,22 @@
624
630
  function openCard(key, opener) {
625
631
  view.open = key;
626
632
  view.opener = opener || null;
627
- var card = $('streetCard');
633
+ card.form = null; card.error = ''; card.fresh = null;
634
+ var el = $('streetCard');
628
635
  if (!fillCard()) return;
629
- card.hidden = false;
636
+ el.hidden = false;
630
637
  draw();
631
638
  var close = $('streetCardClose');
632
639
  if (close && close.focus) close.focus();
640
+ if (card.cv && card.cv.loading) return loadRoom();
633
641
  }
634
642
  function closeCard(restore) {
635
643
  var was = view.opener;
636
644
  view.open = null;
637
645
  view.opener = null;
638
- var card = $('streetCard');
639
- if (card) card.hidden = true;
646
+ card.cv = null; card.form = null; card.error = ''; card.busy = false; card.fresh = null;
647
+ var el = $('streetCard');
648
+ if (el) el.hidden = true;
640
649
  draw();
641
650
  // The pins were redrawn, so focus goes back to the new button for the same matter.
642
651
  if (restore !== false && was && was.getAttribute) {
@@ -652,6 +661,87 @@
652
661
  }
653
662
  }
654
663
  }
664
+ // ---------- acting from the card (task 1004528) ----------
665
+ // A sitting's ballot and the constitution in force, read when its card opens
666
+ // and again after an act. A failed read leaves an empty room, so the card says
667
+ // the sitting could not be read rather than drawing a ballot it never saw.
668
+ function loadRoom() {
669
+ return Promise.all([getJson(FEEDS.room), getJson(FEEDS.constitution)]).then(function (r) {
670
+ data.room = r[0] && (Array.isArray(r[0].open) || Array.isArray(r[0].closed)) ? r[0] : { open: [], closed: [] };
671
+ data.constitution = r[1] && r[1].board ? r[1] : null;
672
+ if (view.open) fillCard();
673
+ });
674
+ }
675
+ function send(req) {
676
+ return fetch(req.url, { method: req.method, credentials: 'same-origin', headers: { accept: 'application/json', 'content-type': 'application/json' }, body: JSON.stringify(req.body || {}) })
677
+ .then(function (r) {
678
+ return Promise.resolve(r && r.json ? r.json() : {}).catch(function () { return {}; })
679
+ .then(function (b) { return { ok: !!(r && r.ok), status: r ? r.status : 0, body: b || {} }; });
680
+ })
681
+ .catch(function () { return { ok: false, status: 0, body: {} }; });
682
+ }
683
+ function openForm(name) {
684
+ card.form = name || null;
685
+ card.error = '';
686
+ fillCard();
687
+ var f = name === 'object' ? $('mcardReason') : name === 'revise' ? $('mcardRevise') : $('streetCardClose');
688
+ if (f && f.focus) f.focus();
689
+ }
690
+ // One act: checked, sent to its own route (the server's permission checks
691
+ // decide), then answered. An act that records nothing (abstain, hold) is
692
+ // answered without a request.
693
+ function actOn(key, input) {
694
+ var C = window.GovMatterCard, cv = card.cv;
695
+ if (!C || !cv || card.busy) return Promise.resolve(false);
696
+ var refused = C.check(cv, key, input);
697
+ if (refused) { card.error = refused; fillCard(); return Promise.resolve(false); }
698
+ card.error = '';
699
+ var req = C.request(cv, key, input);
700
+ if (!req) return Promise.resolve(answered(cv, key, {}, false));
701
+ card.busy = true;
702
+ fillCard();
703
+ return send(req).then(function (out) {
704
+ card.busy = false;
705
+ if (!out.ok) { card.error = C.refusal(out.status, out.body); if (view.open) fillCard(); return false; }
706
+ return answered(cv, key, out.body, true);
707
+ });
708
+ }
709
+ function answered(cv, key, body, recorded) {
710
+ var a = window.GovMatterCard.answer(cv, key, body);
711
+ say(a.toast);
712
+ card.form = null;
713
+ card.fresh = key === 'for' || key === 'object' ? 'you' : null;
714
+ if (a.building && a.kind) card.answer = { building: a.building, matter: cv.key, kind: a.kind, at: Date.now() };
715
+ if (a.close) closeCard(true);
716
+ else if (view.open) { fillCard(); var c = $('streetCardClose'); if (c && c.focus) c.focus(); }
717
+ if (!recorded) return true;
718
+ // the street answers: pins, readings, counts and the evening from the docket
719
+ // as it now stands, and the ballot again when a card is still open
720
+ data.room = null;
721
+ return Promise.all([load(), view.open ? loadRoom() : null]).then(function () { return true; });
722
+ }
723
+ // The building that answered rises or lights, and the matter's pin settles:
724
+ // a class on the freshly drawn nodes for 1.6 s, the motion itself in CSS.
725
+ function markAnswer() {
726
+ var a = card.answer;
727
+ if (!a) return;
728
+ if (Date.now() - a.at > 1600) { card.answer = null; return; }
729
+ var blds = ($('streetBlds') && $('streetBlds').children) || [];
730
+ for (var i = 0; i < blds.length; i++) if (blds[i].getAttribute('data-key') === a.building) blds[i].setAttribute('class', 'street-answer street-answer--' + a.kind);
731
+ var pins = ($('streetPins') && $('streetPins').children) || [];
732
+ for (var j = 0; j < pins.length; j++) if (pins[j].getAttribute('data-matter') === a.matter) pins[j].className += ' street-pin--answer';
733
+ }
734
+ var toastTimer = null;
735
+ function say(text) {
736
+ var t = $('streetToast');
737
+ if (!t || !text) return;
738
+ t.textContent = text;
739
+ t.hidden = false;
740
+ if (typeof setTimeout !== 'function') return;
741
+ if (toastTimer && typeof clearTimeout === 'function') clearTimeout(toastTimer);
742
+ toastTimer = setTimeout(function () { if (t.textContent === text) t.hidden = true; }, 3600);
743
+ }
744
+
655
745
  function onKey(e) {
656
746
  if (e.key === 'Escape' && view.open) { e.preventDefault(); closeCard(true); }
657
747
  }
@@ -677,6 +767,6 @@
677
767
  return load();
678
768
  }
679
769
 
680
- window.GovStreetPage = { boot: boot, draw: draw, load: load, fit: fit, openCard: openCard, closeCard: closeCard, pickFloor: pickFloor, goStreet: goStreet, FEEDS: FEEDS, THEME_KEY: THEME_KEY };
770
+ window.GovStreetPage = { boot: boot, draw: draw, load: load, fit: fit, openCard: openCard, closeCard: closeCard, actOn: actOn, pickFloor: pickFloor, goStreet: goStreet, FEEDS: FEEDS, THEME_KEY: THEME_KEY };
681
771
  window.GovStreetPage.ready = boot();
682
772
  }());
@@ -249,6 +249,12 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
249
249
  #view-new .rmGlow,#view-new .rmStem,#view-new .rmShade,#view-new .rmMug,#view-new .rmLeaf{display:none;}
250
250
  #view-new .rmFloor{top:auto;height:80px;}
251
251
  }
252
+ /* under 600 the words fill the screen, so the window fades further and its lit windows
253
+ go — they read as stray marks between the review rows (task 1004543) */
254
+ @media (max-width:600px){
255
+ #view-new .rmWindow{opacity:.18;}
256
+ #view-new .rmLight{display:none;}
257
+ }
252
258
  /* the words column above the room, held to the mockup's measure so the window never
253
259
  slides under it; headings in the mockup's sentence case */
254
260
  #view-new .wrap{position:relative;z-index:1;min-height:calc(100vh - var(--top-h,64px) - 120px);}
@@ -386,7 +392,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
386
392
  display:block;padding-top:4px;
387
393
  }
388
394
  .humanSteps b{display:block;font-size:16px;font-weight:500;color:var(--ink);}
389
- .humanSteps p{font-size:14px;font-weight:400;color:var(--ink-soft);line-height:1.5;margin-top:4px;max-width:56ch;}
395
+ .humanSteps p{font-size:14px;font-weight:400;color:var(--ink-soft);line-height:1.5;margin-top:4px;max-width:56ch;overflow-wrap:anywhere;}
390
396
  /* a repo the platform already handled is named as a plain inline link (task 1002855) —
391
397
  the .ghbtn treatment reads as an action to take, and there is nothing here to do */
392
398
  .humanSteps p a{color:var(--ink);font-weight:400;text-decoration:underline;text-underline-offset:3px;display:inline-block;padding:3px 0;min-height:24px;line-height:18px;}
@@ -415,6 +421,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
415
421
  .copy:hover{border-color:var(--ink-faint);color:var(--ink);}
416
422
  .copy.did{background:none;border-color:var(--ok);color:var(--ok);}
417
423
  .samePath{font-size:13px;font-weight:400;color:var(--ink-faint);margin-top:18px;}
424
+ .samePath a{display:inline-block;padding:3px 0;min-height:24px;line-height:18px;}
418
425
  #liveProgress{margin-top:4px;}
419
426
 
420
427
  /* ── the standup strip (task 2083), drawn as the lifecycle rail (DESIGN.md):
@@ -6942,6 +6949,8 @@ summary{min-height:24px;padding:3px 0;}
6942
6949
  /* the alive moment (task 1004504): said only once the project has answered */
6943
6950
  $('done-h').textContent = (state.name || state.slug || 'Your project') + ' is alive.';
6944
6951
  paintInviteGate(true); /* the hall answers, so others can join now */
6952
+ /* task 1004543: the line under it stops saying "setting up" once it answers */
6953
+ $('doneLede').innerHTML = 'Its home is ready. Watch the rest here, or any time on <a href="?view=mine">your projects page</a>.';
6945
6954
  setProgress(progressMarkup(-1, 3,
6946
6955
  'Your project is answering at ' + link + '. Finish the steps below and the hall is yours.', 'isLive', flow));
6947
6956
  return true;
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.21.11",
3
+ "version": "1.21.13",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.21.11",
9
+ "version": "1.21.13",
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.11",
3
+ "version": "1.21.13",
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",
@@ -8822,5 +8822,17 @@
8822
8822
  "id": "1004526",
8823
8823
  "text": "Every building on the governor's street now opens. Step through a door and the building appears in section, drawn exactly like the prototype: each floor a real storey with its sign, name and state on the wall, dark empty floor"
8824
8824
  }
8825
+ ],
8826
+ "1.21.12": [
8827
+ {
8828
+ "id": "1004543",
8829
+ "text": "When your new project comes alive, the line under \"is alive\" now says its home is ready instead of still saying it is being set up. On phones, the dim night-room picture behind the setup screens no longer shows stray lit windo"
8830
+ }
8831
+ ],
8832
+ "1.21.13": [
8833
+ {
8834
+ "id": "1004528",
8835
+ "text": "On the street, a pin now opens the full matter card: who raised it in their own words, exactly what would change, who has voted and what it still needs, the objections, and a short note of advice. Governors can vote for, objec"
8836
+ }
8825
8837
  ]
8826
8838
  }
@@ -177,6 +177,9 @@ const CANNED_WRITES = [
177
177
  // afterwards from the card's "Offered skills" editor.
178
178
  ['POST', /^specialities$/, 'specialities.post'],
179
179
  ['PATCH', /^specialities\/\d+$/, 'specialities__12.patch'],
180
+ // a vote cast from the street's matter card (task 1004528), so the card's
181
+ // answer (the toast, the building lighting) renders here too.
182
+ ['POST', /^government\/board\/items\/\d+\/votes$/, 'government__board__items__votes.post'],
180
183
  ];
181
184
 
182
185
  // A DATE THAT ROTS CANNOT DEMO AN AGE SIGNAL (task 1004041). The help-request
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.11'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
78
+ const CORE_VERSION = '1.21.13'; // 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');