@certrev/cert-block 0.5.4 → 1.0.0
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/CHANGELOG.md +195 -12
- package/LICENSE +21 -0
- package/README.md +422 -50
- package/dist/builder/cert-chrome-data.d.ts +26 -8
- package/dist/builder/cert-chrome-data.d.ts.map +1 -1
- package/dist/builder/cert-chrome-data.js +5 -5
- package/dist/builder/cert-chrome-data.js.map +1 -1
- package/dist/builder/cert-review-card.d.ts +35 -5
- package/dist/builder/cert-review-card.d.ts.map +1 -1
- package/dist/builder/cert-review-card.js +115 -34
- package/dist/builder/cert-review-card.js.map +1 -1
- package/dist/builder/index.d.ts +33 -16
- package/dist/builder/index.d.ts.map +1 -1
- package/dist/builder/index.js +33 -16
- package/dist/builder/index.js.map +1 -1
- package/dist/components/CertBadge.d.ts +2 -2
- package/dist/components/CertBadge.d.ts.map +1 -1
- package/dist/components/CertBadge.js +11 -5
- package/dist/components/CertBadge.js.map +1 -1
- package/dist/components/CertJsonLd.d.ts.map +1 -1
- package/dist/components/CertJsonLd.js.map +1 -1
- package/dist/components/CertRevBacklink.d.ts.map +1 -1
- package/dist/components/CertRevBacklink.js +3 -2
- package/dist/components/CertRevBacklink.js.map +1 -1
- package/dist/components/CertReview.d.ts +1 -1
- package/dist/components/ExpertBio.d.ts.map +1 -1
- package/dist/components/ExpertBio.js +12 -4
- package/dist/components/ExpertBio.js.map +1 -1
- package/dist/components/escape.d.ts +28 -1
- package/dist/components/escape.d.ts.map +1 -1
- package/dist/components/escape.js +37 -8
- package/dist/components/escape.js.map +1 -1
- package/dist/components/format.d.ts +51 -2
- package/dist/components/format.d.ts.map +1 -1
- package/dist/components/format.js +103 -6
- package/dist/components/format.js.map +1 -1
- package/dist/components/preview-block.d.ts +1 -1
- package/dist/components/preview-block.js +3 -3
- package/dist/components/preview-block.js.map +1 -1
- package/dist/components/rel.d.ts +24 -0
- package/dist/components/rel.d.ts.map +1 -0
- package/dist/components/rel.js +24 -0
- package/dist/components/rel.js.map +1 -0
- package/dist/components/render-cert-block.d.ts +50 -22
- package/dist/components/render-cert-block.d.ts.map +1 -1
- package/dist/components/render-cert-block.js +146 -80
- package/dist/components/render-cert-block.js.map +1 -1
- package/dist/components/render-def.d.ts +13 -14
- package/dist/components/render-def.d.ts.map +1 -1
- package/dist/components/render-def.js +14 -15
- package/dist/components/render-def.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/jsonld/project.d.ts +46 -13
- package/dist/jsonld/project.d.ts.map +1 -1
- package/dist/jsonld/project.js +139 -56
- package/dist/jsonld/project.js.map +1 -1
- package/dist/modal/avatar-resize.d.ts +10 -10
- package/dist/modal/avatar-resize.js +14 -14
- package/dist/modal/avatar-resize.js.map +1 -1
- package/dist/modal/cert-modal-view.d.ts +27 -7
- package/dist/modal/cert-modal-view.d.ts.map +1 -1
- package/dist/modal/cert-modal-view.js +49 -25
- package/dist/modal/cert-modal-view.js.map +1 -1
- package/dist/modal/certrev-cert-modal.d.ts +5 -5
- package/dist/modal/certrev-cert-modal.js +9 -9
- package/dist/modal/certrev-cert-modal.js.map +1 -1
- package/dist/modal/certrev-cert-modal.styles.d.ts +6 -6
- package/dist/modal/certrev-cert-modal.styles.d.ts.map +1 -1
- package/dist/modal/certrev-cert-modal.styles.js +8 -8
- package/dist/modal/certrev-cert-modal.styles.js.map +1 -1
- package/dist/modal/certrev-cert.entry.d.ts +8 -6
- package/dist/modal/certrev-cert.entry.d.ts.map +1 -1
- package/dist/modal/certrev-cert.entry.js +9 -7
- package/dist/modal/certrev-cert.entry.js.map +1 -1
- package/dist/modal/certrev-cert.js +6 -6
- package/dist/modal/display-strings.d.ts +25 -5
- package/dist/modal/display-strings.d.ts.map +1 -1
- package/dist/modal/display-strings.js +27 -5
- package/dist/modal/display-strings.js.map +1 -1
- package/dist/modal/escape.d.ts +26 -3
- package/dist/modal/escape.d.ts.map +1 -1
- package/dist/modal/escape.js +32 -3
- package/dist/modal/escape.js.map +1 -1
- package/dist/modal/font-face.d.ts.map +1 -1
- package/dist/modal/font-face.js +8 -11
- package/dist/modal/font-face.js.map +1 -1
- package/dist/modal/ftc-disclosure.d.ts +2 -2
- package/dist/modal/ftc-disclosure.js +2 -2
- package/dist/modal/ftc-guard.d.ts +44 -9
- package/dist/modal/ftc-guard.d.ts.map +1 -1
- package/dist/modal/ftc-guard.js +50 -17
- package/dist/modal/ftc-guard.js.map +1 -1
- package/dist/modal/index.d.ts +1 -1
- package/dist/modal/index.js +1 -1
- package/dist/modal/interactions.d.ts +2 -2
- package/dist/modal/interactions.js +2 -2
- package/dist/modal/logo-paths.js +2 -2
- package/dist/modal/logo-paths.js.map +1 -1
- package/dist/modal/placement.d.ts +4 -3
- package/dist/modal/placement.d.ts.map +1 -1
- package/dist/modal/placement.js +7 -6
- package/dist/modal/placement.js.map +1 -1
- package/dist/modal/revalidate.d.ts +2 -3
- package/dist/modal/revalidate.d.ts.map +1 -1
- package/dist/modal/revalidate.js +2 -3
- package/dist/modal/revalidate.js.map +1 -1
- package/dist/modal/verify-client.d.ts +2 -2
- package/dist/modal/verify-client.js +2 -2
- package/dist/verify/cache.d.ts +21 -6
- package/dist/verify/cache.d.ts.map +1 -1
- package/dist/verify/cache.js +29 -8
- package/dist/verify/cache.js.map +1 -1
- package/dist/verify/fetch-with-deadline.d.ts +35 -0
- package/dist/verify/fetch-with-deadline.d.ts.map +1 -0
- package/dist/verify/fetch-with-deadline.js +55 -0
- package/dist/verify/fetch-with-deadline.js.map +1 -0
- package/dist/verify/get-verified-envelope.d.ts +23 -9
- package/dist/verify/get-verified-envelope.d.ts.map +1 -1
- package/dist/verify/get-verified-envelope.js +89 -40
- package/dist/verify/get-verified-envelope.js.map +1 -1
- package/dist/verify/resolve-kid.d.ts +3 -0
- package/dist/verify/resolve-kid.d.ts.map +1 -1
- package/dist/verify/resolve-kid.js +17 -8
- package/dist/verify/resolve-kid.js.map +1 -1
- package/dist/webcomponent/certrev-badge.d.ts.map +1 -1
- package/dist/webcomponent/certrev-badge.js +24 -2
- package/dist/webcomponent/certrev-badge.js.map +1 -1
- package/dist/webcomponent/render-badge-html.d.ts +1 -1
- package/dist/webcomponent/render-badge-html.d.ts.map +1 -1
- package/dist/webcomponent/render-badge-html.js +36 -17
- package/dist/webcomponent/render-badge-html.js.map +1 -1
- package/package.json +14 -2
- package/src/builder/cert-chrome-data.ts +26 -8
- package/src/builder/cert-review-card.tsx +147 -38
- package/src/builder/index.tsx +34 -17
- package/src/components/CertBadge.tsx +14 -9
- package/src/components/CertJsonLd.tsx +1 -1
- package/src/components/CertRevBacklink.tsx +10 -3
- package/src/components/CertReview.tsx +1 -1
- package/src/components/ExpertBio.tsx +12 -7
- package/src/components/escape.ts +37 -7
- package/src/components/format.ts +102 -6
- package/src/components/preview-block.ts +3 -3
- package/src/components/rel.ts +23 -0
- package/src/components/render-cert-block.ts +192 -109
- package/src/components/render-def.ts +14 -15
- package/src/index.ts +4 -2
- package/src/jsonld/project.ts +155 -59
- package/src/modal/avatar-resize.ts +14 -14
- package/src/modal/cert-modal-view.ts +52 -27
- package/src/modal/certrev-cert-modal.styles.ts +8 -8
- package/src/modal/certrev-cert-modal.ts +9 -9
- package/src/modal/certrev-cert.entry.ts +9 -7
- package/src/modal/display-strings.ts +26 -5
- package/src/modal/escape.ts +32 -4
- package/src/modal/font-face.ts +8 -11
- package/src/modal/ftc-disclosure.ts +2 -2
- package/src/modal/ftc-guard.ts +73 -18
- package/src/modal/index.ts +1 -1
- package/src/modal/interactions.ts +2 -2
- package/src/modal/logo-paths.ts +2 -2
- package/src/modal/placement.ts +7 -6
- package/src/modal/revalidate.ts +2 -3
- package/src/modal/verify-client.ts +2 -2
- package/src/verify/cache.ts +34 -8
- package/src/verify/fetch-with-deadline.ts +60 -0
- package/src/verify/get-verified-envelope.ts +131 -48
- package/src/verify/resolve-kid.ts +22 -6
- package/src/webcomponent/certrev-badge.ts +24 -2
- package/src/webcomponent/render-badge-html.ts +66 -19
- package/src/__tests__/__snapshots__/render-cert-block.test.ts.snap +0 -17
- package/src/__tests__/components.test.tsx +0 -206
- package/src/__tests__/preview-block.test.ts +0 -84
- package/src/__tests__/project.test.ts +0 -128
- package/src/__tests__/render-cert-block.test.ts +0 -657
- package/src/__tests__/render-def.test.tsx +0 -295
- package/src/__tests__/verify.test.ts +0 -239
- package/src/__tests__/webcomponent.test.tsx +0 -116
- package/src/builder/__tests__/builder.test.tsx +0 -158
- package/src/modal/cert-modal-view.test.ts +0 -175
- package/src/modal/certrev-cert-modal.test.ts +0 -125
- package/src/modal/ftc-guard.test.ts +0 -153
- package/src/modal/interactions.test.ts +0 -250
- package/src/modal/placement.test.ts +0 -398
- package/src/modal/revalidate.test.ts +0 -111
- package/src/modal/verify-client.test.ts +0 -164
|
@@ -4,19 +4,18 @@
|
|
|
4
4
|
* layer) + the two always-on COMPLIANCE strings.
|
|
5
5
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* that must paint the SAME compliant, optionally-themed badge from the SAME facts.
|
|
7
|
+
* This is the Builder / headless projection twin's half of the "law-as-code"
|
|
8
|
+
* contract: cert-block is one of the four render surfaces (Shopify Liquid,
|
|
9
|
+
* WordPress PHP, JS engine, and this) that must paint the SAME compliant,
|
|
10
|
+
* optionally-themed badge from the SAME facts.
|
|
12
11
|
*
|
|
13
12
|
* cert-block is a self-contained npm package — it does NOT import the portal's
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* design-guide enforcement core. Instead it mirrors, at the RENDER edge, the
|
|
14
|
+
* small subset that shape the projection carries:
|
|
16
15
|
*
|
|
17
16
|
* 1. The four allowlisted THEMEABLE tokens `{accentColor, surface, cornerRadius,
|
|
18
17
|
* fontSlot}`, emitted as the exact `--certrev-*` CSS custom properties the
|
|
19
|
-
* Shopify/WordPress engines emit and the
|
|
18
|
+
* Shopify/WordPress engines emit and the crawl monitor asserts
|
|
20
19
|
* (`--certrev-accent` / `--certrev-surface` / `--certrev-radius` /
|
|
21
20
|
* `--certrev-font`). Each is RE-VALIDATED here at render time (defense in
|
|
22
21
|
* depth over the write-path fail-closed gate) against the SAME strict
|
|
@@ -41,16 +40,16 @@
|
|
|
41
40
|
* entry targets (edge / Workers / server / client).
|
|
42
41
|
*/
|
|
43
42
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
44
|
-
// Themeable token vocabulary (mirrors portal
|
|
43
|
+
// Themeable token vocabulary (mirrors the portal's design-guide token module)
|
|
45
44
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
46
45
|
/** The font ENUM. Resolves to a FIXED slot stack — never a free-form family. */
|
|
47
46
|
export const FONT_SLOTS = ['sans', 'serif', 'system', 'grotesk', 'mono'];
|
|
48
47
|
/**
|
|
49
48
|
* The `fontSlot` → fixed font-stack literal — the render-def theme map the webcomponent MODAL
|
|
50
49
|
* (`resolveRenderTheme` → `renderBadgeHtml`) and the legacy `<CertBadge>` resolve. The three original
|
|
51
|
-
* slots (sans/serif/system) stay BYTE-IDENTICAL to the Shopify Liquid engine's
|
|
52
|
-
*
|
|
53
|
-
*
|
|
50
|
+
* slots (sans/serif/system) stay BYTE-IDENTICAL to the Shopify Liquid engine's badge-snippet
|
|
51
|
+
* stacks, so a themed brand's popup modal and its inline Liquid card paint the same
|
|
52
|
+
* typography — the load-bearing card==modal invariant.
|
|
54
53
|
* grotesk (Plus Jakarta Sans) + mono (JetBrains Mono) are ADDED at the shared guide values —
|
|
55
54
|
* byte-identical to portal `FONT_SLOT_STACKS`, this package's `RENDER_BLOCK_FONT_STACKS`, and the
|
|
56
55
|
* Liquid twin's added branches — so card == modal holds for them too. A raw family string in a
|
|
@@ -139,8 +138,8 @@ export function isFieldHidden(def, id) {
|
|
|
139
138
|
// sans/serif/system to the legacy Liquid table (the card==modal invariant) while this
|
|
140
139
|
// map pins them to the guide table; grotesk/mono are byte-identical across both.
|
|
141
140
|
/**
|
|
142
|
-
* The full portal font-slot enum (mirrors portal
|
|
143
|
-
* `
|
|
141
|
+
* The full portal font-slot enum (mirrors the portal design-guide's `FONT_SLOTS`).
|
|
142
|
+
* `grotesk` ⇐ the guide's wordmark slot; `mono` ⇐ the label slot.
|
|
144
143
|
*/
|
|
145
144
|
export const RENDER_BLOCK_FONT_SLOTS = ['sans', 'serif', 'system', 'grotesk', 'mono'];
|
|
146
145
|
/**
|
|
@@ -177,7 +176,7 @@ function parseHexColor(hex) {
|
|
|
177
176
|
}
|
|
178
177
|
/**
|
|
179
178
|
* AUTO-CONTRAST PICKER — the ink painted over an opaque fill. BYTE-FAITHFUL to the portal's
|
|
180
|
-
*
|
|
179
|
+
* design-guide `accentFg`: Rec.601 perceptual luma with a strict `> 0.62`
|
|
181
180
|
* cutoff, returning EXACTLY `#141414` (near-black) or `#ffffff`; an unparseable value falls back
|
|
182
181
|
* to the dark ink (safe default). Used at the render edge to DERIVE `barInkFg` from a themed
|
|
183
182
|
* `barInk` (v2 Phase 1), so the bar foreground always clears its own fill with no separate stored
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-def.js","sourceRoot":"","sources":["../../src/components/render-def.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"render-def.js","sourceRoot":"","sources":["../../src/components/render-def.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export { ExpertBio, type ExpertBioProps } from './components/ExpertBio.js';
|
|
|
27
27
|
export { escapeAttribute, escapeHtml, safeCssColor, safeHttpUrl, tidyText } from './components/escape.js';
|
|
28
28
|
export { credentialSuffix, DEFAULT_ACCENT, expertNameWithCredentials, formatDate, type ResolvedDisplay, resolveDisplay, } from './components/format.js';
|
|
29
29
|
export { type PreviewBlockOptions, previewExpiry, renderPreviewBlock, } from './components/preview-block.js';
|
|
30
|
+
export { CERTREV_LINK_REL } from './components/rel.js';
|
|
30
31
|
export { type CertBlockFace, type CertBlockFacts, type CertBlockLayout, type CertBlockMode, type RenderCertBlockInput, renderCertBlock, } from './components/render-cert-block.js';
|
|
31
32
|
export { accentFg, type BadgeVisibilityFieldId, CERT_SCOPE_LINE, CERT_STRINGS_VERSION, type CertRenderDef, type CertThemeTokens, COMPENSATED_EXPERT_CUE, CREDENTIAL_VERIFIED_ATTRIBUTION, DEFAULT_BLOCK_THEME, FONT_SLOTS, FONT_STACKS, type FontSlot, HIDEABLE_FIELDS, isCornerRadius, isFieldHidden, isFontSlot, isOpaqueHexColor, isRenderBlockFontSlot, RENDER_BLOCK_FONT_SLOTS, RENDER_BLOCK_FONT_STACKS, type RenderBlockFontSlot, type ResolvedBlockTheme, type ResolvedRenderTheme, resolveBlockTheme, resolveRenderTheme, themeCssVarDeclarations, } from './components/render-def.js';
|
|
32
33
|
export { CANONICALIZATION, type CertContent, type CertCredential, type CertDeliveryEnvelope, type CertDisplayConfig, type CertLifecycle, type CertPayload, type CertSignature, type CertSubject, type CertSuppressReason, type CertVerdict, CONTRACT_VERSION, type ContractVersion, type Ed25519PublicKeyInput, type RenderContext, type ResolvePublicKeyByKid, renderVerdict, SIGNATURE_ALG, type SignatureAlg, type VerdictKernel, verifyEnvelope, verifySignatureOnly, } from './contract/kernel.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC7E,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,iCAAiC,CAAA;AAC5F,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC7E,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAEzG,OAAO,EACN,gBAAgB,EAChB,cAAc,EACd,yBAAyB,EACzB,UAAU,EACV,KAAK,eAAe,EACpB,cAAc,GACd,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACN,KAAK,mBAAmB,EACxB,aAAa,EACb,kBAAkB,GAClB,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,eAAe,GACf,MAAM,mCAAmC,CAAA;AAE1C,OAAO,EACN,QAAQ,EACR,KAAK,sBAAsB,EAC3B,eAAe,EACf,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,sBAAsB,EACtB,+BAA+B,EAC/B,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,KAAK,QAAQ,EACb,eAAe,EACf,cAAc,EACd,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,GACvB,MAAM,4BAA4B,CAAA;AAInC,OAAO,EACN,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,aAAa,EACb,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,cAAc,EACd,mBAAmB,GACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,iBAAiB,EACjB,uBAAuB,EACvB,wBAAwB,GACxB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,0BAA0B,EAC/B,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,GAClB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACN,KAAK,0BAA0B,EAC/B,mBAAmB,EACnB,KAAK,YAAY,EACjB,iBAAiB,GACjB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAAE,KAAK,kBAAkB,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC7E,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,iCAAiC,CAAA;AAC5F,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC7E,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAEzG,OAAO,EACN,gBAAgB,EAChB,cAAc,EACd,yBAAyB,EACzB,UAAU,EACV,KAAK,eAAe,EACpB,cAAc,GACd,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACN,KAAK,mBAAmB,EACxB,aAAa,EACb,kBAAkB,GAClB,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEtD,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,eAAe,GACf,MAAM,mCAAmC,CAAA;AAE1C,OAAO,EACN,QAAQ,EACR,KAAK,sBAAsB,EAC3B,eAAe,EACf,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,sBAAsB,EACtB,+BAA+B,EAC/B,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,KAAK,QAAQ,EACb,eAAe,EACf,cAAc,EACd,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,GACvB,MAAM,4BAA4B,CAAA;AAInC,OAAO,EACN,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,aAAa,EACb,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,cAAc,EACd,mBAAmB,GACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,iBAAiB,EACjB,uBAAuB,EACvB,wBAAwB,GACxB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,0BAA0B,EAC/B,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,GAClB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACN,KAAK,0BAA0B,EAC/B,mBAAmB,EACnB,KAAK,YAAY,EACjB,iBAAiB,GACjB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAAE,KAAK,kBAAkB,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -28,11 +28,13 @@ export { ExpertBio } from './components/ExpertBio.js';
|
|
|
28
28
|
export { escapeAttribute, escapeHtml, safeCssColor, safeHttpUrl, tidyText } from './components/escape.js';
|
|
29
29
|
// ── Presentation helpers (shared with the Web Component) ────────────────────────────
|
|
30
30
|
export { credentialSuffix, DEFAULT_ACCENT, expertNameWithCredentials, formatDate, resolveDisplay, } from './components/format.js';
|
|
31
|
-
// ── Pre-sales PREVIEW block — static, non-delivery, hard-bounded
|
|
31
|
+
// ── Pre-sales PREVIEW block — static, non-delivery, hard-bounded ──
|
|
32
32
|
export { previewExpiry, renderPreviewBlock, } from './components/preview-block.js';
|
|
33
|
+
// ── Link qualification — the `rel` every anchor this package emits carries (read-only) ──
|
|
34
|
+
export { CERTREV_LINK_REL } from './components/rel.js';
|
|
33
35
|
// ── Cert-render BLOCK — the LOCKED 3-mode cert design as an SSR-safe HTML string ──
|
|
34
36
|
export { renderCertBlock, } from './components/render-cert-block.js';
|
|
35
|
-
// ── Design-guide render-def support (
|
|
37
|
+
// ── Design-guide render-def support (themeable tokens + subtractive visibility) ──
|
|
36
38
|
export { accentFg, CERT_SCOPE_LINE, CERT_STRINGS_VERSION, COMPENSATED_EXPERT_CUE, CREDENTIAL_VERIFIED_ATTRIBUTION, DEFAULT_BLOCK_THEME, FONT_SLOTS, FONT_STACKS, HIDEABLE_FIELDS, isCornerRadius, isFieldHidden, isFontSlot, isOpaqueHexColor, isRenderBlockFontSlot, RENDER_BLOCK_FONT_SLOTS, RENDER_BLOCK_FONT_STACKS, resolveBlockTheme, resolveRenderTheme, themeCssVarDeclarations, } from './components/render-def.js';
|
|
37
39
|
// ── Contract surface (types + kernel) — re-export the binding point ────────────────
|
|
38
40
|
// NOTE: the test/dev fixtures (makeSignedEnvelope, makeMockPayload, FIXTURE_KID) are
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,qFAAqF;AACrF,OAAO,EAAE,SAAS,EAAuB,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,UAAU,EAAwB,MAAM,4BAA4B,CAAA;AAC7E,OAAO,EAAE,eAAe,EAA6B,MAAM,iCAAiC,CAAA;AAC5F,OAAO,EAAE,UAAU,EAAwB,MAAM,4BAA4B,CAAA;AAC7E,OAAO,EAAE,SAAS,EAAuB,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACzG,uFAAuF;AACvF,OAAO,EACN,gBAAgB,EAChB,cAAc,EACd,yBAAyB,EACzB,UAAU,EAEV,cAAc,GACd,MAAM,wBAAwB,CAAA;AAC/B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,qFAAqF;AACrF,OAAO,EAAE,SAAS,EAAuB,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,UAAU,EAAwB,MAAM,4BAA4B,CAAA;AAC7E,OAAO,EAAE,eAAe,EAA6B,MAAM,iCAAiC,CAAA;AAC5F,OAAO,EAAE,UAAU,EAAwB,MAAM,4BAA4B,CAAA;AAC7E,OAAO,EAAE,SAAS,EAAuB,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACzG,uFAAuF;AACvF,OAAO,EACN,gBAAgB,EAChB,cAAc,EACd,yBAAyB,EACzB,UAAU,EAEV,cAAc,GACd,MAAM,wBAAwB,CAAA;AAC/B,qEAAqE;AACrE,OAAO,EAEN,aAAa,EACb,kBAAkB,GAClB,MAAM,+BAA+B,CAAA;AACtC,2FAA2F;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,qFAAqF;AACrF,OAAO,EAMN,eAAe,GACf,MAAM,mCAAmC,CAAA;AAC1C,oFAAoF;AACpF,OAAO,EACN,QAAQ,EAER,eAAe,EACf,oBAAoB,EAGpB,sBAAsB,EACtB,+BAA+B,EAC/B,mBAAmB,EACnB,UAAU,EACV,WAAW,EAEX,eAAe,EACf,cAAc,EACd,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EAIxB,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,GACvB,MAAM,4BAA4B,CAAA;AACnC,sFAAsF;AACtF,qFAAqF;AACrF,2FAA2F;AAC3F,OAAO,EACN,gBAAgB,EAWhB,gBAAgB,EAKhB,aAAa,EACb,aAAa,EAGb,cAAc,EACd,mBAAmB,GACnB,MAAM,sBAAsB,CAAA;AAC7B,uFAAuF;AACvF,OAAO,EAGN,iBAAiB,EACjB,uBAAuB,EACvB,wBAAwB,GACxB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,QAAQ,EAAwB,MAAM,mBAAmB,CAAA;AAClE,qFAAqF;AACrF,OAAO,EAGN,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,GAClB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAEN,mBAAmB,EAEnB,iBAAiB,GACjB,MAAM,yBAAyB,CAAA;AAChC,sFAAsF;AACtF,OAAO,EAA2B,eAAe,EAAE,MAAM,qCAAqC,CAAA"}
|
package/dist/jsonld/project.d.ts
CHANGED
|
@@ -9,31 +9,49 @@
|
|
|
9
9
|
* server pre-render) produce the same structured data, and a snapshot diff is meaningful.
|
|
10
10
|
*
|
|
11
11
|
* DESIGN GOAL: MERGE, don't collide.
|
|
12
|
-
* A brand page usually already emits
|
|
13
|
-
* WordPress, the theme on Shopify, the framework on headless. If we emit a
|
|
14
|
-
* top-level `Article`, crawlers see two competing primary entities. Instead we
|
|
15
|
-
* `@graph` whose Article
|
|
16
|
-
* (the
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
12
|
+
* A brand page usually already emits a `WebPage` + `Article` (or `BlogPosting`) graph —
|
|
13
|
+
* Yoast on WordPress, the theme on Shopify, the framework on headless. If we emit a
|
|
14
|
+
* SECOND top-level `Article`, crawlers see two competing primary entities. Instead we
|
|
15
|
+
* emit a `@graph` whose WebPage / Article nodes carry the SAME `@id`s the page's own
|
|
16
|
+
* nodes use (the unmodified canonical URL for the page, the page URL + `#article` for
|
|
17
|
+
* the article — the Yoast convention) so a consumer MERGES our `reviewedBy` /
|
|
18
|
+
* `lastReviewed` / `dateModified` into the existing nodes by `@id` rather than
|
|
19
|
+
* duplicating them. Our own nodes are keyed to the CERTIFICATION instead (the verify
|
|
20
|
+
* URL, the expert's profile, the fixed CertREV organization id) — never under the host
|
|
21
|
+
* page's `@id` namespace — so they cannot clash with the host page's nodes.
|
|
20
22
|
*
|
|
21
23
|
* The Person we attach is the reviewing EXPERT (schema.org `reviewedBy`), which is the
|
|
22
24
|
* E-E-A-T signal CertREV exists to add — not the author. The author (content byline) is
|
|
23
25
|
* attached as `author` only when present + distinct.
|
|
26
|
+
*
|
|
27
|
+
* VISIBILITY: "Don't mark up content that is not visible to readers" (Google's
|
|
28
|
+
* structured-data guidelines) is a HARD rule here, not a nicety. Every field on this
|
|
29
|
+
* graph that mirrors something the card paints behind a visibility flag — the memo, the
|
|
30
|
+
* author byline, the reviewer photo — is gated by the SAME `resolveDisplay` the card
|
|
31
|
+
* uses (which folds in the render-def's subtractive hide-set), so the graph can never
|
|
32
|
+
* assert to a crawler what the reader cannot see. Re-deriving those flags here instead
|
|
33
|
+
* of importing the card's resolver is exactly how the two would silently drift.
|
|
24
34
|
*/
|
|
35
|
+
import type { CertRenderDef } from '../components/render-def.js';
|
|
25
36
|
import type { CertPayload } from '../contract/kernel.js';
|
|
26
37
|
/** A JSON-LD node/graph value. Loose by design — this is wire JSON, not a TS model. */
|
|
27
38
|
export type JsonLdValue = Record<string, unknown>;
|
|
28
39
|
export interface ProjectJsonLdOptions {
|
|
29
40
|
/**
|
|
30
41
|
* The canonical page URL the article renders on. Used to derive the primary
|
|
31
|
-
* Article node `@id` (`${pageUrl}#article`) so we merge
|
|
32
|
-
* instead of colliding. When omitted, the first
|
|
33
|
-
* used; when there is none either,
|
|
34
|
-
*
|
|
42
|
+
* WebPage / Article node `@id`s (`${pageUrl}` and `${pageUrl}#article`) so we merge
|
|
43
|
+
* into the host page's graph instead of colliding. When omitted, the first
|
|
44
|
+
* `subject.canonicalUrls` entry is used; when there is none either, the WebPage node
|
|
45
|
+
* is DROPPED and a CertREV-scoped article `@id` is derived from `verifyUrl` (still
|
|
46
|
+
* mergeable, just not aligned to a host node).
|
|
35
47
|
*/
|
|
36
48
|
readonly pageUrl?: string;
|
|
49
|
+
/**
|
|
50
|
+
* The brand render-def in force for THIS face. Read for its SUBTRACTIVE hide-set only
|
|
51
|
+
* (its theming half means nothing to structured data): a brand that hides the memo or
|
|
52
|
+
* the reviewer photo on the card must not have it published in the graph either.
|
|
53
|
+
*/
|
|
54
|
+
readonly renderDef?: CertRenderDef;
|
|
37
55
|
/**
|
|
38
56
|
* When true (default), emit the wrapping `{ "@context": "...", "@graph": [...] }`.
|
|
39
57
|
* When false, return just the array of nodes — for a caller that merges them into an
|
|
@@ -41,11 +59,26 @@ export interface ProjectJsonLdOptions {
|
|
|
41
59
|
*/
|
|
42
60
|
readonly wrapGraph?: boolean;
|
|
43
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* THE CertREV organization node `@id` — a hard CONSTANT, not a per-payload derivation.
|
|
64
|
+
*
|
|
65
|
+
* It used to be `${new URL(verifyUrl).origin}/#certrev-organization` under a comment
|
|
66
|
+
* claiming "one stable CertREV organization node across all pages", which it was not: a
|
|
67
|
+
* cert served from a second verify origin (a staging/preview host, a vanity verify
|
|
68
|
+
* domain, a future regional origin) minted a SECOND CertREV org node that would never
|
|
69
|
+
* merge with the first. CertREV is ONE real-world organization; its node id must not
|
|
70
|
+
* move because of which host happened to serve a verify link. Portal must emit this
|
|
71
|
+
* exact literal for the two halves to merge — see the package README.
|
|
72
|
+
*/
|
|
73
|
+
export declare const CERTREV_ORGANIZATION_ID = "https://certrev.com/#certrev-organization";
|
|
74
|
+
/** CertREV's canonical homepage — the `url` of the organization node above, same reasoning. */
|
|
75
|
+
export declare const CERTREV_ORGANIZATION_URL = "https://certrev.com";
|
|
44
76
|
/**
|
|
45
77
|
* Project the verified certification facts into a schema.org `@graph`.
|
|
46
78
|
*
|
|
47
79
|
* @param payload The cryptographically-verified payload (the same facts the badge renders).
|
|
48
|
-
* @param opts Page URL for @id alignment
|
|
80
|
+
* @param opts Page URL for @id alignment, the render-def whose hide-set gates
|
|
81
|
+
* visibility-bound fields, + graph-wrapping control.
|
|
49
82
|
* @returns A `{ "@context", "@graph" }` object (wrapGraph !== false) OR a bare
|
|
50
83
|
* array of nodes (wrapGraph === false) for merge-into-existing-graph callers.
|
|
51
84
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/jsonld/project.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/jsonld/project.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,uFAAuF;AACvF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEjD,MAAM,WAAW,oBAAoB;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;IAClC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAC5B;AAID;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,8CAA8C,CAAA;AAElF,+FAA+F;AAC/F,eAAO,MAAM,wBAAwB,wBAAwB,CAAA;AA2K7D;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,GAAE,oBAAyB,GAAG,WAAW,GAAG,WAAW,EAAE,CAepH;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,GAAE,oBAAyB,GAAG,MAAM,CAErG;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE/D"}
|
package/dist/jsonld/project.js
CHANGED
|
@@ -9,20 +9,45 @@
|
|
|
9
9
|
* server pre-render) produce the same structured data, and a snapshot diff is meaningful.
|
|
10
10
|
*
|
|
11
11
|
* DESIGN GOAL: MERGE, don't collide.
|
|
12
|
-
* A brand page usually already emits
|
|
13
|
-
* WordPress, the theme on Shopify, the framework on headless. If we emit a
|
|
14
|
-
* top-level `Article`, crawlers see two competing primary entities. Instead we
|
|
15
|
-
* `@graph` whose Article
|
|
16
|
-
* (the
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
12
|
+
* A brand page usually already emits a `WebPage` + `Article` (or `BlogPosting`) graph —
|
|
13
|
+
* Yoast on WordPress, the theme on Shopify, the framework on headless. If we emit a
|
|
14
|
+
* SECOND top-level `Article`, crawlers see two competing primary entities. Instead we
|
|
15
|
+
* emit a `@graph` whose WebPage / Article nodes carry the SAME `@id`s the page's own
|
|
16
|
+
* nodes use (the unmodified canonical URL for the page, the page URL + `#article` for
|
|
17
|
+
* the article — the Yoast convention) so a consumer MERGES our `reviewedBy` /
|
|
18
|
+
* `lastReviewed` / `dateModified` into the existing nodes by `@id` rather than
|
|
19
|
+
* duplicating them. Our own nodes are keyed to the CERTIFICATION instead (the verify
|
|
20
|
+
* URL, the expert's profile, the fixed CertREV organization id) — never under the host
|
|
21
|
+
* page's `@id` namespace — so they cannot clash with the host page's nodes.
|
|
20
22
|
*
|
|
21
23
|
* The Person we attach is the reviewing EXPERT (schema.org `reviewedBy`), which is the
|
|
22
24
|
* E-E-A-T signal CertREV exists to add — not the author. The author (content byline) is
|
|
23
25
|
* attached as `author` only when present + distinct.
|
|
26
|
+
*
|
|
27
|
+
* VISIBILITY: "Don't mark up content that is not visible to readers" (Google's
|
|
28
|
+
* structured-data guidelines) is a HARD rule here, not a nicety. Every field on this
|
|
29
|
+
* graph that mirrors something the card paints behind a visibility flag — the memo, the
|
|
30
|
+
* author byline, the reviewer photo — is gated by the SAME `resolveDisplay` the card
|
|
31
|
+
* uses (which folds in the render-def's subtractive hide-set), so the graph can never
|
|
32
|
+
* assert to a crawler what the reader cannot see. Re-deriving those flags here instead
|
|
33
|
+
* of importing the card's resolver is exactly how the two would silently drift.
|
|
24
34
|
*/
|
|
35
|
+
import { resolveDisplay } from '../components/format.js';
|
|
25
36
|
const SCHEMA_CONTEXT = 'https://schema.org';
|
|
37
|
+
/**
|
|
38
|
+
* THE CertREV organization node `@id` — a hard CONSTANT, not a per-payload derivation.
|
|
39
|
+
*
|
|
40
|
+
* It used to be `${new URL(verifyUrl).origin}/#certrev-organization` under a comment
|
|
41
|
+
* claiming "one stable CertREV organization node across all pages", which it was not: a
|
|
42
|
+
* cert served from a second verify origin (a staging/preview host, a vanity verify
|
|
43
|
+
* domain, a future regional origin) minted a SECOND CertREV org node that would never
|
|
44
|
+
* merge with the first. CertREV is ONE real-world organization; its node id must not
|
|
45
|
+
* move because of which host happened to serve a verify link. Portal must emit this
|
|
46
|
+
* exact literal for the two halves to merge — see the package README.
|
|
47
|
+
*/
|
|
48
|
+
export const CERTREV_ORGANIZATION_ID = 'https://certrev.com/#certrev-organization';
|
|
49
|
+
/** CertREV's canonical homepage — the `url` of the organization node above, same reasoning. */
|
|
50
|
+
export const CERTREV_ORGANIZATION_URL = 'https://certrev.com';
|
|
26
51
|
/**
|
|
27
52
|
* Strip the query AND fragment from a URL for a stable, mergeable @id. The Yoast / host
|
|
28
53
|
* convention keys the primary Article node on `{path}#article` with no query — so a page
|
|
@@ -33,105 +58,156 @@ function baseUrl(url) {
|
|
|
33
58
|
const cut = Math.min(...[url.indexOf('#'), url.indexOf('?')].filter((i) => i !== -1), url.length);
|
|
34
59
|
return url.slice(0, cut);
|
|
35
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The page this face renders on — the caller's `pageUrl` first (it knows the actual
|
|
63
|
+
* request), else the payload's primary canonical. Null when neither is known, which is
|
|
64
|
+
* the ONLY honest answer: we then assert nothing about any page.
|
|
65
|
+
*/
|
|
66
|
+
function pageBaseUrl(payload, opts) {
|
|
67
|
+
const page = opts.pageUrl ?? payload.subject.canonicalUrls[0];
|
|
68
|
+
return page ? baseUrl(page) : null;
|
|
69
|
+
}
|
|
36
70
|
/** Derive the primary Article `@id`, aligned to the Yoast/host convention when we can. */
|
|
37
71
|
function articleId(payload, opts) {
|
|
38
|
-
const page = opts
|
|
72
|
+
const page = pageBaseUrl(payload, opts);
|
|
39
73
|
if (page)
|
|
40
|
-
return `${
|
|
74
|
+
return `${page}#article`;
|
|
41
75
|
// No page URL known — derive a stable, mergeable id from the verify URL.
|
|
42
76
|
return `${baseUrl(payload.content.verifyUrl)}#article`;
|
|
43
77
|
}
|
|
44
|
-
/** CertREV-scoped node ids — namespaced under the
|
|
45
|
-
function reviewId(
|
|
46
|
-
return `${content.verifyUrl}#certrev-review`;
|
|
78
|
+
/** CertREV-scoped node ids — namespaced under the certification, so they never clash. */
|
|
79
|
+
function reviewId(payload) {
|
|
80
|
+
return `${payload.content.verifyUrl}#certrev-review`;
|
|
47
81
|
}
|
|
48
|
-
function expertId(
|
|
49
|
-
|
|
82
|
+
function expertId(payload) {
|
|
83
|
+
const { expert, verifyUrl } = payload.content;
|
|
84
|
+
return expert.profileUrl ? `${expert.profileUrl}#person` : `${verifyUrl}#expert`;
|
|
50
85
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
86
|
+
/**
|
|
87
|
+
* The `YYYY-MM-DD` head of an ISO-8601 instant, or null. `lastReviewed`'s schema.org
|
|
88
|
+
* range is `Date` — not `DateTime` — so the certified instant is truncated to its
|
|
89
|
+
* calendar day. String-sliced rather than `Date`-parsed on purpose: parsing would
|
|
90
|
+
* re-render the day in the runtime's timezone and break the determinism invariant
|
|
91
|
+
* (server and edge could disagree by a day).
|
|
92
|
+
*/
|
|
93
|
+
function isoDate(value) {
|
|
94
|
+
if (typeof value !== 'string')
|
|
95
|
+
return null;
|
|
96
|
+
const match = /^\d{4}-\d{2}-\d{2}/.exec(value);
|
|
97
|
+
return match ? match[0] : null;
|
|
60
98
|
}
|
|
61
99
|
/** Build the reviewing-expert Person node (the E-E-A-T signal). */
|
|
62
|
-
function expertPersonNode(payload) {
|
|
100
|
+
function expertPersonNode(payload, display) {
|
|
63
101
|
const { expert } = payload.content;
|
|
64
102
|
const node = {
|
|
65
103
|
'@type': 'Person',
|
|
66
|
-
'@id': expertId(payload
|
|
104
|
+
'@id': expertId(payload),
|
|
67
105
|
name: expert.displayName,
|
|
68
106
|
};
|
|
69
107
|
if (expert.profileUrl)
|
|
70
108
|
node.url = expert.profileUrl;
|
|
71
|
-
|
|
109
|
+
// The photo is a brand-toggleable (`free`) field: a face that hides the reviewer photo
|
|
110
|
+
// must not publish one either. `showExpertPhoto` already folds in the render-def's
|
|
111
|
+
// `reviewerPhoto` hide-set. Deliberately NOT gated on `badgeStyle` — a compact card
|
|
112
|
+
// omits the photo as a LAYOUT choice, and the same payload may render full elsewhere on
|
|
113
|
+
// the page, so keying the graph to it would make the projection slot-dependent.
|
|
114
|
+
if (display.showExpertPhoto && expert.photoUrl)
|
|
72
115
|
node.image = expert.photoUrl;
|
|
73
116
|
if (expert.credentials.length > 0) {
|
|
74
117
|
// hasCredential → EducationalOccupationalCredential per credential; also a flat
|
|
75
|
-
// honorificSuffix list for consumers that don't read hasCredential.
|
|
118
|
+
// honorificSuffix list for consumers that don't read hasCredential. `recognizedBy`
|
|
119
|
+
// (a Credential property whose expected type is Organization) names CertREV as the
|
|
120
|
+
// body that verified it — the structured-data form of the face's locked
|
|
121
|
+
// "Credential verified by CertREV" attribution, and what the reviewedby-schema
|
|
122
|
+
// validator looks for.
|
|
76
123
|
node.hasCredential = expert.credentials.map((c) => ({
|
|
77
124
|
'@type': 'EducationalOccupationalCredential',
|
|
78
125
|
name: c.fullName,
|
|
79
126
|
alternateName: c.abbreviation,
|
|
127
|
+
recognizedBy: { '@id': CERTREV_ORGANIZATION_ID },
|
|
80
128
|
}));
|
|
81
129
|
node.honorificSuffix = expert.credentials.map((c) => c.abbreviation).join(', ');
|
|
82
130
|
}
|
|
83
131
|
return node;
|
|
84
132
|
}
|
|
85
133
|
/** Build the CertREV organization node (publisher of the review credential). */
|
|
86
|
-
function certRevOrgNode(
|
|
87
|
-
let url = 'https://certrev.com';
|
|
88
|
-
try {
|
|
89
|
-
url = new URL(payload.content.verifyUrl).origin;
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
/* keep default */
|
|
93
|
-
}
|
|
134
|
+
function certRevOrgNode() {
|
|
94
135
|
return {
|
|
95
136
|
'@type': 'Organization',
|
|
96
|
-
'@id':
|
|
137
|
+
'@id': CERTREV_ORGANIZATION_ID,
|
|
97
138
|
name: 'CertREV',
|
|
98
|
-
url,
|
|
139
|
+
url: CERTREV_ORGANIZATION_URL,
|
|
99
140
|
};
|
|
100
141
|
}
|
|
101
142
|
/** Build the Review node binding the article to its expert review. */
|
|
102
|
-
function reviewNode(payload) {
|
|
143
|
+
function reviewNode(payload, opts, display) {
|
|
103
144
|
const node = {
|
|
104
145
|
'@type': 'Review',
|
|
105
|
-
'@id': reviewId(payload
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
146
|
+
'@id': reviewId(payload),
|
|
147
|
+
// The SAME opts the Article node is built from. Passing `{}` here (as this did)
|
|
148
|
+
// pointed `itemReviewed` at the payload's canonical while the Article node carried
|
|
149
|
+
// the caller's `pageUrl` — an orphaned reference on every page whose URL differs
|
|
150
|
+
// from `canonicalUrls[0]` (locale prefix, `www`, trailing slash, a syndicated copy).
|
|
151
|
+
itemReviewed: { '@id': articleId(payload, opts) },
|
|
152
|
+
author: { '@id': expertId(payload) },
|
|
153
|
+
publisher: { '@id': CERTREV_ORGANIZATION_ID },
|
|
109
154
|
datePublished: payload.content.certifiedAt,
|
|
110
155
|
url: payload.content.verifyUrl,
|
|
111
156
|
};
|
|
112
|
-
|
|
157
|
+
// The memo is the one piece of REVIEW PROSE on the face, and it is brand-toggleable.
|
|
158
|
+
// Publishing `reviewBody` off a hidden memo marks up text no reader can see.
|
|
159
|
+
if (display.showMemo && payload.content.memo)
|
|
113
160
|
node.reviewBody = payload.content.memo;
|
|
114
161
|
return node;
|
|
115
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* The host page node — where `lastReviewed` and `reviewedBy` actually belong: BOTH are
|
|
165
|
+
* schema.org `WebPage` properties (`lastReviewed`: "Date on which the content on this web
|
|
166
|
+
* page was last reviewed for accuracy and/or completeness", range `Date`; `reviewedBy`:
|
|
167
|
+
* "People or organizations that have reviewed the content on this web page", range
|
|
168
|
+
* `Organization`/`Person`). Keyed on the UNMODIFIED canonical URL, the Yoast WebPage
|
|
169
|
+
* convention, so this merges into the host's existing WebPage node instead of adding a
|
|
170
|
+
* competing one — and, like the Article node, it carries ONLY the certification-relevant
|
|
171
|
+
* properties so a merge never overwrites the host's richer page fields.
|
|
172
|
+
*
|
|
173
|
+
* Returns null when no page URL is known: `lastReviewed` on a page we cannot name would
|
|
174
|
+
* either be a dangling node or — worse — an assertion about the CertREV verify page,
|
|
175
|
+
* which is not the reviewed content.
|
|
176
|
+
*/
|
|
177
|
+
function webPageNode(payload, opts) {
|
|
178
|
+
const id = pageBaseUrl(payload, opts);
|
|
179
|
+
if (!id)
|
|
180
|
+
return null;
|
|
181
|
+
const lastReviewed = isoDate(payload.content.certifiedAt);
|
|
182
|
+
return {
|
|
183
|
+
'@type': 'WebPage',
|
|
184
|
+
'@id': id,
|
|
185
|
+
url: id,
|
|
186
|
+
...(lastReviewed ? { lastReviewed } : {}),
|
|
187
|
+
reviewedBy: { '@id': expertId(payload) },
|
|
188
|
+
};
|
|
189
|
+
}
|
|
116
190
|
/**
|
|
117
191
|
* The primary Article node — carries the SAME `@id` the host page's article uses so a
|
|
118
192
|
* consumer merges our `reviewedBy` / `dateModified` into the existing node rather than
|
|
119
193
|
* duplicating the primary entity. We deliberately emit ONLY the certification-relevant
|
|
120
|
-
* properties (reviewedBy, dateModified, plus author when distinct) — not
|
|
121
|
-
* image, etc. — so we never overwrite the host's richer Article fields
|
|
194
|
+
* properties (reviewedBy, dateModified, plus author when distinct AND shown) — not
|
|
195
|
+
* headline, body, image, etc. — so we never overwrite the host's richer Article fields
|
|
196
|
+
* on merge.
|
|
122
197
|
*/
|
|
123
|
-
function articleNode(payload, opts) {
|
|
198
|
+
function articleNode(payload, opts, display) {
|
|
124
199
|
const { content } = payload;
|
|
125
200
|
const node = {
|
|
126
201
|
'@type': 'Article',
|
|
127
202
|
'@id': articleId(payload, opts),
|
|
128
|
-
reviewedBy: { '@id': expertId(
|
|
129
|
-
review: { '@id': reviewId(
|
|
203
|
+
reviewedBy: { '@id': expertId(payload) },
|
|
204
|
+
review: { '@id': reviewId(payload) },
|
|
130
205
|
};
|
|
131
206
|
if (content.contentModifiedAt)
|
|
132
207
|
node.dateModified = content.contentModifiedAt;
|
|
133
|
-
// Author only when
|
|
134
|
-
|
|
208
|
+
// Author only when the face SHOWS the byline (a `free`, brand-toggleable field) and it
|
|
209
|
+
// is present + distinct from the expert (don't double-attribute).
|
|
210
|
+
if (display.showAuthor && content.author.name && content.author.name !== content.expert.displayName) {
|
|
135
211
|
const author = { '@type': 'Person', name: content.author.name };
|
|
136
212
|
if (content.author.title)
|
|
137
213
|
author.jobTitle = content.author.title;
|
|
@@ -143,16 +219,23 @@ function articleNode(payload, opts) {
|
|
|
143
219
|
* Project the verified certification facts into a schema.org `@graph`.
|
|
144
220
|
*
|
|
145
221
|
* @param payload The cryptographically-verified payload (the same facts the badge renders).
|
|
146
|
-
* @param opts Page URL for @id alignment
|
|
222
|
+
* @param opts Page URL for @id alignment, the render-def whose hide-set gates
|
|
223
|
+
* visibility-bound fields, + graph-wrapping control.
|
|
147
224
|
* @returns A `{ "@context", "@graph" }` object (wrapGraph !== false) OR a bare
|
|
148
225
|
* array of nodes (wrapGraph === false) for merge-into-existing-graph callers.
|
|
149
226
|
*/
|
|
150
227
|
export function projectCertJsonLd(payload, opts = {}) {
|
|
228
|
+
// ONE resolution of the visibility flags, shared by every node — the same resolver
|
|
229
|
+
// (and therefore the same answer) the rendered card uses. No accent override: theming
|
|
230
|
+
// is meaningless to structured data.
|
|
231
|
+
const display = resolveDisplay(payload.content.display, undefined, opts.renderDef);
|
|
232
|
+
const page = webPageNode(payload, opts);
|
|
151
233
|
const nodes = [
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
234
|
+
...(page ? [page] : []),
|
|
235
|
+
articleNode(payload, opts, display),
|
|
236
|
+
reviewNode(payload, opts, display),
|
|
237
|
+
expertPersonNode(payload, display),
|
|
238
|
+
certRevOrgNode(),
|
|
156
239
|
];
|
|
157
240
|
if (opts.wrapGraph === false)
|
|
158
241
|
return nodes;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/jsonld/project.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/jsonld/project.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Avatar right-sizing
|
|
2
|
+
* Avatar right-sizing — the SINGLE source of truth for how a cert contributor/expert
|
|
3
3
|
* headshot URL is rewritten to a display-sized derivative. Referenced by the cert MODAL (TS,
|
|
4
4
|
* cert-modal-view.ts), mirrored by the Liquid snippet (certrev-avatar-url.liquid), and by the
|
|
5
|
-
* Cloudflare size-allowlist WAF rule
|
|
5
|
+
* Cloudflare size-allowlist WAF rule. Keep the three in lockstep — the
|
|
6
6
|
* `avatar-resize.test.ts` `SIZES`/param assertions are the tripwire.
|
|
7
7
|
*
|
|
8
|
-
* Architecture
|
|
8
|
+
* Architecture: the signed cert envelope carries ONE canonical full-res photoUrl
|
|
9
9
|
* (also the JSON-LD Person.image — never shrunk). Display sizing is a PRESENTATION concern, applied
|
|
10
10
|
* here via Cloudflare Image Resizing on the same zone as assets.certrev.com:
|
|
11
11
|
* https://assets.certrev.com/cdn-cgi/image/width=W,height=W,fit=cover,format=auto/<path>
|
|
@@ -13,19 +13,19 @@
|
|
|
13
13
|
* allowlist is needed; format=auto serves webp/avif; the transform is cached + billed per-unique.
|
|
14
14
|
*
|
|
15
15
|
* ENABLEMENT GATE (`AVATAR_RESIZE_ENABLED`): OFF until Cloudflare Image Resizing is enabled on the
|
|
16
|
-
* certrev.com zone AND a real photo is verified to resize
|
|
16
|
+
* certrev.com zone AND a real photo is verified to resize. While OFF, avatars use
|
|
17
17
|
* the raw canonical URL — byte-identical to today's behavior — so the extension can deploy for the
|
|
18
18
|
* font de-block with ZERO avatar regression (a cdn-cgi/image URL 404s → initials fallback if the
|
|
19
19
|
* feature isn't live). Going live is a one-line flip here + in certrev-avatar-url.liquid, then the
|
|
20
|
-
* raw branch is removed (flag-debt:
|
|
20
|
+
* raw branch is removed (flag-debt: the avatar go-live follow-up).
|
|
21
21
|
*/
|
|
22
22
|
/**
|
|
23
|
-
* Enablement gate — LIVE as of 2026-07-20
|
|
24
|
-
* is enabled on the certrev.com zone (`image_resizing = on`), and the size-allowlist WAF rule
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* Enablement gate — LIVE as of 2026-07-20. Cloudflare Image Resizing (Transformations)
|
|
24
|
+
* is enabled on the certrev.com zone (`image_resizing = on`), and the size-allowlist WAF rule is
|
|
25
|
+
* verified enforcing on `/cdn-cgi/image/` paths — a real photo resizes (`width=84` → 200
|
|
26
|
+
* image/jpeg) while a disallowed size (`width=999`) is 403-blocked.
|
|
27
27
|
* Kept as a one-line kill-switch: flip back to `false` + redeploy to instantly revert to raw URLs if
|
|
28
|
-
* resizing ever misbehaves (removing the raw branch entirely is the flag-debt follow-up
|
|
28
|
+
* resizing ever misbehaves (removing the raw branch entirely is the flag-debt follow-up).
|
|
29
29
|
*/
|
|
30
30
|
export declare const AVATAR_RESIZE_ENABLED = true;
|
|
31
31
|
/**
|