@certrev/cert-block 1.2.1 → 2.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +230 -0
  2. package/README.md +135 -8
  3. package/dist/builder/card-contract.d.ts +143 -0
  4. package/dist/builder/card-contract.d.ts.map +1 -0
  5. package/dist/builder/card-contract.js +262 -0
  6. package/dist/builder/card-contract.js.map +1 -0
  7. package/dist/builder/card-report.d.ts +37 -0
  8. package/dist/builder/card-report.d.ts.map +1 -0
  9. package/dist/builder/card-report.js +43 -0
  10. package/dist/builder/card-report.js.map +1 -0
  11. package/dist/builder/cert-review-card.d.ts +7 -5
  12. package/dist/builder/cert-review-card.d.ts.map +1 -1
  13. package/dist/builder/cert-review-card.js +227 -18
  14. package/dist/builder/cert-review-card.js.map +1 -1
  15. package/dist/builder/contract-card.d.ts +53 -0
  16. package/dist/builder/contract-card.d.ts.map +1 -0
  17. package/dist/builder/contract-card.js +141 -0
  18. package/dist/builder/contract-card.js.map +1 -0
  19. package/dist/builder/delivered-face.d.ts +83 -0
  20. package/dist/builder/delivered-face.d.ts.map +1 -0
  21. package/dist/builder/delivered-face.js +135 -0
  22. package/dist/builder/delivered-face.js.map +1 -0
  23. package/dist/builder/delivery-runtime.d.ts +15 -1
  24. package/dist/builder/delivery-runtime.d.ts.map +1 -1
  25. package/dist/builder/delivery-runtime.js +26 -1
  26. package/dist/builder/delivery-runtime.js.map +1 -1
  27. package/dist/builder/index.d.ts +7 -1
  28. package/dist/builder/index.d.ts.map +1 -1
  29. package/dist/builder/index.js +8 -1
  30. package/dist/builder/index.js.map +1 -1
  31. package/dist/builder/ssr-seed.d.ts +31 -1
  32. package/dist/builder/ssr-seed.d.ts.map +1 -1
  33. package/dist/builder/ssr-seed.js +38 -4
  34. package/dist/builder/ssr-seed.js.map +1 -1
  35. package/dist/index.d.ts +3 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +3 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/reader/reader-css.generated.d.ts +1 -1
  40. package/dist/reader/reader-css.generated.d.ts.map +1 -1
  41. package/dist/reader/reader-css.generated.js +1 -1
  42. package/dist/reader/reader-css.generated.js.map +1 -1
  43. package/dist/reader/render.d.ts.map +1 -1
  44. package/dist/reader/render.js +71 -34
  45. package/dist/reader/render.js.map +1 -1
  46. package/dist/reader/tokens.d.ts +2 -0
  47. package/dist/reader/tokens.d.ts.map +1 -1
  48. package/dist/reader/tokens.js +28 -6
  49. package/dist/reader/tokens.js.map +1 -1
  50. package/dist/verify/get-verified-delivery.d.ts +56 -0
  51. package/dist/verify/get-verified-delivery.d.ts.map +1 -1
  52. package/dist/verify/get-verified-delivery.js +49 -4
  53. package/dist/verify/get-verified-delivery.js.map +1 -1
  54. package/dist/verify/key-set.d.ts +67 -0
  55. package/dist/verify/key-set.d.ts.map +1 -0
  56. package/dist/verify/key-set.js +190 -0
  57. package/dist/verify/key-set.js.map +1 -0
  58. package/package.json +115 -112
  59. package/src/builder/card-contract.ts +314 -0
  60. package/src/builder/card-report.ts +61 -0
  61. package/src/builder/cert-review-card.tsx +332 -26
  62. package/src/builder/contract-card.tsx +159 -0
  63. package/src/builder/delivered-face.ts +156 -0
  64. package/src/builder/delivery-runtime.ts +32 -1
  65. package/src/builder/index.tsx +16 -1
  66. package/src/builder/ssr-seed.ts +56 -4
  67. package/src/index.ts +12 -0
  68. package/src/reader/reader-css.generated.ts +1 -1
  69. package/src/reader/reader.css +297 -2
  70. package/src/reader/render.ts +87 -38
  71. package/src/reader/tokens.ts +29 -6
  72. package/src/verify/get-verified-delivery.ts +85 -4
  73. package/src/verify/key-set.ts +225 -0
