@tokenoftrust/storefront-runner 2.4.37 → 2.4.39

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.
@@ -330,10 +330,13 @@ a.article-card__tag:hover { text-decoration: underline; }
330
330
  .age-gate__panel { width: min(30rem, 100%); background: var(--color-surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: clamp(24px, 4vw, 40px); text-align: center; }
331
331
  .age-gate__mark { font-family: var(--font-display); font-weight: 800; letter-spacing: .04em; font-size: 1.05rem; color: var(--color-text); }
332
332
  .age-gate__mark .is-red { color: var(--color-primary); }
333
+ .age-gate__eyebrow { font-family: var(--font-display); font-weight: 700; font-size: .75rem; letter-spacing: .12em; text-transform: uppercase; color: var(--color-muted); }
333
334
  .age-gate h2 { margin-top: 14px; font-size: clamp(1.4rem, 3vw, 1.85rem); }
334
335
  .age-gate p { margin-top: 10px; color: var(--color-text); }
335
336
  .age-gate__actions { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-top: 24px; }
336
337
  .age-gate__actions .btn { min-width: 9rem; }
338
+ .age-gate__help { font-size: .875rem; }
339
+ .age-gate__help a { color: var(--color-accent-ink, var(--color-primary)); text-decoration: underline; }
337
340
  .age-gate__deny { display: none; }
338
341
  .age-gate.is-denied .age-gate__enter, .age-gate.is-denied .age-gate__lede { display: none; }
339
342
  .age-gate.is-denied .age-gate__deny { display: block; margin-top: 18px; color: var(--color-error); font-weight: 600; }
@@ -47,7 +47,7 @@ import type {
47
47
  HeaderVariant,
48
48
  SocialLink,
49
49
  } from "./chrome/model.js";
50
- import { resolveAgeGateCopy, resolveChromeAssignment } from "./chrome/model.js";
50
+ import { isSafeAgeGateHref, resolveAgeGateCopy, resolveChromeAssignment } from "./chrome/model.js";
51
51
  import { resolveHeaderVariant } from "./chrome/variant.js";
52
52
 
53
53
  /** HTML-escape text/attribute content. Covers the five significant characters. */
@@ -436,6 +436,9 @@ function renderHeader(header: ChromeHeader, variant: HeaderVariant, currentPath?
436
436
  * can't close the tag early.
437
437
  */
438
438
  function renderAgeGate(g: ChromeAgeGate): string {
439
+ // showNo/eyebrow/helpLink defaults mirror resolveAgeGateRuntime (AgeGate.tsx)
440
+ // exactly, so the raw-chrome gate and the component gate present identically
441
+ // for the same chrome.json.
439
442
  const resolved = {
440
443
  ...g,
441
444
  title: resolveAgeGateCopy(g.title, g.minAge),
@@ -444,6 +447,13 @@ function renderAgeGate(g: ChromeAgeGate): string {
444
447
  noLabel: resolveAgeGateCopy(g.noLabel, g.minAge),
445
448
  denyText: resolveAgeGateCopy(g.denyText, g.minAge),
446
449
  fineText: resolveAgeGateCopy(g.fineText, g.minAge),
450
+ showNo: g.showNo !== false,
451
+ eyebrow:
452
+ g.eyebrow === false ? null : (resolveAgeGateCopy(g.eyebrow, g.minAge) ?? "Age verification"),
453
+ helpLink:
454
+ g.helpLink && isSafeAgeGateHref(g.helpLink.href)
455
+ ? { label: resolveAgeGateCopy(g.helpLink.label, g.minAge) ?? g.helpLink.label, href: g.helpLink.href }
456
+ : null,
447
457
  };
448
458
  const json = JSON.stringify(resolved).replace(/</g, "\\u003c");
449
459
  return `<script type="application/json" id="tot-age-gate-config">${json}</script>`;
@@ -726,20 +736,28 @@ export const CHROME_BEHAVIOR_JS = `(function () {
726
736
  return '<span' + (p.className ? ' class="' + gescape(p.className) + '"' : '') + '>' + gescape(p.text) + '</span>';
727
737
  }).join('') + '</p>'
728
738
  : '';
739
+ var geyebrow = !gmark && gcfg.eyebrow ? '<p class="age-gate__eyebrow">' + gescape(gcfg.eyebrow) + '</p>' : '';
729
740
  var gtitle = gescape(gcfg.title || ('Are you ' + gcfg.minAge + ' or older?'));
730
741
  var glede = gcfg.lede ? '<p class="age-gate__lede" id="ageGateBody">' + gescape(gcfg.lede) + '</p>' : '';
731
742
  var gfine = gcfg.fineText ? '<p class="age-gate__fine">' + gescape(gcfg.fineText) + '</p>' : '';
732
743
  var gdeny = gescape(gcfg.denyText || ('You must be ' + gcfg.minAge + ' or older to enter this site.'));
733
744
  var gdescribedby = gcfg.lede ? ' aria-describedby="ageGateBody"' : '';
745
+ var gnoButton = gcfg.showNo
746
+ ? '<button type="button" class="btn btn-outline age-gate__enter" id="ageNo">' + gescape(gcfg.noLabel || ('No, I am under ' + gcfg.minAge)) + '</button>'
747
+ : '';
748
+ var ghelp = gcfg.helpLink
749
+ ? '<p class="age-gate__help"><a href="' + gescape(gcfg.helpLink.href) + '">' + gescape(gcfg.helpLink.label) + '</a></p>'
750
+ : '';
734
751
 
735
752
  mount.innerHTML =
736
753
  '<div class="age-gate" id="ageGate" role="dialog" aria-modal="true" aria-labelledby="ageGateTitle"' + gdescribedby + '>' +
737
- '<div class="age-gate__panel">' + gmark +
754
+ '<div class="age-gate__panel">' + gmark + geyebrow +
738
755
  '<h2 id="ageGateTitle">' + gtitle + '</h2>' + glede +
739
756
  '<div class="age-gate__actions">' +
740
757
  '<button type="button" class="btn btn-primary age-gate__enter" id="ageYes">' + gescape(gcfg.yesLabel || ('Yes, I am ' + gcfg.minAge + '+')) + '</button>' +
741
- '<button type="button" class="btn btn-outline age-gate__enter" id="ageNo">' + gescape(gcfg.noLabel || ('No, I am under ' + gcfg.minAge)) + '</button>' +
758
+ gnoButton +
742
759
  '</div>' +
760
+ ghelp +
743
761
  '<p class="age-gate__deny" id="ageDeny">' + gdeny + '</p>' + gfine +
744
762
  '</div></div>';
745
763
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.4.37",
3
+ "version": "2.4.39",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
6
6
  "packageManager": "pnpm@11.9.0",
@@ -150,7 +150,7 @@ export const ACTION_CATALOG = {
150
150
  },
151
151
  "preview.build.completed": {
152
152
  actors: ["system"], source: "server", sinks: LIFECYCLE,
153
- argsAllow: ["changeId", "result"], renderedAllow: [],
153
+ argsAllow: ["changeId", "result", "prNumber", "headSource", "forgeOpenVerified", "terminalSource"], renderedAllow: [],
154
154
  desc: "A preview build finished (result: ok|failed).",
155
155
  },
156
156
  "preview.override": {
@@ -176,7 +176,7 @@ export const ACTION_CATALOG = {
176
176
  // A reconcile reached a terminal state (reconciled|blocked|failed). Low
177
177
  // volume, high value → timelined for the ops UI.
178
178
  actors: ["system"], source: "server", sinks: LIFECYCLE,
179
- argsAllow: ["changeId", "result", "evidenceStatus", "evidencePushError"], renderedAllow: [],
179
+ argsAllow: ["changeId", "result", "evidenceStatus", "evidencePushError", "prNumber", "forgeOpenVerified", "terminalSource"], renderedAllow: [],
180
180
  desc: "A candidate reconcile reached a terminal state.",
181
181
  },
182
182
  "candidate.closed": {
@@ -185,7 +185,7 @@ export const ACTION_CATALOG = {
185
185
  // unmerged), so the row leaves the operator's review queue. Low volume (one
186
186
  // per PR close), high value — the end-of-life marker for a candidate's timeline.
187
187
  actors: ["system"], source: "server", sinks: LIFECYCLE,
188
- argsAllow: ["changeId", "merged", "result", "status"], renderedAllow: [],
188
+ argsAllow: ["changeId", "merged", "result", "status", "prNumber"], renderedAllow: [],
189
189
  desc: "A merged/closed PR webhook transitioned a candidate to a terminal state.",
190
190
  },
191
191
  "prebuild.dispatched": {