@certrev/cert-block 1.3.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +235 -0
  2. package/README.md +162 -9
  3. package/dist/builder/card-beacon.d.ts +73 -0
  4. package/dist/builder/card-beacon.d.ts.map +1 -0
  5. package/dist/builder/card-beacon.js +178 -0
  6. package/dist/builder/card-beacon.js.map +1 -0
  7. package/dist/builder/card-contract.d.ts +151 -0
  8. package/dist/builder/card-contract.d.ts.map +1 -0
  9. package/dist/builder/card-contract.js +286 -0
  10. package/dist/builder/card-contract.js.map +1 -0
  11. package/dist/builder/card-placement.d.ts +45 -0
  12. package/dist/builder/card-placement.d.ts.map +1 -0
  13. package/dist/builder/card-placement.js +193 -0
  14. package/dist/builder/card-placement.js.map +1 -0
  15. package/dist/builder/card-report.d.ts +49 -0
  16. package/dist/builder/card-report.d.ts.map +1 -0
  17. package/dist/builder/card-report.js +97 -0
  18. package/dist/builder/card-report.js.map +1 -0
  19. package/dist/builder/cert-review-card.d.ts +7 -5
  20. package/dist/builder/cert-review-card.d.ts.map +1 -1
  21. package/dist/builder/cert-review-card.js +202 -170
  22. package/dist/builder/cert-review-card.js.map +1 -1
  23. package/dist/builder/contract-card.d.ts +70 -0
  24. package/dist/builder/contract-card.d.ts.map +1 -0
  25. package/dist/builder/contract-card.js +191 -0
  26. package/dist/builder/contract-card.js.map +1 -0
  27. package/dist/builder/delivered-face.d.ts +9 -0
  28. package/dist/builder/delivered-face.d.ts.map +1 -1
  29. package/dist/builder/delivered-face.js +18 -5
  30. package/dist/builder/delivered-face.js.map +1 -1
  31. package/dist/builder/delivery-runtime.d.ts +27 -3
  32. package/dist/builder/delivery-runtime.d.ts.map +1 -1
  33. package/dist/builder/delivery-runtime.js +40 -4
  34. package/dist/builder/delivery-runtime.js.map +1 -1
  35. package/dist/builder/index.d.ts +7 -4
  36. package/dist/builder/index.d.ts.map +1 -1
  37. package/dist/builder/index.js +8 -4
  38. package/dist/builder/index.js.map +1 -1
  39. package/dist/builder/ssr-seed.d.ts +23 -4
  40. package/dist/builder/ssr-seed.d.ts.map +1 -1
  41. package/dist/builder/ssr-seed.js +29 -6
  42. package/dist/builder/ssr-seed.js.map +1 -1
  43. package/dist/builder/version.d.ts +3 -0
  44. package/dist/builder/version.d.ts.map +1 -0
  45. package/dist/builder/version.js +3 -0
  46. package/dist/builder/version.js.map +1 -0
  47. package/dist/index.d.ts +1 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +2 -0
  50. package/dist/index.js.map +1 -1
  51. package/dist/reader/reader-css.generated.d.ts +1 -1
  52. package/dist/reader/reader-css.generated.d.ts.map +1 -1
  53. package/dist/reader/reader-css.generated.js +1 -1
  54. package/dist/reader/reader-css.generated.js.map +1 -1
  55. package/dist/reader/render.d.ts.map +1 -1
  56. package/dist/reader/render.js +71 -34
  57. package/dist/reader/render.js.map +1 -1
  58. package/dist/reader/tokens.d.ts +2 -0
  59. package/dist/reader/tokens.d.ts.map +1 -1
  60. package/dist/reader/tokens.js +28 -6
  61. package/dist/reader/tokens.js.map +1 -1
  62. package/dist/verify/get-verified-delivery.d.ts +32 -0
  63. package/dist/verify/get-verified-delivery.d.ts.map +1 -1
  64. package/dist/verify/get-verified-delivery.js +30 -5
  65. package/dist/verify/get-verified-delivery.js.map +1 -1
  66. package/dist/verify/key-set.d.ts +67 -0
  67. package/dist/verify/key-set.d.ts.map +1 -0
  68. package/dist/verify/key-set.js +190 -0
  69. package/dist/verify/key-set.js.map +1 -0
  70. package/package.json +115 -112
  71. package/src/builder/card-beacon.ts +201 -0
  72. package/src/builder/card-contract.ts +336 -0
  73. package/src/builder/card-placement.ts +208 -0
  74. package/src/builder/card-report.ts +126 -0
  75. package/src/builder/cert-review-card.tsx +285 -201
  76. package/src/builder/contract-card.tsx +216 -0
  77. package/src/builder/delivered-face.ts +18 -4
  78. package/src/builder/delivery-runtime.ts +46 -4
  79. package/src/builder/index.tsx +16 -4
  80. package/src/builder/ssr-seed.ts +37 -7
  81. package/src/builder/version.ts +2 -0
  82. package/src/index.ts +9 -0
  83. package/src/reader/reader-css.generated.ts +1 -1
  84. package/src/reader/reader.css +297 -2
  85. package/src/reader/render.ts +87 -38
  86. package/src/reader/tokens.ts +29 -6
  87. package/src/verify/get-verified-delivery.ts +54 -5
  88. package/src/verify/key-set.ts +225 -0