@@ -0,0 +1,141 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The contract-1 card face (2.0.0): one Builder block printing CertREV's server-rendered card verbatim.
4
+ *
5
+ * The block's wrapper holds, as its `dangerouslySetInnerHTML`, exactly `cardHtml(card, names)`: the block's part hosts
6
+ * in page order and, in the block holding the document's first part, the head fragment, both CertREV dialog hosts and
7
+ * the runtime's tag (`card-contract.ts`).
8
+ * Each host is a `<certrev-card>` with a declarative shadow root (`<template shadowrootmode="open">`), which the
9
+ * browser's HTML parser attaches as it reads the server's page: the card is complete with JavaScript off, and no page
10
+ * rule reaches inside it. React never reconciles the hosts (they are not React children), so the hosted runtime may
11
+ * remove a revoked placement's hosts without React noticing.
12
+ *
13
+ * What the parser does not do, the card does once it has mounted (`useCardRoots`):
14
+ * - ATTACH ROOTS the parser never saw. A card React inserts (an in-app navigation, Builder's editor, React's own
15
+ * re-render after a hydration error) gets its HTML through `innerHTML`, which leaves a declarative template inert.
16
+ * The wrapper is re-parsed with `setHTMLUnsafe`, which attaches declarative roots; a browser without it (or without
17
+ * declarative shadow DOM at all) gets each root attached from its template (`attachShadow` with the template's mode
18
+ * and options, then the template's content). A host that already has its root is left alone, so a hydrated card,
19
+ * whose roots the parser attached, is not touched.
20
+ * - LOAD THE RUNTIME a React-inserted `<script>` never runs: once per page, into `document.head`, with its SRI
21
+ * integrity and `crossorigin="anonymous"`, unless the runtime is already running (its global guard) or already
22
+ * injected. Only the block that prints the runtime's tag loads it (`cardCarriesAssets`). The server's HTML carries
23
+ * the same tag in that block's wrapper, which the parser runs; the runtime's init is idempotent, so it starts once.
24
+ * Both run in a layout effect, before the browser paints the inserted card, so a client-mounted card never shows an
25
+ * empty frame.
26
+ *
27
+ * ONE BUILDER RULE, for the sidebar card only. The card's rail is a right float that the article's copy flows around
28
+ * (the Appearance preview's layout, and every other CMS's). On a Builder page the block sits in Builder's own wrapper,
29
+ * a flex item of the flex column that holds the entry's blocks, and a flex item contains its floats: the block spans
30
+ * the column at the rail's height and no copy sits beside it. `BUILDER_CARD_FLOW_CSS` lets the float out of Builder's
31
+ * wrappers, as 1.2.0-canary.6 did for the reader face: in a column that holds a printed rail, above the card's own
32
+ * 820px breakpoint (below it the rail stacks as a block), the column lays its blocks out as blocks and still contains
33
+ * the float, and the rail's block and the card wrapper become plain blocks. It reaches nothing inside the card (the
34
+ * shadow root is out of its reach) and nothing on a page without a rail, or in a browser without `:has()`.
35
+ */
36
+ import * as React from 'react';
37
+ import { cardCarriesAssets, cardHtml } from './card-contract.js';
38
+ import { SEED_ATTRIBUTE } from './ssr-seed.js';
39
+ /** The global the hosted runtime sets once it has started (`Symbol.for('certrev.card-runtime')` on `document`). */
40
+ const RUNTIME_GUARD = Symbol.for('certrev.card-runtime');
41
+ /** Marks the runtime tag this package injected into `document.head`. */
42
+ const INJECTED_RUNTIME_ATTRIBUTE = 'data-certrev-card-runtime';
43
+ /** A layout effect in a browser; nothing on a server (where React would only warn about it). */
44
+ const useBrowserLayoutEffect = typeof document === 'undefined' ? () => { } : React.useLayoutEffect;
45
+ /** Whether this host still holds an unattached declarative root. */
46
+ function pendingTemplate(host) {
47
+ if (host.shadowRoot)
48
+ return null;
49
+ for (const child of Array.from(host.children)) {
50
+ if (child instanceof HTMLTemplateElement && child.hasAttribute('shadowrootmode'))
51
+ return child;
52
+ }
53
+ return null;
54
+ }
55
+ /** Attach one host's root from its template, as the parser would have: its mode, its options, its content. */
56
+ function attachFromTemplate(host, template) {
57
+ const mode = template.getAttribute('shadowrootmode') === 'closed' ? 'closed' : 'open';
58
+ const init = {
59
+ mode,
60
+ delegatesFocus: template.hasAttribute('shadowrootdelegatesfocus'),
61
+ ...(template.hasAttribute('shadowrootclonable') ? { clonable: true } : {}),
62
+ ...(template.hasAttribute('shadowrootserializable') ? { serializable: true } : {}),
63
+ };
64
+ const root = host.attachShadow(init);
65
+ root.appendChild(template.content);
66
+ template.remove();
67
+ }
68
+ /**
69
+ * Give every host in `wrapper` its declarative root: a no-op where the parser attached them all; else the wrapper is
70
+ * re-parsed from `html` with `setHTMLUnsafe` where the browser has it, or each pending root is attached from its
71
+ * template. Returns how many hosts were pending (for tests and the replica's evidence).
72
+ */
73
+ export function attachCardRoots(wrapper, html) {
74
+ const pending = Array.from(wrapper.querySelectorAll('certrev-card')).filter((host) => pendingTemplate(host) !== null);
75
+ if (pending.length === 0)
76
+ return 0;
77
+ const parse = wrapper.setHTMLUnsafe;
78
+ if (typeof parse === 'function') {
79
+ parse.call(wrapper, html);
80
+ // A browser whose setHTMLUnsafe predates declarative roots leaves templates behind: attach those by hand.
81
+ for (const host of Array.from(wrapper.querySelectorAll('certrev-card'))) {
82
+ const template = pendingTemplate(host);
83
+ if (template)
84
+ attachFromTemplate(host, template);
85
+ }
86
+ return pending.length;
87
+ }
88
+ for (const host of pending) {
89
+ const template = pendingTemplate(host);
90
+ if (template)
91
+ attachFromTemplate(host, template);
92
+ }
93
+ return pending.length;
94
+ }
95
+ /** Load the document's runtime once per page: unless it already runs, or this package already injected it. */
96
+ export function injectCardRuntime(card) {
97
+ const runtime = card.assets.runtime;
98
+ if (!runtime || typeof document === 'undefined' || !document.head)
99
+ return;
100
+ if (document[RUNTIME_GUARD])
101
+ return;
102
+ for (const script of Array.from(document.head.querySelectorAll(`script[${INJECTED_RUNTIME_ATTRIBUTE}]`))) {
103
+ if (script.getAttribute('src') === runtime.src)
104
+ return;
105
+ }
106
+ const script = document.createElement('script');
107
+ // Attributes, not IDL properties: the markup is then exactly the server's tag, wherever a property is not reflected.
108
+ script.setAttribute('src', runtime.src);
109
+ script.setAttribute('integrity', runtime.integrity);
110
+ script.setAttribute('crossorigin', 'anonymous');
111
+ script.defer = true;
112
+ script.setAttribute(INJECTED_RUNTIME_ATTRIBUTE, '');
113
+ document.head.appendChild(script);
114
+ }
115
+ /** Builder's wrappers around a printed rail let its float out (the header comment). */
116
+ export const BUILDER_CARD_FLOW_CSS = '@media not all and (max-width: 820px){' +
117
+ '.builder-blocks:has(> .builder-block > [data-certrev-face="card"] > certrev-card[data-certrev-part="rail"]){display:flow-root !important}' +
118
+ '.builder-blocks > .builder-block:has(> [data-certrev-face="card"] > certrev-card[data-certrev-part="rail"]){display:block !important}' +
119
+ '.builder-blocks > .builder-block > [data-certrev-face="card"]:has(> certrev-card[data-certrev-part="rail"]){display:block !important}' +
120
+ '}';
121
+ export function ContractCardFace(props) {
122
+ const ref = React.useRef(null);
123
+ const html = cardHtml(props.card, props.names);
124
+ const { card } = props;
125
+ const carriesAssets = cardCarriesAssets(card, props.names);
126
+ useBrowserLayoutEffect(() => {
127
+ const wrapper = ref.current;
128
+ if (!wrapper)
129
+ return;
130
+ attachCardRoots(wrapper, html);
131
+ if (carriesAssets)
132
+ injectCardRuntime(card);
133
+ }, [html, card, carriesAssets]);
134
+ const wrapper = (_jsx("div", { ref: ref, "data-certrev-cert-chrome": "", "data-certrev-delivery": "", "data-certrev-face": "card", "data-certrev-contract": String(props.card.contract), "data-certrev-def-version": props.card.defVersion, [SEED_ATTRIBUTE]: props.seed,
135
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: CertREV's card document, printed only after it hashes to the digest the verified envelope signs
136
+ dangerouslySetInnerHTML: { __html: html } }));
137
+ if (!props.names.includes('rail'))
138
+ return wrapper;
139
+ return (_jsxs(_Fragment, { children: [wrapper, _jsx("style", { "data-certrev-builder-flow": "", dangerouslySetInnerHTML: { __html: BUILDER_CARD_FLOW_CSS } })] }));
140
+ }
141
+ //# sourceMappingURL=contract-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-card.js","sourceRoot":"","sources":["../../src/builder/contract-card.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────────────────────
3
+ * The delivered face gate: when the Builder card may paint the server's markup (1.3.0)
4
+ * ─────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * The Delivery API may serve a server-rendered face (`face: { html, css }`) beside the signed artifact. That sibling
7
+ * is UNSIGNED. What binds it is the signed `payload.content.faceDigest` (cert-contract 0.6.0): the lowercase hex sha256
8
+ * of the markup's and the stylesheet's UTF-8 bytes, for one saved design version. The card paints the delivered face
9
+ * only when ALL of these hold:
10
+ *
11
+ * - the verdict is `render` (the caller has already checked that);
12
+ * - the card is not pinned (a design pin keeps 1.2.1's pinned face; the caller checks that);
13
+ * - the block shows the whole card: `part` is not `'header'` or `'memo'`, so a split banner never paints the face
14
+ * twice (`showsWholeCard`);
15
+ * - the verified content carries a digest it understands (`v: 1`, two 64-char lowercase hex digests, a numeric
16
+ * `defVersion`);
17
+ * - a well-formed face was served;
18
+ * - a def was served and its `v` IS the digest's `defVersion` (markup rendered for another design version is not
19
+ * this design);
20
+ * - sha256(html) and sha256(css) equal the signed values.
21
+ *
22
+ * In every other case the card paints exactly what 1.2.1 paints. Markup whose bytes do not hash to the signed digest
23
+ * is never painted, and a WebCrypto failure counts as a mismatch, never as a pass.
24
+ *
25
+ * Hashing uses WebCrypto (`crypto.subtle.digest`), the global the kernel verifies with, so it runs wherever the card
26
+ * already verifies (Node SSR, edge runtimes, a browser in a secure context). It is react-free; the card owns the
27
+ * suspension around the async digest.
28
+ */
29
+ import type { CertContent, CertFaceDigest } from '../contract/kernel.js';
30
+ import type { DeliveredFace } from '../verify/get-verified-delivery.js';
31
+ import type { CertChromeRenderDef } from './cert-chrome-data.js';
32
+ /**
33
+ * Whether a block shows the whole card, by `renderCertBlock`'s own rule: only `'header'` and `'memo'` split the
34
+ * banner; absent, `'full'`, null or anything unrecognised is the full card. A split block keeps its own part even
35
+ * when a delivered face matches, because the delivered face is a whole design.
36
+ */
37
+ export declare function showsWholeCard(part: unknown): boolean;
38
+ /** The signed digest, accepted only in the shape cert-contract 0.6.0 defines; anything else is null. */
39
+ export declare function acceptedFaceDigest(content: CertContent | null | undefined): CertFaceDigest | null;
40
+ /**
41
+ * The synchronous half of the gate: the digest to hash the face against, or null when the delivered face cannot
42
+ * paint whatever its bytes are (no digest, no face, no def, or a def of another version). The card hashes only when
43
+ * this is non-null, so a delivery without a face never suspends a second time.
44
+ */
45
+ export declare function deliveredFaceDigest(content: CertContent | null | undefined, renderDef: CertChromeRenderDef | null, face: DeliveredFace | null | undefined): CertFaceDigest | null;
46
+ /** Lowercase hex sha256 of a string's UTF-8 bytes, via WebCrypto. */
47
+ export declare function sha256HexUtf8(text: string): Promise<string>;
48
+ /** The two digests of a delivered face. */
49
+ export interface DeliveredFaceHashes {
50
+ readonly html: string;
51
+ readonly css: string;
52
+ }
53
+ /** Hash both halves. Never rejects: a WebCrypto failure (none in an insecure context) is null, which never matches. */
54
+ export declare function hashDeliveredFace(face: DeliveredFace): Promise<DeliveredFaceHashes | null>;
55
+ /** The asynchronous half's verdict: both halves hash to the signed values. */
56
+ export declare function deliveredFaceMatches(hashes: DeliveredFaceHashes | null, digest: CertFaceDigest): boolean;
57
+ /** The markup the card paints for a matching face: the stylesheet first, then the face. */
58
+ export declare function deliveredFaceHtml(face: DeliveredFace): string;
59
+ /**
60
+ * Make room in a memo of pending-then-settled work: drop the oldest SETTLED entry once the memo is full, never a pending
61
+ * one. A render suspends on a pending entry and, when React retries it, must find that entry again; evicting it would make
62
+ * every retry start (and suspend on) a new one, so a page with more documents than the memo holds would never finish
63
+ * (break-it B1). With nothing settled to drop, the memo grows past its bound until entries settle.
64
+ */
65
+ export declare function evictOldestSettled<V extends {
66
+ settled?: unknown;
67
+ }>(memo: Map<string, V>, max: number): void;
68
+ /** One face's hashing: the pending digest, and its answer once settled. */
69
+ export interface FaceHashMemo {
70
+ readonly pending: Promise<DeliveredFaceHashes | null>;
71
+ settled?: {
72
+ readonly hashes: DeliveredFaceHashes | null;
73
+ };
74
+ }
75
+ /**
76
+ * The memoized hashing of one face, keyed by its CONTENT (both strings), not by the object: the browser parses a seed
77
+ * from the page afresh on each render, and a server's cached read and a seed of the same face share one entry. A
78
+ * failure settles to null and stays settled (a context with no WebCrypto does not gain one).
79
+ */
80
+ export declare function faceHashesFor(face: DeliveredFace): FaceHashMemo;
81
+ /** Test seam: forget every face's hashes (a new process). */
82
+ export declare function forgetFaceHashesForTests(): void;
83
+ //# sourceMappingURL=delivered-face.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delivered-face.d.ts","sourceRoot":"","sources":["../../src/builder/delivered-face.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AACvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAIhE;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAErD;AAED,wGAAwG;AACxG,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,cAAc,GAAG,IAAI,CAQjG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,EACvC,SAAS,EAAE,mBAAmB,GAAG,IAAI,EACrC,IAAI,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GACpC,cAAc,GAAG,IAAI,CAKvB;AAED,qEAAqE;AACrE,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKjE;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACpB;AAED,uHAAuH;AACvH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAOhG;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAExG;AAED,2FAA2F;AAC3F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAE7D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAQ3G;AAED,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAA;IACrD,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAA;KAAE,CAAA;CACzD;AAMD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CAc/D;AAED,6DAA6D;AAC7D,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────────────────────
3
+ * The delivered face gate: when the Builder card may paint the server's markup (1.3.0)
4
+ * ─────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * The Delivery API may serve a server-rendered face (`face: { html, css }`) beside the signed artifact. That sibling
7
+ * is UNSIGNED. What binds it is the signed `payload.content.faceDigest` (cert-contract 0.6.0): the lowercase hex sha256
8
+ * of the markup's and the stylesheet's UTF-8 bytes, for one saved design version. The card paints the delivered face
9
+ * only when ALL of these hold:
10
+ *
11
+ * - the verdict is `render` (the caller has already checked that);
12
+ * - the card is not pinned (a design pin keeps 1.2.1's pinned face; the caller checks that);
13
+ * - the block shows the whole card: `part` is not `'header'` or `'memo'`, so a split banner never paints the face
14
+ * twice (`showsWholeCard`);
15
+ * - the verified content carries a digest it understands (`v: 1`, two 64-char lowercase hex digests, a numeric
16
+ * `defVersion`);
17
+ * - a well-formed face was served;
18
+ * - a def was served and its `v` IS the digest's `defVersion` (markup rendered for another design version is not
19
+ * this design);
20
+ * - sha256(html) and sha256(css) equal the signed values.
21
+ *
22
+ * In every other case the card paints exactly what 1.2.1 paints. Markup whose bytes do not hash to the signed digest
23
+ * is never painted, and a WebCrypto failure counts as a mismatch, never as a pass.
24
+ *
25
+ * Hashing uses WebCrypto (`crypto.subtle.digest`), the global the kernel verifies with, so it runs wherever the card
26
+ * already verifies (Node SSR, edge runtimes, a browser in a secure context). It is react-free; the card owns the
27
+ * suspension around the async digest.
28
+ */
29
+ const SHA256_HEX = /^[0-9a-f]{64}$/;
30
+ /**
31
+ * Whether a block shows the whole card, by `renderCertBlock`'s own rule: only `'header'` and `'memo'` split the
32
+ * banner; absent, `'full'`, null or anything unrecognised is the full card. A split block keeps its own part even
33
+ * when a delivered face matches, because the delivered face is a whole design.
34
+ */
35
+ export function showsWholeCard(part) {
36
+ return part !== 'header' && part !== 'memo';
37
+ }
38
+ /** The signed digest, accepted only in the shape cert-contract 0.6.0 defines; anything else is null. */
39
+ export function acceptedFaceDigest(content) {
40
+ const digest = content?.faceDigest;
41
+ if (typeof digest !== 'object' || digest === null || Array.isArray(digest))
42
+ return null;
43
+ const { v, defVersion, html, css } = digest;
44
+ if (v !== 1 || typeof defVersion !== 'number' || !Number.isFinite(defVersion))
45
+ return null;
46
+ if (typeof html !== 'string' || !SHA256_HEX.test(html))
47
+ return null;
48
+ if (typeof css !== 'string' || !SHA256_HEX.test(css))
49
+ return null;
50
+ return { v: 1, defVersion, html, css };
51
+ }
52
+ /**
53
+ * The synchronous half of the gate: the digest to hash the face against, or null when the delivered face cannot
54
+ * paint whatever its bytes are (no digest, no face, no def, or a def of another version). The card hashes only when
55
+ * this is non-null, so a delivery without a face never suspends a second time.
56
+ */
57
+ export function deliveredFaceDigest(content, renderDef, face) {
58
+ if (!face)
59
+ return null;
60
+ const digest = acceptedFaceDigest(content);
61
+ if (!digest || !renderDef || renderDef.v !== digest.defVersion)
62
+ return null;
63
+ return digest;
64
+ }
65
+ /** Lowercase hex sha256 of a string's UTF-8 bytes, via WebCrypto. */
66
+ export async function sha256HexUtf8(text) {
67
+ const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text)));
68
+ let hex = '';
69
+ for (const byte of digest)
70
+ hex += byte.toString(16).padStart(2, '0');
71
+ return hex;
72
+ }
73
+ /** Hash both halves. Never rejects: a WebCrypto failure (none in an insecure context) is null, which never matches. */
74
+ export async function hashDeliveredFace(face) {
75
+ try {
76
+ const [html, css] = await Promise.all([sha256HexUtf8(face.html), sha256HexUtf8(face.css)]);
77
+ return { html, css };
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ /** The asynchronous half's verdict: both halves hash to the signed values. */
84
+ export function deliveredFaceMatches(hashes, digest) {
85
+ return hashes !== null && hashes.html === digest.html && hashes.css === digest.css;
86
+ }
87
+ /** The markup the card paints for a matching face: the stylesheet first, then the face. */
88
+ export function deliveredFaceHtml(face) {
89
+ return `<style>${face.css}</style>${face.html}`;
90
+ }
91
+ /**
92
+ * Make room in a memo of pending-then-settled work: drop the oldest SETTLED entry once the memo is full, never a pending
93
+ * one. A render suspends on a pending entry and, when React retries it, must find that entry again; evicting it would make
94
+ * every retry start (and suspend on) a new one, so a page with more documents than the memo holds would never finish
95
+ * (break-it B1). With nothing settled to drop, the memo grows past its bound until entries settle.
96
+ */
97
+ export function evictOldestSettled(memo, max) {
98
+ if (memo.size < max)
99
+ return;
100
+ for (const [key, entry] of memo) {
101
+ if (entry.settled !== undefined) {
102
+ memo.delete(key);
103
+ return;
104
+ }
105
+ }
106
+ }
107
+ /** How many faces' hashes are kept (insertion order; the oldest goes first). A page or a brand has one or a few. */
108
+ const FACE_HASH_MEMO_MAX = 64;
109
+ const faceHashMemo = new Map();
110
+ /**
111
+ * The memoized hashing of one face, keyed by its CONTENT (both strings), not by the object: the browser parses a seed
112
+ * from the page afresh on each render, and a server's cached read and a seed of the same face share one entry. A
113
+ * failure settles to null and stays settled (a context with no WebCrypto does not gain one).
114
+ */
115
+ export function faceHashesFor(face) {
116
+ const key = `${face.html.length}:${face.html}${face.css}`;
117
+ const known = faceHashMemo.get(key);
118
+ if (known)
119
+ return known;
120
+ const fresh = {
121
+ pending: hashDeliveredFace(face).then((hashes) => {
122
+ fresh.settled = { hashes };
123
+ return hashes;
124
+ }),
125
+ };
126
+ // 2.0.0: never a pending entry (break-it B1: more faces than the memo holds made every retry suspend again).
127
+ evictOldestSettled(faceHashMemo, FACE_HASH_MEMO_MAX);
128
+ faceHashMemo.set(key, fresh);
129
+ return fresh;
130
+ }
131
+ /** Test seam: forget every face's hashes (a new process). */
132
+ export function forgetFaceHashesForTests() {
133
+ faceHashMemo.clear();
134
+ }
135
+ //# sourceMappingURL=delivered-face.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delivered-face.js","sourceRoot":"","sources":["../../src/builder/delivered-face.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,11 @@
13
13
  * read on a brand's server then misses at most once per placement, and a warm one never waits.
14
14
  * A revocation still lands on the first render after its refresh does. The tests' runtime gets
15
15
  * the same cache shape, so they exercise what production runs.
16
+ *
17
+ * 2.0.0: the trust root is a key SET (`verify/key-set.ts`). `resolveKid` is still the baked set (today
18
+ * `cert-issuer-1`), and the card verifies with `resolveKidFor(baseUrl)`: a baked kid resolves as before, any other kid
19
+ * makes one read of the delivery origin's published key set, accepted only when a baked kid signs it. A rotation then
20
+ * needs no reinstall. The card's (2.0) contract-1 document digests are memoized with the delivered faces' hashes.
16
21
  */
17
22
  import type { ResolvePublicKeyByKid } from '../contract/kernel.js';
18
23
  import { TtlCache } from '../verify/cache.js';
@@ -22,16 +27,25 @@ export declare const CARD_DELIVERY_TIMEOUT_MS = 8000;
22
27
  /** How long past its 60s TTL a certificate may still paint while it is refreshed or CertREV is down. */
23
28
  export declare const CARD_DELIVERY_STALE_MS = 300000;
24
29
  export interface CardDeliveryRuntime {
30
+ /** The baked kids: the package's own trust root (and the only keys that may sign a published key set). */
25
31
  readonly resolveKid: ResolvePublicKeyByKid;
32
+ /**
33
+ * 2.0.0: what the card verifies a read from `baseUrl` with: the baked kids, then that origin's signed published set.
34
+ * Absent: derived from `resolveKid`, `fetchImpl` and `timeoutMs` (`keySetResolver`).
35
+ */
36
+ readonly resolveKidFor?: (baseUrl: string) => ResolvePublicKeyByKid;
26
37
  readonly fetchImpl: () => typeof fetch;
27
38
  readonly cache: TtlCache<CachedDelivery>;
28
39
  readonly now: () => number;
29
40
  readonly timeoutMs?: number;
30
41
  }
31
42
  export declare function cardDeliveryRuntime(): CardDeliveryRuntime;
43
+ /** 2.0.0: the resolver for a read from `baseUrl`: the runtime's own, else the baked kids plus the origin's key set. */
44
+ export declare function cardResolveKidFor(rt: CardDeliveryRuntime, baseUrl: string): ResolvePublicKeyByKid;
32
45
  /**
33
46
  * Test seam: replace parts of the runtime (a fresh cache unless one is given) and forget the page's envelope seeds
34
- * (a fresh runtime is a fresh page). Returns a restore.
47
+ * (a fresh runtime is a fresh page), (1.3.0) every delivered face's memoized hashes and (2.0.0) every card
48
+ * document's digest and every published key set. Returns a restore.
35
49
  */
36
50
  export declare function setCardDeliveryRuntimeForTests(overrides: Partial<CardDeliveryRuntime>): () => void;
37
51
  //# sourceMappingURL=delivery-runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delivery-runtime.d.ts","sourceRoot":"","sources":["../../src/builder/delivery-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAIxE,iGAAiG;AACjG,eAAO,MAAM,wBAAwB,OAAQ,CAAA;AAC7C,wGAAwG;AACxG,eAAO,MAAM,sBAAsB,SAAU,CAAA;AAE7C,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAA;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,KAAK,CAAA;IACtC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAA;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC3B;AA6BD,wBAAgB,mBAAmB,IAAI,mBAAmB,CAEzD;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,MAAM,IAAI,CAYlG"}
1
+ {"version":3,"file":"delivery-runtime.d.ts","sourceRoot":"","sources":["../../src/builder/delivery-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAQxE,iGAAiG;AACjG,eAAO,MAAM,wBAAwB,OAAQ,CAAA;AAC7C,wGAAwG;AACxG,eAAO,MAAM,sBAAsB,SAAU,CAAA;AAE7C,MAAM,WAAW,mBAAmB;IACnC,0GAA0G;IAC1G,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAA;IAC1C;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,qBAAqB,CAAA;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,KAAK,CAAA;IACtC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAA;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC3B;AA6BD,wBAAgB,mBAAmB,IAAI,mBAAmB,CAEzD;AAED,uHAAuH;AACvH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAQjG;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,MAAM,IAAI,CAgBlG"}
@@ -13,8 +13,17 @@
13
13
  * read on a brand's server then misses at most once per placement, and a warm one never waits.
14
14
  * A revocation still lands on the first render after its refresh does. The tests' runtime gets
15
15
  * the same cache shape, so they exercise what production runs.
16
+ *
17
+ * 2.0.0: the trust root is a key SET (`verify/key-set.ts`). `resolveKid` is still the baked set (today
18
+ * `cert-issuer-1`), and the card verifies with `resolveKidFor(baseUrl)`: a baked kid resolves as before, any other kid
19
+ * makes one read of the delivery origin's published key set, accepted only when a baked kid signs it. A rotation then
20
+ * needs no reinstall. The card's (2.0) contract-1 document digests are memoized with the delivered faces' hashes.
16
21
  */
17
22
  import { TtlCache } from '../verify/cache.js';
23
+ import { forgetKeySetsForTests, keySetResolver } from '../verify/key-set.js';
24
+ import { forgetCardDigestsForTests } from './card-contract.js';
25
+ import { forgetCardReportsForTests } from './card-report.js';
26
+ import { forgetFaceHashesForTests } from './delivered-face.js';
18
27
  import { CERT_ISSUER_KID, CERT_ISSUER_PUBLIC_KEY_PEM } from './issuer-key.js';
19
28
  import { forgetPageSeedsForTests } from './ssr-seed.js';
20
29
  /** The card's server read deadline. A brand's server waits this long once per cold placement. */
@@ -46,12 +55,28 @@ let runtime = defaultRuntime();
46
55
  export function cardDeliveryRuntime() {
47
56
  return runtime;
48
57
  }
58
+ /** 2.0.0: the resolver for a read from `baseUrl`: the runtime's own, else the baked kids plus the origin's key set. */
59
+ export function cardResolveKidFor(rt, baseUrl) {
60
+ if (rt.resolveKidFor)
61
+ return rt.resolveKidFor(baseUrl);
62
+ return keySetResolver({
63
+ baseUrl,
64
+ bakedResolveKid: rt.resolveKid,
65
+ fetchImpl: rt.fetchImpl(),
66
+ ...(rt.timeoutMs !== undefined ? { timeoutMs: rt.timeoutMs } : {}),
67
+ });
68
+ }
49
69
  /**
50
70
  * Test seam: replace parts of the runtime (a fresh cache unless one is given) and forget the page's envelope seeds
51
- * (a fresh runtime is a fresh page). Returns a restore.
71
+ * (a fresh runtime is a fresh page), (1.3.0) every delivered face's memoized hashes and (2.0.0) every card
72
+ * document's digest and every published key set. Returns a restore.
52
73
  */
53
74
  export function setCardDeliveryRuntimeForTests(overrides) {
54
75
  forgetPageSeedsForTests();
76
+ forgetFaceHashesForTests();
77
+ forgetCardDigestsForTests();
78
+ forgetKeySetsForTests();
79
+ forgetCardReportsForTests();
55
80
  const previous = runtime;
56
81
  const now = overrides.now ?? previous.now;
57
82
  runtime = {
@@ -1 +1 @@
1
- {"version":3,"file":"delivery-runtime.js","sourceRoot":"","sources":["../../src/builder/delivery-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAE7C,OAAO,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAEvD,iGAAiG;AACjG,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAA;AAC7C,wGAAwG;AACxG,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAA;AAU7C,MAAM,eAAe,GAA0B,CAAC,GAAG,EAAE,EAAE,CACtD,GAAG,KAAK,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,0BAA0B,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAEpF,SAAS,YAAY,CAAC,GAAkB;IACvC,OAAO,IAAI,QAAQ,CAAiB;QACnC,KAAK,EAAE,MAAM;QACb,aAAa,EAAE,KAAK;QACpB,OAAO,EAAE,sBAAsB;QAC/B,wFAAwF;QACxF,+EAA+E;QAC/E,aAAa,EAAE,wBAAwB,GAAG,KAAK;QAC/C,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvB,CAAC,CAAA;AACH,CAAC;AAED,SAAS,cAAc;IACtB,OAAO;QACN,UAAU,EAAE,eAAe;QAC3B,SAAS,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK;QACjC,KAAK,EAAE,YAAY,EAAE;QACrB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,SAAS,EAAE,wBAAwB;KACnC,CAAA;AACF,CAAC;AAED,IAAI,OAAO,GAAwB,cAAc,EAAE,CAAA;AAEnD,MAAM,UAAU,mBAAmB;IAClC,OAAO,OAAO,CAAA;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAAC,SAAuC;IACrF,uBAAuB,EAAE,CAAA;IACzB,MAAM,QAAQ,GAAG,OAAO,CAAA;IACxB,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAA;IACzC,OAAO,GAAG;QACT,GAAG,QAAQ;QACX,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC;QACxB,GAAG,SAAS;KACZ,CAAA;IACD,OAAO,GAAG,EAAE;QACX,OAAO,GAAG,QAAQ,CAAA;IACnB,CAAC,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"delivery-runtime.js","sourceRoot":"","sources":["../../src/builder/delivery-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAE7C,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAA;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAEvD,iGAAiG;AACjG,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAA;AAC7C,wGAAwG;AACxG,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAA;AAgB7C,MAAM,eAAe,GAA0B,CAAC,GAAG,EAAE,EAAE,CACtD,GAAG,KAAK,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,0BAA0B,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAEpF,SAAS,YAAY,CAAC,GAAkB;IACvC,OAAO,IAAI,QAAQ,CAAiB;QACnC,KAAK,EAAE,MAAM;QACb,aAAa,EAAE,KAAK;QACpB,OAAO,EAAE,sBAAsB;QAC/B,wFAAwF;QACxF,+EAA+E;QAC/E,aAAa,EAAE,wBAAwB,GAAG,KAAK;QAC/C,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvB,CAAC,CAAA;AACH,CAAC;AAED,SAAS,cAAc;IACtB,OAAO;QACN,UAAU,EAAE,eAAe;QAC3B,SAAS,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK;QACjC,KAAK,EAAE,YAAY,EAAE;QACrB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,SAAS,EAAE,wBAAwB;KACnC,CAAA;AACF,CAAC;AAED,IAAI,OAAO,GAAwB,cAAc,EAAE,CAAA;AAEnD,MAAM,UAAU,mBAAmB;IAClC,OAAO,OAAO,CAAA;AACf,CAAC;AAED,uHAAuH;AACvH,MAAM,UAAU,iBAAiB,CAAC,EAAuB,EAAE,OAAe;IACzE,IAAI,EAAE,CAAC,aAAa;QAAE,OAAO,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;IACtD,OAAO,cAAc,CAAC;QACrB,OAAO;QACP,eAAe,EAAE,EAAE,CAAC,UAAU;QAC9B,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE;QACzB,GAAG,CAAC,EAAE,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClE,CAAC,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B,CAAC,SAAuC;IACrF,uBAAuB,EAAE,CAAA;IACzB,wBAAwB,EAAE,CAAA;IAC1B,yBAAyB,EAAE,CAAA;IAC3B,qBAAqB,EAAE,CAAA;IACvB,yBAAyB,EAAE,CAAA;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAA;IACxB,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAA;IACzC,OAAO,GAAG;QACT,GAAG,QAAQ;QACX,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC;QACxB,GAAG,SAAS;KACZ,CAAA;IACD,OAAO,GAAG,EAAE;QACX,OAAO,GAAG,QAAQ,CAAA;IACnB,CAAC,CAAA;AACF,CAAC"}
@@ -27,7 +27,10 @@
27
27
  * for the entry it renders inside (`builderContext.content.id`) from the CertREV Delivery API on
28
28
  * `baseUrl` (an https certrev.com origin, nothing else), verifies it against the baked
29
29
  * `cert-issuer-1` key, re-judges revocation and expiry at every render, and paints the brand's
30
- * render def served beside it. A revoked, expired, unverifiable or unreachable cert, a marker that
30
+ * render def served beside it. 1.3.0: when the Delivery API also serves a server-rendered face
31
+ * (`face: { html, css }`, unsigned) and the verified envelope signs that face's sha256 digests for the
32
+ * def version served (`content.faceDigest`), an unpinned whole-card block paints that face instead;
33
+ * markup that does not hash to the signed digest is never painted. A revoked, expired, unverifiable or unreachable cert, a marker that
31
34
  * is not exactly that shape, or a missing entry id renders nothing. A block WITHOUT the marker
32
35
  * (every entry exported before 1.1.0) renders from its options exactly as 1.0.3 did, so the
33
36
  * paragraph above still describes it: an editor who deletes the marker gets the options face back,
@@ -101,7 +104,10 @@ export interface CertRevBuilderRegistration {
101
104
  */
102
105
  export declare const certRevAnchorComponent: CertRevBuilderRegistration;
103
106
  export { type DesignPin, designPinFromUrl, parseDesignPin } from '../verify/get-verified-delivery.js';
107
+ export { acceptedCardBinding, type CardBinding, type CardDocumentV1, type CardPartName, cardDocumentDigest, cardPartsFor, parseCardDocument, } from './card-contract.js';
108
+ export { CARD_FALLBACK_EVENT, type CardFallbackDetail, type CardFallbackReason } from './card-report.js';
104
109
  export { BUILDER_CERT_CHROME_KEYS, type BuilderCertChromeData, type BuilderCertChromeKey, type CertChromeRenderDef, type CertDeliveryMarker, } from './cert-chrome-data.js';
105
110
  export { acceptedDeliveryMarker, BUILDER_REGISTRATION, type BuilderInput, CERT_COMPONENT_NAME, type CertBlockBuilderRegistration, CertReviewCard, type CertReviewCardProps, type CertReviewCardRegisteredComponent, certRevCertComponent, PLACEMENT_INPUTS, WIRE_INPUTS, } from './cert-review-card.js';
111
+ export { BUILDER_CARD_FLOW_CSS } from './contract-card.js';
106
112
  export { CERT_FACE_FIELDS, credentialLabel, type EnvelopeCardOptions, envelopeCardHtml, envelopeCardInput, faceFromDef, isReaderFaceDef, layoutFor, READER_FACE, themeFromDef, } from './envelope-face.js';
107
113
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,OAAO,EAAiB,KAAK,SAAS,EAAc,MAAM,OAAO,CAAA;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,mFAAmF;AACnF,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,sGAAsG;IACtG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CACzB;AASD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC/B,OAAO,EACP,SAAiB,EACjB,QAAQ,GACR,EAAE;IACF,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC5B,+BAEA;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,gCAiBvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,uCAO5B;AAED;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IAC1C,SAAS,EAAE,OAAO,aAAa,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,KAAK,EAAE,CAAA;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,0BAIpC,CAAA;AAGD,OAAO,EAAE,KAAK,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAGrG,OAAO,EACN,wBAAwB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,KAAK,YAAY,EACjB,mBAAmB,EACnB,KAAK,4BAA4B,EACjC,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,iCAAiC,EACtC,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,gBAAgB,EAChB,eAAe,EACf,KAAK,mBAAmB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,SAAS,EACT,WAAW,EACX,YAAY,GACZ,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,OAAO,EAAiB,KAAK,SAAS,EAAc,MAAM,OAAO,CAAA;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,mFAAmF;AACnF,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,sGAAsG;IACtG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CACzB;AASD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC/B,OAAO,EACP,SAAiB,EACjB,QAAQ,GACR,EAAE;IACF,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC5B,+BAEA;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,gCAiBvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,uCAO5B;AAED;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IAC1C,SAAS,EAAE,OAAO,aAAa,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,KAAK,EAAE,CAAA;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,0BAIpC,CAAA;AAGD,OAAO,EAAE,KAAK,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAGrG,OAAO,EACN,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,GACjB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAExG,OAAO,EACN,wBAAwB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,KAAK,YAAY,EACjB,mBAAmB,EACnB,KAAK,4BAA4B,EACjC,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,iCAAiC,EACtC,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EACN,gBAAgB,EAChB,eAAe,EACf,KAAK,mBAAmB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,SAAS,EACT,WAAW,EACX,YAAY,GACZ,MAAM,oBAAoB,CAAA"}
@@ -28,7 +28,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
28
28
  * for the entry it renders inside (`builderContext.content.id`) from the CertREV Delivery API on
29
29
  * `baseUrl` (an https certrev.com origin, nothing else), verifies it against the baked
30
30
  * `cert-issuer-1` key, re-judges revocation and expiry at every render, and paints the brand's
31
- * render def served beside it. A revoked, expired, unverifiable or unreachable cert, a marker that
31
+ * render def served beside it. 1.3.0: when the Delivery API also serves a server-rendered face
32
+ * (`face: { html, css }`, unsigned) and the verified envelope signs that face's sha256 digests for the
33
+ * def version served (`content.faceDigest`), an unpinned whole-card block paints that face instead;
34
+ * markup that does not hash to the signed digest is never painted. A revoked, expired, unverifiable or unreachable cert, a marker that
32
35
  * is not exactly that shape, or a missing entry id renders nothing. A block WITHOUT the marker
33
36
  * (every entry exported before 1.1.0) renders from its options exactly as 1.0.3 did, so the
34
37
  * paragraph above still describes it: an editor who deletes the marker gets the options face back,
@@ -107,8 +110,12 @@ export const certRevAnchorComponent = {
107
110
  };
108
111
  // 1.2.0-canary.5: the design pin, for CertREV's own canary replica (`<Content context={{ certrevDesignPin }}>`).
109
112
  export { designPinFromUrl, parseDesignPin } from '../verify/get-verified-delivery.js';
113
+ // 2.0.0: the contract-1 card (CertREV's server-rendered card document), its fallback report and the Builder flow rule.
114
+ export { acceptedCardBinding, cardDocumentDigest, cardPartsFor, parseCardDocument, } from './card-contract.js';
115
+ export { CARD_FALLBACK_EVENT } from './card-report.js';
110
116
  // ── The Builder.io PUSH kit (CertReviewCard) + the single-sourced wire type ──
111
117
  export { BUILDER_CERT_CHROME_KEYS, } from './cert-chrome-data.js';
112
118
  export { acceptedDeliveryMarker, BUILDER_REGISTRATION, CERT_COMPONENT_NAME, CertReviewCard, certRevCertComponent, PLACEMENT_INPUTS, WIRE_INPUTS, } from './cert-review-card.js';
119
+ export { BUILDER_CARD_FLOW_CSS } from './contract-card.js';
113
120
  export { CERT_FACE_FIELDS, credentialLabel, envelopeCardHtml, envelopeCardInput, faceFromDef, isReaderFaceDef, layoutFor, READER_FACE, themeFromDef, } from './envelope-face.js';
114
121
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,MAAM,OAAO,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAexD,MAAM,cAAc,GAAG,aAAa,CAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAC/B,OAAO,EACP,SAAS,GAAG,KAAK,EACjB,QAAQ,GAKR;IACA,OAAO,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAG,QAAQ,GAA2B,CAAA;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACvC,OAAO,CACN,kDACiC,EAAE,EAClC,KAAK,EAAE;YACN,MAAM,EAAE,oBAAoB;YAC5B,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,SAAS;SACrB,iCAEe,8CAA0B,oJAErC,CACN,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC5B,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACzD,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,SAAG,CAAA;IACrF,CAAC;IACD,IAAI,SAAS;QAAE,OAAO,KAAC,wBAAwB,KAAG,CAAA;IAClD,OAAO,IAAI,CAAA;AACZ,CAAC;AAaD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA+B;IACjE,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,gBAAgB;IACtB,MAAM,EAAE,EAAE;CACV,CAAA;AAED,iHAAiH;AACjH,OAAO,EAAkB,gBAAgB,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAErG,gFAAgF;AAChF,OAAO,EACN,wBAAwB,GAKxB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EAEpB,mBAAmB,EAEnB,cAAc,EAGd,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,gBAAgB,EAChB,eAAe,EAEf,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,SAAS,EACT,WAAW,EACX,YAAY,GACZ,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,MAAM,OAAO,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAexD,MAAM,cAAc,GAAG,aAAa,CAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAC/B,OAAO,EACP,SAAS,GAAG,KAAK,EACjB,QAAQ,GAKR;IACA,OAAO,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAG,QAAQ,GAA2B,CAAA;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACvC,OAAO,CACN,kDACiC,EAAE,EAClC,KAAK,EAAE;YACN,MAAM,EAAE,oBAAoB;YAC5B,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,SAAS;SACrB,iCAEe,8CAA0B,oJAErC,CACN,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC5B,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACzD,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,SAAG,CAAA;IACrF,CAAC;IACD,IAAI,SAAS;QAAE,OAAO,KAAC,wBAAwB,KAAG,CAAA;IAClD,OAAO,IAAI,CAAA;AACZ,CAAC;AAaD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA+B;IACjE,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,gBAAgB;IACtB,MAAM,EAAE,EAAE;CACV,CAAA;AAED,iHAAiH;AACjH,OAAO,EAAkB,gBAAgB,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAErG,uHAAuH;AACvH,OAAO,EACN,mBAAmB,EAInB,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,GACjB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,mBAAmB,EAAoD,MAAM,kBAAkB,CAAA;AACxG,gFAAgF;AAChF,OAAO,EACN,wBAAwB,GAKxB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EAEpB,mBAAmB,EAEnB,cAAc,EAGd,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EACN,gBAAgB,EAChB,eAAe,EAEf,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,SAAS,EACT,WAAW,EACX,YAAY,GACZ,MAAM,oBAAoB,CAAA"}
@@ -48,11 +48,29 @@
48
48
  * server withheld. A record holds no payload, so `parseSeed` never takes one for a seed. A `'none'` record sits on a
49
49
  * `hidden` element with nothing in it.
50
50
  *
51
+ * A DELIVERED FACE RIDES THE SEED (1.3.0), and only when the server painted it: the seed then ends with
52
+ * `face: { html, css }`, the server-rendered markup and stylesheet the Delivery API served beside the envelope, whose
53
+ * hashes the signed `payload.content.faceDigest` binds. A browser repaints that face from the seed only after hashing
54
+ * it against the seed payload's digest itself (`cert-review-card.tsx`); a seed with no `face`, a malformed one, or one
55
+ * whose hashes fail paints what 1.2.1 paints for it. A seed with no face is byte for byte the 1.2.1 seed. The face is
56
+ * on the page twice (painted, and in the seed), which is its page-weight cost.
57
+ *
58
+ * A CONTRACT-1 CARD RIDES THE SEED (2.0.0), and only when the server printed it: the seed then ends with `card`, the
59
+ * card document exactly as the Delivery API served it, whose RFC 8785 digest the signed `payload.content.faceDigest`
60
+ * binds (`contract: 1`, `doc`). The browser prints that card from the seed only after it has checked it against the
61
+ * seed payload's binding itself (`cert-review-card.tsx`), so a client re-render (React's recovery from a hydration
62
+ * error, a streamed record, a later mount of the same placement) prints the server's card with no read. A seed with no
63
+ * card is the 1.3.1 seed byte for byte. The card is on the page twice (printed, and in the seed), as 1.3.0's face is.
64
+ *
65
+ * A FALLBACK RECORD MAY NAME ITS REASON (2.0.0): where a contract-1 placement prints nothing, its `'none'` record ends
66
+ * with `reason` (`card_missing`, `card_verify_failed`, `card_unsupported_contract`, `card_pin_mismatch`), the same
67
+ * reason the card reports. Every other record is the 1.3.1 record byte for byte, and 1.3.1 ignores the key.
68
+ *
51
69
  * React-free and browser-only in effect: with no `document` (a server) `pageSeedFor` finds nothing, so a server never
52
70
  * reads or keeps a seed and a multi-tenant render process holds nothing per page.
53
71
  */
54
72
  import type { CertPayload, CertSignature } from '../contract/kernel.js';
55
- import { type DesignPin } from '../verify/get-verified-delivery.js';
73
+ import { type DeliveredFace, type DesignPin } from '../verify/get-verified-delivery.js';
56
74
  import type { CertChromeRenderDef } from './cert-chrome-data.js';
57
75
  export declare const SEED_ATTRIBUTE = "data-certrev-envelope";
58
76
  export interface DeliverySeed {
@@ -68,6 +86,16 @@ export interface DeliverySeed {
68
86
  */
69
87
  readonly signature?: CertSignature;
70
88
  readonly renderDef: CertChromeRenderDef | null;
89
+ /**
90
+ * 1.3.0: the delivered face the server painted (the file header); absent whenever it painted anything else. On a
91
+ * seed read from the page it is UNVERIFIED until hashed against `payload.content.faceDigest`.
92
+ */
93
+ readonly face?: DeliveredFace;
94
+ /**
95
+ * 2.0.0: the contract-1 card document the server printed (the file header), exactly as served; absent whenever it
96
+ * printed anything else. On a seed read from the page it is UNVERIFIED until checked against the payload's binding.
97
+ */
98
+ readonly card?: Readonly<Record<string, unknown>>;
71
99
  }
72
100
  /** A seed as read from the page: the parsed seed and the attribute's exact text (re-rendered verbatim on hydration). */
73
101
  export interface PageSeed {
@@ -82,6 +110,8 @@ export interface DeliveryFallback {
82
110
  readonly externalId: string;
83
111
  readonly design?: DesignPin;
84
112
  readonly face: FallbackFace;
113
+ /** 2.0.0: why a contract-1 placement printed nothing; absent on every other record. */
114
+ readonly reason?: string;
85
115
  }
86
116
  /** A fallback record as read from the page, with the attribute's exact text. */
87
117
  export interface PageFallback {