@@ -0,0 +1,178 @@
1
+ /**
2
+ * The Builder card's beacon (2.0.1): what the card tells CertREV about itself, so a card that does not print on a brand's
3
+ * page is seen by CertREV and not only by the brand's console.
4
+ *
5
+ * One `POST <delivery origin>/api/cert/v1/beacon` per event, a `text/plain` JSON body (a CORS simple request, so no
6
+ * preflight; the origin is the delivery origin the page's `connect-src` already allows, so no new CSP source):
7
+ *
8
+ * { v: 1, client: 'cert-block', clientVersion, contract, platform: 'builder', externalId, family,
9
+ * events: [{ kind: 'fallback' | 'health', reason, outcome, status? }] }
10
+ *
11
+ * What it never carries: cookies (`credentials: 'omit'`), the page's address (`referrerPolicy: 'no-referrer'`) or text, the reader's user agent (only a
12
+ * coarse family: chromium, webkit, gecko, other, or server), or anything the reader typed. `externalId` is the Builder
13
+ * entry the block renders in, which the brand's page already sends to CertREV when it reads the card.
14
+ *
15
+ * How much: in a browser, every fallback (each placement and reason once per page, `card-report.ts`) and one health
16
+ * event per page in a hundred, at most three beacons per page (a page is a document address: an in-app navigation to
17
+ * another article starts a new budget); on a server, each placement, reason and origin at most
18
+ * once an hour per process, with a short deadline, never awaited. A beacon that fails is dropped: it never retries,
19
+ * never stores anything and never changes what the card prints.
20
+ */
21
+ import { CARD_CONTRACT_VERSION } from './card-contract.js';
22
+ import { CERT_BLOCK_VERSION } from './version.js';
23
+ export const BEACON_PATH = '/api/cert/v1/beacon';
24
+ /** Where a block with no accepted delivery marker reports: CertREV's production delivery origin. */
25
+ export const DEFAULT_BEACON_ORIGIN = 'https://portal.certrev.com';
26
+ /** The most beacons one page sends. */
27
+ export const BEACON_MAX_PER_PAGE = 3;
28
+ /** The share of page views that send one health event. */
29
+ export const BEACON_HEALTH_SAMPLE = 0.01;
30
+ /** A server sends one beacon per placement, reason and origin at most this often. */
31
+ export const BEACON_SERVER_INTERVAL_MS = 3_600_000;
32
+ const SERVER_MAX_KEYS = 500;
33
+ const SERVER_TIMEOUT_MS = 1_500;
34
+ /** The transport the card ships with: `fetch`, never awaited, never throwing (exported for CertREV's replica harness). */
35
+ export const fetchBeaconTransport = (url, body, where) => {
36
+ try {
37
+ const send = globalThis.fetch;
38
+ if (typeof send !== 'function')
39
+ return;
40
+ const init = {
41
+ method: 'POST',
42
+ body,
43
+ headers: { 'content-type': 'text/plain;charset=UTF-8' },
44
+ credentials: 'omit',
45
+ // Never the reader's page address, whatever the storefront's Referrer-Policy.
46
+ referrerPolicy: 'no-referrer',
47
+ ...(where === 'browser'
48
+ ? // Opaque and kept alive: no CORS read of the answer, and it outlives a navigation away.
49
+ { mode: 'no-cors', keepalive: true }
50
+ : typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function'
51
+ ? { signal: AbortSignal.timeout(SERVER_TIMEOUT_MS) }
52
+ : {}),
53
+ };
54
+ void send(url, init).catch(() => { });
55
+ }
56
+ catch {
57
+ // A beacon never throws into a render.
58
+ }
59
+ };
60
+ let transport = fetchBeaconTransport;
61
+ let sentOnPage = 0;
62
+ let healthDrawn = null;
63
+ /** The document address the page budget belongs to. */
64
+ let budgetPage = null;
65
+ /** Start a new page budget when the document's address changed (an in-app navigation). */
66
+ function samePageBudget() {
67
+ const page = typeof location !== 'undefined' ? `${location.pathname}${location.search}` : '';
68
+ if (page === budgetPage)
69
+ return;
70
+ budgetPage = page;
71
+ sentOnPage = 0;
72
+ healthDrawn = null;
73
+ }
74
+ const sentOnServer = new Map();
75
+ let random = Math.random;
76
+ let clock = Date.now;
77
+ /** The coarse browser family, from the user agent; nothing else of it is kept. */
78
+ export function browserFamily(userAgent) {
79
+ if (/Firefox\//.test(userAgent))
80
+ return 'gecko';
81
+ if (/(Chrome|Chromium|CriOS|Edg)\//.test(userAgent))
82
+ return 'chromium';
83
+ if (/AppleWebKit\//.test(userAgent))
84
+ return 'webkit';
85
+ return 'other';
86
+ }
87
+ /** The delivery origin a beacon goes to: an `https:` origin on certrev.com, else CertREV's production origin. */
88
+ function beaconOrigin(baseUrl) {
89
+ if (!baseUrl)
90
+ return DEFAULT_BEACON_ORIGIN;
91
+ try {
92
+ const url = new URL(baseUrl);
93
+ const host = url.hostname;
94
+ if (url.protocol === 'https:' && (host === 'certrev.com' || host.endsWith('.certrev.com')))
95
+ return url.origin;
96
+ }
97
+ catch {
98
+ // Fall through.
99
+ }
100
+ return DEFAULT_BEACON_ORIGIN;
101
+ }
102
+ export function beaconBody(externalId, events, family) {
103
+ return {
104
+ v: 1,
105
+ client: 'cert-block',
106
+ clientVersion: CERT_BLOCK_VERSION,
107
+ contract: CARD_CONTRACT_VERSION,
108
+ platform: 'builder',
109
+ ...(externalId ? { externalId } : {}),
110
+ family,
111
+ events,
112
+ };
113
+ }
114
+ /** Send one event from the browser, within the page's budget. */
115
+ export function sendBrowserBeacon(baseUrl, externalId, event) {
116
+ if (typeof document === 'undefined')
117
+ return;
118
+ samePageBudget();
119
+ if (sentOnPage >= BEACON_MAX_PER_PAGE)
120
+ return;
121
+ sentOnPage++;
122
+ const userAgent = typeof navigator !== 'undefined' ? String(navigator.userAgent ?? '') : '';
123
+ const body = beaconBody(externalId, [event], browserFamily(userAgent));
124
+ transport(`${beaconOrigin(baseUrl)}${BEACON_PATH}`, JSON.stringify(body), 'browser');
125
+ }
126
+ /** Send one health event from the browser on one page view in a hundred (drawn once per page). */
127
+ export function maybeSendBrowserHealth(baseUrl, externalId) {
128
+ if (typeof document === 'undefined')
129
+ return;
130
+ samePageBudget();
131
+ if (healthDrawn === null)
132
+ healthDrawn = random() < BEACON_HEALTH_SAMPLE;
133
+ if (!healthDrawn)
134
+ return;
135
+ healthDrawn = false;
136
+ sendBrowserBeacon(baseUrl, externalId, { kind: 'health', reason: 'card_printed', outcome: 'ok' });
137
+ }
138
+ /** Send one event from a server: each placement, reason and origin at most once an hour per process. */
139
+ export function sendServerBeacon(baseUrl, externalId, event) {
140
+ const origin = beaconOrigin(baseUrl);
141
+ const key = `${origin}|${externalId}|${event.kind}|${event.reason}`;
142
+ const now = clock();
143
+ const last = sentOnServer.get(key);
144
+ if (last !== undefined && now - last < BEACON_SERVER_INTERVAL_MS)
145
+ return;
146
+ sentOnServer.delete(key);
147
+ if (sentOnServer.size >= SERVER_MAX_KEYS) {
148
+ const oldest = sentOnServer.keys().next().value;
149
+ if (oldest !== undefined)
150
+ sentOnServer.delete(oldest);
151
+ }
152
+ sentOnServer.set(key, now);
153
+ transport(`${origin}${BEACON_PATH}`, JSON.stringify(beaconBody(externalId, [event], 'server')), 'server');
154
+ }
155
+ /**
156
+ * Test seam: replace the transport (and the sampling draw and clock) and forget the page's and the process's budget.
157
+ * Returns a restore.
158
+ */
159
+ export function setCardBeaconForTests(overrides = {}) {
160
+ const previous = { transport, random, clock };
161
+ transport = overrides.transport ?? (() => { });
162
+ random = overrides.random ?? (() => 1);
163
+ clock = overrides.now ?? Date.now;
164
+ forgetCardBeaconsForTests();
165
+ return () => {
166
+ transport = previous.transport;
167
+ random = previous.random;
168
+ clock = previous.clock;
169
+ };
170
+ }
171
+ /** Test seam: a new page and a new process. */
172
+ export function forgetCardBeaconsForTests() {
173
+ sentOnPage = 0;
174
+ healthDrawn = null;
175
+ budgetPage = null;
176
+ sentOnServer.clear();
177
+ }
178
+ //# sourceMappingURL=card-beacon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-beacon.js","sourceRoot":"","sources":["../../src/builder/card-beacon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────────────────────
3
+ * The contract-1 card a Builder block prints (2.0.0): the server's card document, bound by the signed digest
4
+ * ─────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * CertREV's contract v1 (portal ADR 0001) moves the card to the server: one renderer paints every part of a placement
7
+ * into a CARD DOCUMENT, and every CMS client prints those parts verbatim. Nothing visual, no string a reader sees and
8
+ * no layout is computed here. The Builder Delivery response carries the document as an UNSIGNED top-level sibling,
9
+ * `card`, beside `payload`, `signature`, `renderDef` and `face`. What binds it is the signed
10
+ * `payload.content.faceDigest`, which for a contract-1 Builder placement is
11
+ *
12
+ * { v: 1, defVersion, html, css, contract: 1, doc }
13
+ *
14
+ * where `doc` is the lowercase hex sha256 of the document's RFC 8785 bytes (cert-contract's `canonicalizeJson`) and
15
+ * `html`/`css` bind 1.3.x's delivered face, which this client no longer reads for such a placement.
16
+ *
17
+ * THE SCHEMA IS THE PORTAL'S (`src/lib/cert-delivery/card-document.ts`, `CardDocumentV1`; source read at portal
18
+ * v1-core d5e459b685). This module mirrors only the fields a client reads, read-only, and never redefines them: the
19
+ * digest covers the document's exact bytes, so `parseCardDocument` returns the object it was given, never a copy.
20
+ *
21
+ * Which path a verified delivery takes is decided by the SIGNED binding alone (`acceptedCardBinding`):
22
+ * - no `contract` key in the digest (every brand without CertREV's contract-1 entitlement): the 1.3.1 path, unchanged;
23
+ * - a well-formed contract-1 binding: the card, only when the served document hashes to `doc` and names the same
24
+ * design version, certificate and placement (`cardBinds`); anything else prints nothing and is reported;
25
+ * - a `contract` key that is not a well-formed contract-1 binding (a later contract, a malformed digest): nothing, and
26
+ * reported. A client refuses a contract it does not support (ADR 0001, section 2).
27
+ *
28
+ * React-free. Hashing uses WebCrypto (`crypto.subtle.digest`), as the kernel and the 1.3.0 face gate do, so it runs
29
+ * wherever the card verifies (Node SSR, edge runtimes, a browser in a secure context); the card owns the suspension.
30
+ */
31
+ import { type CertContent, type CertPayload } from '../contract/kernel.js';
32
+ /** The contract this client prints. */
33
+ export declare const CARD_CONTRACT_VERSION = 1;
34
+ /** The part names a document can hold, by where they go: banner card and memo, the sidebar rail, the floating pill. */
35
+ export declare const CARD_PART_NAMES: readonly ["top", "bottom", "rail", "pill"];
36
+ export type CardPartName = (typeof CARD_PART_NAMES)[number];
37
+ /** The layouts a document paints. */
38
+ export type CardLayout = 'banner' | 'sidebar' | 'floating';
39
+ /**
40
+ * The fields of the portal's `CardDocumentV1` this client reads. The document carries more (`renderer`, `placement`,
41
+ * `jsonld`, `lifecycle`, `cache`, `illustrative`); they are part of the digest and printed nowhere by Builder.
42
+ */
43
+ export interface CardDocumentV1 {
44
+ readonly contract: 1;
45
+ readonly kind: 'certrev.card';
46
+ readonly certId: string;
47
+ readonly subject: {
48
+ readonly platform: string;
49
+ readonly externalId: string;
50
+ };
51
+ readonly defVersion: number;
52
+ readonly layout: CardLayout;
53
+ /** Each part is one complete `<certrev-card>` host holding a declarative shadow root. */
54
+ readonly parts: Partial<Record<CardPartName, string>>;
55
+ /** CertREV's own certificate and expert-profile dialogs: complete hosts, identical on every brand and CMS. */
56
+ readonly dialogs: {
57
+ readonly expert?: string;
58
+ readonly certificate?: string;
59
+ };
60
+ readonly assets: {
61
+ /** A complete `<style data-certrev-card-head>` fragment: the pill's fixed box and the fonts. */
62
+ readonly head: string;
63
+ /** The hosted runtime (dialogs, revalidate, polyfill), present once CertREV serves it. */
64
+ readonly runtime?: {
65
+ readonly src: string;
66
+ readonly integrity: string;
67
+ };
68
+ };
69
+ }
70
+ /** The signed binding of a contract-1 card, as the verified `content.faceDigest` carries it. */
71
+ export interface CardBinding {
72
+ readonly contract: 1;
73
+ readonly defVersion: number;
74
+ readonly doc: string;
75
+ }
76
+ /**
77
+ * The signed binding, judged:
78
+ * - `null`: the verified content's digest has no `contract` key (or there is no digest): not a contract-1 placement,
79
+ * so the card paints what 1.3.1 paints;
80
+ * - a `CardBinding`: `{ v: 1, contract: 1 }` with a non-negative integer `defVersion` and a 64-char lowercase hex `doc`
81
+ * (`html` and `css` may be there or not; this client does not read them);
82
+ * - `'unsupported'`: a `contract` key with anything else (another contract, a malformed binding). Fails closed.
83
+ */
84
+ export declare function acceptedCardBinding(content: CertContent | null | undefined): CardBinding | 'unsupported' | null;
85
+ /**
86
+ * The served document, shape-checked for what this client reads: the object itself when it is a contract-1 card
87
+ * document, else null. Nothing is copied or normalised (the digest covers the object as served). Parts it does not
88
+ * know are left in place (they are part of the digest) and never printed.
89
+ */
90
+ export declare function parseCardDocument(value: unknown): CardDocumentV1 | null;
91
+ /** Lowercase hex sha256 of a document's RFC 8785 bytes; null when it cannot be canonicalized or hashed. */
92
+ export declare function cardDocumentDigest(card: unknown): Promise<string | null>;
93
+ /** One document's digest: the pending hash, and its answer once settled (null never matches). */
94
+ export interface CardDigestMemo {
95
+ readonly pending: Promise<string | null>;
96
+ settled?: {
97
+ readonly digest: string | null;
98
+ };
99
+ }
100
+ /**
101
+ * The memoized digest of one document, keyed by its canonical bytes (a seed parsed afresh from the page and the
102
+ * server's cached read of the same document share one entry). A document that cannot be canonicalized settles to null
103
+ * at once, without a key.
104
+ */
105
+ export declare function cardDigestFor(card: unknown): CardDigestMemo;
106
+ /** Test seam: forget every document's digest (a new process). */
107
+ export declare function forgetCardDigestsForTests(): void;
108
+ /**
109
+ * The synchronous half of the check: the document binds this binding, payload and placement in everything but its
110
+ * bytes (contract, design version, certificate, and the Builder placement it was painted for).
111
+ */
112
+ export declare function cardMatchesPlacement(card: CardDocumentV1, binding: CardBinding, payload: CertPayload, externalId: string): boolean;
113
+ /** The asynchronous half's verdict: the document's own digest is the signed one. */
114
+ export declare function cardDigestMatches(digest: string | null, binding: CardBinding): boolean;
115
+ /** Both halves at once (for callers outside React): the served document is the one the verified envelope signs. */
116
+ export declare function cardBinds(card: CardDocumentV1, binding: CardBinding, payload: CertPayload, externalId: string): Promise<boolean>;
117
+ /**
118
+ * The parts one Builder block prints, in page order. The saved design decides the layout (the block's `mode` is not
119
+ * read). A banner block prints its card (`top`) and its memo (`bottom`); a block whose `part` is `'header'` prints the
120
+ * card only, and one whose `part` is `'memo'` the memo only, so a split banner prints each part once. Outside the
121
+ * banner a `'memo'` block prints nothing and any other block the design's one part (the rail, the pill): a page that
122
+ * kept its split header and memo blocks after the brand chose a sidebar design shows one sidebar card, not two.
123
+ */
124
+ export declare function cardPartsFor(card: CardDocumentV1, part: unknown): CardPartName[];
125
+ /** The runtime's tag, or '' when the document names none. */
126
+ export declare function cardRuntimeTag(card: CardDocumentV1): string;
127
+ /**
128
+ * The document's first part: `top`, else `rail`, else `pill`, from the document's own parts (the lead's ruling on ADR
129
+ * 0001's head-once-per-page, relay 074dbd2c8ef3); `bottom` only for a document with none of those, so its head and
130
+ * dialogs always have a block to print them. Null for a document with no part.
131
+ */
132
+ export declare function cardFirstPart(card: CardDocumentV1): CardPartName | null;
133
+ /** Whether the block printing `names` prints the page's head fragment, dialogs and runtime: it holds the first part. */
134
+ export declare function cardCarriesAssets(card: CardDocumentV1, names: readonly CardPartName[]): boolean;
135
+ /**
136
+ * 2.0.1: the page's CertREV graph, when the document carries one CertREV's server resolved for this placement
137
+ * (`card.jsonld.graph`: a complete JSON-LD object, every link and page fact filled in, an additive key under contract
138
+ * 1). Printed verbatim as `<script type="application/ld+json" data-certrev-graph ...>`, with every `<` written as
139
+ * `\u003c` so no value can close the element; '' when the document has no graph. The block never builds or edits the
140
+ * graph: what it says, and when it changes, is the server's.
141
+ */
142
+ export declare function cardGraphTag(card: CardDocumentV1): string;
143
+ /**
144
+ * Exactly what a block prints inside its wrapper: its parts in page order and, only in the block holding the document's
145
+ * first part (`cardCarriesAssets`), the head fragment before them and both dialogs (expert, then certificate), the
146
+ * runtime's tag and (2.0.1) the page's graph after. So a split banner (a header block and a memo block) prints the head, the dialogs and the runtime
147
+ * once, in the header block, with no coordination between the blocks and with JavaScript off; the runtime finds a
148
+ * control's dialog anywhere on the page by its placement. Every byte but the runtime tag is the document's own.
149
+ */
150
+ export declare function cardHtml(card: CardDocumentV1, names: readonly CardPartName[]): string;
151
+ //# sourceMappingURL=card-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-contract.d.ts","sourceRoot":"","sources":["../../src/builder/card-contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,286 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────────────────────
3
+ * The contract-1 card a Builder block prints (2.0.0): the server's card document, bound by the signed digest
4
+ * ─────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * CertREV's contract v1 (portal ADR 0001) moves the card to the server: one renderer paints every part of a placement
7
+ * into a CARD DOCUMENT, and every CMS client prints those parts verbatim. Nothing visual, no string a reader sees and
8
+ * no layout is computed here. The Builder Delivery response carries the document as an UNSIGNED top-level sibling,
9
+ * `card`, beside `payload`, `signature`, `renderDef` and `face`. What binds it is the signed
10
+ * `payload.content.faceDigest`, which for a contract-1 Builder placement is
11
+ *
12
+ * { v: 1, defVersion, html, css, contract: 1, doc }
13
+ *
14
+ * where `doc` is the lowercase hex sha256 of the document's RFC 8785 bytes (cert-contract's `canonicalizeJson`) and
15
+ * `html`/`css` bind 1.3.x's delivered face, which this client no longer reads for such a placement.
16
+ *
17
+ * THE SCHEMA IS THE PORTAL'S (`src/lib/cert-delivery/card-document.ts`, `CardDocumentV1`; source read at portal
18
+ * v1-core d5e459b685). This module mirrors only the fields a client reads, read-only, and never redefines them: the
19
+ * digest covers the document's exact bytes, so `parseCardDocument` returns the object it was given, never a copy.
20
+ *
21
+ * Which path a verified delivery takes is decided by the SIGNED binding alone (`acceptedCardBinding`):
22
+ * - no `contract` key in the digest (every brand without CertREV's contract-1 entitlement): the 1.3.1 path, unchanged;
23
+ * - a well-formed contract-1 binding: the card, only when the served document hashes to `doc` and names the same
24
+ * design version, certificate and placement (`cardBinds`); anything else prints nothing and is reported;
25
+ * - a `contract` key that is not a well-formed contract-1 binding (a later contract, a malformed digest): nothing, and
26
+ * reported. A client refuses a contract it does not support (ADR 0001, section 2).
27
+ *
28
+ * React-free. Hashing uses WebCrypto (`crypto.subtle.digest`), as the kernel and the 1.3.0 face gate do, so it runs
29
+ * wherever the card verifies (Node SSR, edge runtimes, a browser in a secure context); the card owns the suspension.
30
+ */
31
+ import { canonicalizeJson } from '../contract/kernel.js';
32
+ import { evictOldestSettled, sha256HexUtf8 } from './delivered-face.js';
33
+ /** The contract this client prints. */
34
+ export const CARD_CONTRACT_VERSION = 1;
35
+ /** The part names a document can hold, by where they go: banner card and memo, the sidebar rail, the floating pill. */
36
+ export const CARD_PART_NAMES = ['top', 'bottom', 'rail', 'pill'];
37
+ const CARD_LAYOUTS = ['banner', 'sidebar', 'floating'];
38
+ const SHA256_HEX = /^[0-9a-f]{64}$/;
39
+ const SRI = /^(?:sha256|sha384|sha512)-[A-Za-z0-9+/]+={0,2}(?:\s+(?:sha256|sha384|sha512)-[A-Za-z0-9+/]+={0,2})*$/;
40
+ const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
41
+ const isString = (v) => typeof v === 'string';
42
+ /**
43
+ * The signed binding, judged:
44
+ * - `null`: the verified content's digest has no `contract` key (or there is no digest): not a contract-1 placement,
45
+ * so the card paints what 1.3.1 paints;
46
+ * - a `CardBinding`: `{ v: 1, contract: 1 }` with a non-negative integer `defVersion` and a 64-char lowercase hex `doc`
47
+ * (`html` and `css` may be there or not; this client does not read them);
48
+ * - `'unsupported'`: a `contract` key with anything else (another contract, a malformed binding). Fails closed.
49
+ */
50
+ export function acceptedCardBinding(content) {
51
+ const digest = content?.faceDigest;
52
+ // Own keys via `Object.keys`, not `Object.hasOwn`: this runs for every verified placement, in browsers 1.3.1 painted in
53
+ // (Safari before 15.4 has no `Object.hasOwn`; break-it B3).
54
+ if (!isRecord(digest) || !Object.keys(digest).includes('contract'))
55
+ return null;
56
+ const { v, contract, defVersion, doc } = digest;
57
+ if (v !== 1 || contract !== CARD_CONTRACT_VERSION)
58
+ return 'unsupported';
59
+ if (typeof defVersion !== 'number' || !Number.isSafeInteger(defVersion) || defVersion < 0)
60
+ return 'unsupported';
61
+ if (!isString(doc) || !SHA256_HEX.test(doc))
62
+ return 'unsupported';
63
+ return { contract: CARD_CONTRACT_VERSION, defVersion, doc };
64
+ }
65
+ /**
66
+ * The runtime's `src` must be CertREV's: an `https:` URL on `certrev.com` or one of its subdomains, with no credentials
67
+ * and no port (the rule the delivery marker's origin follows). Anything else is not a runtime, so no document, even one
68
+ * planted in a page's record, can make the card load a script from elsewhere (break-it B7).
69
+ */
70
+ function isCertrevRuntimeUrl(value) {
71
+ if (!isString(value))
72
+ return false;
73
+ try {
74
+ const url = new URL(value);
75
+ const host = url.hostname.toLowerCase();
76
+ return (url.protocol === 'https:' &&
77
+ !url.username &&
78
+ !url.password &&
79
+ !url.port &&
80
+ (host === 'certrev.com' || host.endsWith('.certrev.com')));
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
86
+ /**
87
+ * The served document, shape-checked for what this client reads: the object itself when it is a contract-1 card
88
+ * document, else null. Nothing is copied or normalised (the digest covers the object as served). Parts it does not
89
+ * know are left in place (they are part of the digest) and never printed.
90
+ */
91
+ export function parseCardDocument(value) {
92
+ if (!isRecord(value))
93
+ return null;
94
+ const { contract, kind, certId, subject, defVersion, layout, parts, dialogs, assets } = value;
95
+ if (contract !== CARD_CONTRACT_VERSION || kind !== 'certrev.card' || !isString(certId))
96
+ return null;
97
+ if (!isRecord(subject) || !isString(subject.platform) || !isString(subject.externalId))
98
+ return null;
99
+ if (typeof defVersion !== 'number' || !Number.isSafeInteger(defVersion) || defVersion < 0)
100
+ return null;
101
+ if (!isString(layout) || !CARD_LAYOUTS.includes(layout))
102
+ return null;
103
+ if (!isRecord(parts))
104
+ return null;
105
+ for (const name of CARD_PART_NAMES)
106
+ if (parts[name] !== undefined && !isString(parts[name]))
107
+ return null;
108
+ if (!isRecord(dialogs))
109
+ return null;
110
+ if (dialogs.expert !== undefined && !isString(dialogs.expert))
111
+ return null;
112
+ if (dialogs.certificate !== undefined && !isString(dialogs.certificate))
113
+ return null;
114
+ if (!isRecord(assets) || !isString(assets.head))
115
+ return null;
116
+ if (assets.runtime !== undefined) {
117
+ const runtime = assets.runtime;
118
+ if (!isRecord(runtime) ||
119
+ !isCertrevRuntimeUrl(runtime.src) ||
120
+ !isString(runtime.integrity) ||
121
+ !SRI.test(runtime.integrity)) {
122
+ return null;
123
+ }
124
+ }
125
+ return value;
126
+ }
127
+ /** Lowercase hex sha256 of a document's RFC 8785 bytes; null when it cannot be canonicalized or hashed. */
128
+ export async function cardDocumentDigest(card) {
129
+ let canonical;
130
+ try {
131
+ canonical = canonicalizeJson(card);
132
+ }
133
+ catch {
134
+ return null;
135
+ }
136
+ try {
137
+ return await sha256HexUtf8(canonical);
138
+ }
139
+ catch {
140
+ return null;
141
+ }
142
+ }
143
+ /** How many documents' digests are kept (insertion order; the oldest goes first). A page holds one or a few. */
144
+ const CARD_DIGEST_MEMO_MAX = 32;
145
+ const cardDigestMemo = new Map();
146
+ /**
147
+ * The memoized digest of one document, keyed by its canonical bytes (a seed parsed afresh from the page and the
148
+ * server's cached read of the same document share one entry). A document that cannot be canonicalized settles to null
149
+ * at once, without a key.
150
+ */
151
+ export function cardDigestFor(card) {
152
+ let key;
153
+ try {
154
+ key = canonicalizeJson(card);
155
+ }
156
+ catch {
157
+ const failed = { pending: Promise.resolve(null), settled: { digest: null } };
158
+ return failed;
159
+ }
160
+ const known = cardDigestMemo.get(key);
161
+ if (known)
162
+ return known;
163
+ const fresh = {
164
+ pending: sha256HexUtf8(key).then((digest) => {
165
+ fresh.settled = { digest };
166
+ return digest;
167
+ }, () => {
168
+ fresh.settled = { digest: null };
169
+ return null;
170
+ }),
171
+ };
172
+ evictOldestSettled(cardDigestMemo, CARD_DIGEST_MEMO_MAX);
173
+ cardDigestMemo.set(key, fresh);
174
+ return fresh;
175
+ }
176
+ /** Test seam: forget every document's digest (a new process). */
177
+ export function forgetCardDigestsForTests() {
178
+ cardDigestMemo.clear();
179
+ }
180
+ /**
181
+ * The synchronous half of the check: the document binds this binding, payload and placement in everything but its
182
+ * bytes (contract, design version, certificate, and the Builder placement it was painted for).
183
+ */
184
+ export function cardMatchesPlacement(card, binding, payload, externalId) {
185
+ return (card.contract === binding.contract &&
186
+ card.defVersion === binding.defVersion &&
187
+ card.certId === payload.certId &&
188
+ card.subject.platform === 'builder' &&
189
+ card.subject.externalId === externalId);
190
+ }
191
+ /** The asynchronous half's verdict: the document's own digest is the signed one. */
192
+ export function cardDigestMatches(digest, binding) {
193
+ return digest !== null && digest === binding.doc;
194
+ }
195
+ /** Both halves at once (for callers outside React): the served document is the one the verified envelope signs. */
196
+ export async function cardBinds(card, binding, payload, externalId) {
197
+ if (!cardMatchesPlacement(card, binding, payload, externalId))
198
+ return false;
199
+ return cardDigestMatches(await cardDigestFor(card).pending, binding);
200
+ }
201
+ /**
202
+ * The parts one Builder block prints, in page order. The saved design decides the layout (the block's `mode` is not
203
+ * read). A banner block prints its card (`top`) and its memo (`bottom`); a block whose `part` is `'header'` prints the
204
+ * card only, and one whose `part` is `'memo'` the memo only, so a split banner prints each part once. Outside the
205
+ * banner a `'memo'` block prints nothing and any other block the design's one part (the rail, the pill): a page that
206
+ * kept its split header and memo blocks after the brand chose a sidebar design shows one sidebar card, not two.
207
+ */
208
+ export function cardPartsFor(card, part) {
209
+ let names;
210
+ if (card.layout === 'banner') {
211
+ names = part === 'header' ? ['top'] : part === 'memo' ? ['bottom'] : ['top', 'bottom'];
212
+ }
213
+ else if (part === 'memo') {
214
+ names = [];
215
+ }
216
+ else {
217
+ names = card.layout === 'sidebar' ? ['rail'] : ['pill'];
218
+ }
219
+ return names.filter((name) => typeof card.parts[name] === 'string');
220
+ }
221
+ function escapeAttribute(value) {
222
+ return value.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
223
+ }
224
+ /** The runtime's tag, or '' when the document names none. */
225
+ export function cardRuntimeTag(card) {
226
+ const runtime = card.assets.runtime;
227
+ if (!runtime)
228
+ return '';
229
+ return `<script src="${escapeAttribute(runtime.src)}" integrity="${escapeAttribute(runtime.integrity)}" crossorigin="anonymous" defer></script>`;
230
+ }
231
+ /** Which of the document's parts is its first, in this order: the block holding it prints the page's assets. */
232
+ const CARD_FIRST_PART_ORDER = ['top', 'rail', 'pill', 'bottom'];
233
+ /**
234
+ * The document's first part: `top`, else `rail`, else `pill`, from the document's own parts (the lead's ruling on ADR
235
+ * 0001's head-once-per-page, relay 074dbd2c8ef3); `bottom` only for a document with none of those, so its head and
236
+ * dialogs always have a block to print them. Null for a document with no part.
237
+ */
238
+ export function cardFirstPart(card) {
239
+ return CARD_FIRST_PART_ORDER.find((name) => typeof card.parts[name] === 'string') ?? null;
240
+ }
241
+ /** Whether the block printing `names` prints the page's head fragment, dialogs and runtime: it holds the first part. */
242
+ export function cardCarriesAssets(card, names) {
243
+ const first = cardFirstPart(card);
244
+ return first !== null && names.includes(first);
245
+ }
246
+ /**
247
+ * 2.0.1: the page's CertREV graph, when the document carries one CertREV's server resolved for this placement
248
+ * (`card.jsonld.graph`: a complete JSON-LD object, every link and page fact filled in, an additive key under contract
249
+ * 1). Printed verbatim as `<script type="application/ld+json" data-certrev-graph ...>`, with every `<` written as
250
+ * `\u003c` so no value can close the element; '' when the document has no graph. The block never builds or edits the
251
+ * graph: what it says, and when it changes, is the server's.
252
+ */
253
+ export function cardGraphTag(card) {
254
+ const jsonld = card.jsonld;
255
+ if (typeof jsonld !== 'object' || jsonld === null)
256
+ return '';
257
+ const graph = jsonld.graph;
258
+ if (typeof graph !== 'object' || graph === null || Array.isArray(graph))
259
+ return '';
260
+ let json;
261
+ try {
262
+ json = JSON.stringify(graph);
263
+ }
264
+ catch {
265
+ return '';
266
+ }
267
+ const id = escapeAttribute(card.subject.externalId);
268
+ return `<script type="application/ld+json" data-certrev-graph="" data-certrev-platform="builder" data-certrev-external-id="${id}">${json.replace(/</g, '\\u003c')}</script>`;
269
+ }
270
+ /**
271
+ * Exactly what a block prints inside its wrapper: its parts in page order and, only in the block holding the document's
272
+ * first part (`cardCarriesAssets`), the head fragment before them and both dialogs (expert, then certificate), the
273
+ * runtime's tag and (2.0.1) the page's graph after. So a split banner (a header block and a memo block) prints the head, the dialogs and the runtime
274
+ * once, in the header block, with no coordination between the blocks and with JavaScript off; the runtime finds a
275
+ * control's dialog anywhere on the page by its placement. Every byte but the runtime tag is the document's own.
276
+ */
277
+ export function cardHtml(card, names) {
278
+ let parts = '';
279
+ for (const name of names)
280
+ parts += card.parts[name] ?? '';
281
+ if (!cardCarriesAssets(card, names))
282
+ return parts;
283
+ const dialogs = (card.dialogs.expert ?? '') + (card.dialogs.certificate ?? '');
284
+ return card.assets.head + parts + dialogs + cardRuntimeTag(card) + cardGraphTag(card);
285
+ }
286
+ //# sourceMappingURL=card-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-contract.js","sourceRoot":"","sources":["../../src/builder/card-contract.ts"],"names":[],"mappings":"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"}