@taprootio/site-authoring 0.12.4 → 0.12.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/package.json +1 -1
- package/src/cli.js +1 -1
- package/src/delivery-browser.js +4 -4
- package/src/delivery-check.js +56 -112
- package/src/reference-help.js +6 -7
package/README.md
CHANGED
|
@@ -536,10 +536,11 @@ the way a visitor does and reports each dimension separately: the latest
|
|
|
536
536
|
completed deployment for the target, HTTP delivery of the authored routes and
|
|
537
537
|
the favicon, images, module preloads and site bundle they declare (status and
|
|
538
538
|
content type, no redirects followed), internal link targets, accidental
|
|
539
|
-
local-only references, and the
|
|
540
|
-
entry and capability modules
|
|
541
|
-
|
|
542
|
-
|
|
539
|
+
local-only references, and the pinned runtime — the version the page names,
|
|
540
|
+
its entry module and the capability modules the page declares (a page that
|
|
541
|
+
pins no runtime version and entry fails the runtime check). The
|
|
542
|
+
browser dimension (a fresh and a returning load
|
|
543
|
+
compared against the page's pinned entry, every document request in the browser,
|
|
543
544
|
popups included, held to the site origin before it is sent, and cache reuse
|
|
544
545
|
on the returning load observed rather than assumed)
|
|
545
546
|
runs only when Playwright resolves from the CLI's own install (install
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taprootio/site-authoring",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.6",
|
|
4
4
|
"description": "Taproot site authoring CLI: sign in once in the browser, then pull, validate, push, preview, approve, and deploy a site from the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "ISC",
|
package/src/cli.js
CHANGED
|
@@ -404,7 +404,7 @@ const VERBS = Object.freeze([
|
|
|
404
404
|
+ "visitor does and reports each dimension separately: the latest completed deployment for the target, HTTP "
|
|
405
405
|
+ "delivery of the authored routes (from the workspace manifest, bounded), the favicon, images, module "
|
|
406
406
|
+ "preloads and site bundle they reference, internal link targets, accidental local-only references, and the "
|
|
407
|
-
+ "
|
|
407
|
+
+ "pinned runtime (its version, entry module and capability modules), plus a browser "
|
|
408
408
|
+ "dimension that runs only when Playwright is installed and is reported as unchecked otherwise. --staging "
|
|
409
409
|
+ "resolves the acknowledged staging host and authorizes one preview session for the run (--url is refused "
|
|
410
410
|
+ "there); --production needs --url https://<published-domain>/ because a site-authoring key cannot read "
|
package/src/delivery-browser.js
CHANGED
|
@@ -4,9 +4,9 @@ import { withholdCredentialLocation } from "./staging-check.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* The optional browser dimension of delivery verification (TR00824).
|
|
6
6
|
*
|
|
7
|
-
* HTTP evidence cannot say what a visitor's browser loaded:
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* HTTP evidence cannot say what a visitor's browser loaded: a returning
|
|
8
|
+
* browser may hold an older release's HTML and runtime in its cache. This
|
|
9
|
+
* probe drives a real browser at the target twice — a fresh
|
|
10
10
|
* context, then a second navigation in the same context so the browser's own
|
|
11
11
|
* cache participates — and reports which runtime entry each load evaluated,
|
|
12
12
|
* whether the declared capability elements were defined, and whether the
|
|
@@ -188,7 +188,7 @@ function judge({ observed, strayed, finalUrl }, expectedEntryUrl, capabilityCoun
|
|
|
188
188
|
|
|
189
189
|
/**
|
|
190
190
|
* @param options.baseUrl The delivery origin.
|
|
191
|
-
* @param options.expectedEntryUrl The runtime entry the
|
|
191
|
+
* @param options.expectedEntryUrl The runtime entry the freshly fetched page pins.
|
|
192
192
|
* @param options.capabilities Custom-element tags the home page declares.
|
|
193
193
|
* @param options.cookie Optional staging cookie, sent only to `baseUrl`'s host.
|
|
194
194
|
* @param options.importPlaywright Test seam; defaults to a dynamic import.
|
package/src/delivery-check.js
CHANGED
|
@@ -7,9 +7,9 @@ import { withholdCredentialLocation } from "./staging-check.js";
|
|
|
7
7
|
* A completed deployment job proves that Taproot wrote what it meant to
|
|
8
8
|
* write. It does not prove that a visitor receives it: the edge may still
|
|
9
9
|
* serve the previous route, an asset may 404 behind a 200 HTML error page, and
|
|
10
|
-
* the shared runtime a browser loads
|
|
11
|
-
*
|
|
12
|
-
* deployment target with explicit bounds and reports each dimension on its
|
|
10
|
+
* the shared runtime a browser loads is the version the page pins, which has
|
|
11
|
+
* to be served for that exact version. This module checks the public HTTP
|
|
12
|
+
* surface of a deployment target with explicit bounds and reports each dimension on its
|
|
13
13
|
* own: HTTP delivery is checked here, browser-observed behaviour is the
|
|
14
14
|
* optional probe in `delivery-browser.js`, and a dimension nobody checked is
|
|
15
15
|
* reported as unchecked, never as passed.
|
|
@@ -34,7 +34,6 @@ export const DELIVERY_LIMITS = Object.freeze({
|
|
|
34
34
|
retryMilliseconds: 1_000,
|
|
35
35
|
requestMilliseconds: 15_000,
|
|
36
36
|
htmlBytes: 2 * 1024 * 1024,
|
|
37
|
-
manifestBytes: 256 * 1024,
|
|
38
37
|
propagationWaitMaximumSeconds: 120,
|
|
39
38
|
failures: 40,
|
|
40
39
|
reportBytes: 24_000,
|
|
@@ -45,7 +44,6 @@ export const DELIVERY_LIMITS = Object.freeze({
|
|
|
45
44
|
});
|
|
46
45
|
|
|
47
46
|
const HTML_TYPES = /^text\/html\b/iu;
|
|
48
|
-
const JSON_TYPES = /^application\/(?:json|manifest\+json)\b/iu;
|
|
49
47
|
const SCRIPT_TYPES = /^(?:text|application)\/(?:javascript|ecmascript|x-javascript)\b/iu;
|
|
50
48
|
const IMAGE_TYPES = /^image\//iu;
|
|
51
49
|
const LOCAL_HOST = /^(?:localhost|127\.0\.0\.1|\[::1\]|0\.0\.0\.0|[^/]*\.(?:test|local|localhost|internal))(?::\d+)?$/iu;
|
|
@@ -171,27 +169,12 @@ function typeOf(response) {
|
|
|
171
169
|
|
|
172
170
|
function expectationFor(kind) {
|
|
173
171
|
if (kind === "route" || kind === "link") return { name: "text/html", test: HTML_TYPES };
|
|
174
|
-
if (kind === "manifest") return { name: "application/json", test: JSON_TYPES };
|
|
175
172
|
if (kind === "site-bundle" || kind === "runtime-entry" || kind === "module" || kind === "capability") {
|
|
176
173
|
return { name: "javascript", test: SCRIPT_TYPES };
|
|
177
174
|
}
|
|
178
175
|
return { name: "image/*", test: IMAGE_TYPES };
|
|
179
176
|
}
|
|
180
177
|
|
|
181
|
-
export function compareVersions(left, right) {
|
|
182
|
-
const parse = (value) => {
|
|
183
|
-
const match = /^(\d+)\.(\d+)\.(\d+)$/u.exec(value ?? "");
|
|
184
|
-
return match ? match.slice(1).map(Number) : undefined;
|
|
185
|
-
};
|
|
186
|
-
const a = parse(left);
|
|
187
|
-
const b = parse(right);
|
|
188
|
-
if (!a || !b) return undefined;
|
|
189
|
-
for (let index = 0; index < 3; index += 1) {
|
|
190
|
-
if (a[index] !== b[index]) return a[index] < b[index] ? -1 : 1;
|
|
191
|
-
}
|
|
192
|
-
return 0;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
178
|
/** The runtime bootstrap a rendered page declares, or undefined when absent. */
|
|
196
179
|
export function parseBootstrap(html) {
|
|
197
180
|
const script = tokenizeMarkup(html).find((tag) => tag.name === "script" && tag.attributes.get("id") === "taproot-runtime-bootstrap");
|
|
@@ -208,12 +191,23 @@ export function parseBootstrap(html) {
|
|
|
208
191
|
// capability this page could declare and is counted, never carried.
|
|
209
192
|
const declared = Array.isArray(parsed.runtimeCapabilities) ? parsed.runtimeCapabilities : [];
|
|
210
193
|
const runtimeCapabilities = declared.filter((value) => typeof value === "string" && CUSTOM_ELEMENT_NAME.test(value)).slice(0, 64);
|
|
194
|
+
// The page pins one runtime version: its entry and the module each declared
|
|
195
|
+
// capability resolves to. Only the declared tags are read from the map, so
|
|
196
|
+
// an oversized map costs nothing.
|
|
197
|
+
const capabilityMap = parsed.capabilities !== null && typeof parsed.capabilities === "object" && !Array.isArray(parsed.capabilities)
|
|
198
|
+
? parsed.capabilities
|
|
199
|
+
: {};
|
|
200
|
+
const capabilities = Object.fromEntries(
|
|
201
|
+
runtimeCapabilities
|
|
202
|
+
.filter((tag) => Object.hasOwn(capabilityMap, tag))
|
|
203
|
+
.map((tag) => [tag, text(capabilityMap[tag])]),
|
|
204
|
+
);
|
|
211
205
|
return {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
fallbackRuntimeManifestUrl: text(parsed.fallbackRuntimeManifestUrl),
|
|
206
|
+
version: text(parsed.version).slice(0, 64),
|
|
207
|
+
entry: text(parsed.entry),
|
|
215
208
|
siteBundleUrl: text(parsed.siteBundleUrl),
|
|
216
209
|
runtimeCapabilities,
|
|
210
|
+
capabilities,
|
|
217
211
|
...(declared.length > runtimeCapabilities.length ? { capabilitiesDropped: declared.length - runtimeCapabilities.length } : {}),
|
|
218
212
|
};
|
|
219
213
|
}
|
|
@@ -341,7 +335,7 @@ function bounded(value) {
|
|
|
341
335
|
* read is part of the attempt, so a server that stalls or resets after its
|
|
342
336
|
* headers is retried and recorded, never thrown.
|
|
343
337
|
*/
|
|
344
|
-
async function probe(context, url, kind, { readBody = false, maximumBytes = DELIVERY_LIMITS.
|
|
338
|
+
async function probe(context, url, kind, { readBody = false, maximumBytes = DELIVERY_LIMITS.htmlBytes } = {}) {
|
|
345
339
|
const expectation = expectationFor(kind);
|
|
346
340
|
const origin = new URL(url).origin;
|
|
347
341
|
if (!context.allowedOrigins.has(origin)) {
|
|
@@ -485,14 +479,13 @@ async function observe(context, origin, sampledRoutes) {
|
|
|
485
479
|
}
|
|
486
480
|
}
|
|
487
481
|
|
|
488
|
-
// The runtime the page
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
492
|
-
// admitted for the runtime probes only.
|
|
482
|
+
// The runtime the page pins: one version, the entry module that version
|
|
483
|
+
// names and the module each declared capability maps to. The bootstrap is
|
|
484
|
+
// generator-authored, so its origins are trusted here and admitted for the
|
|
485
|
+
// runtime probes only.
|
|
493
486
|
const runtime = {
|
|
494
487
|
declared: bootstrap !== undefined,
|
|
495
|
-
|
|
488
|
+
version: bootstrap?.version ?? "",
|
|
496
489
|
capabilities: bootstrap?.runtimeCapabilities ?? [],
|
|
497
490
|
...(bootstrap?.capabilitiesDropped ? { capabilitiesDropped: bootstrap.capabilitiesDropped } : {}),
|
|
498
491
|
};
|
|
@@ -510,72 +503,31 @@ async function observe(context, origin, sampledRoutes) {
|
|
|
510
503
|
else addAsset(bundle.href, "site-bundle");
|
|
511
504
|
}
|
|
512
505
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
506
|
+
runtime.pinned = runtime.version !== "" && Boolean(bootstrap?.entry);
|
|
507
|
+
const entryUrl = runtime.pinned ? admit(bootstrap.entry) : undefined;
|
|
508
|
+
if (entryUrl) {
|
|
509
|
+
const { row } = await probe(context, entryUrl.href, "runtime-entry");
|
|
510
|
+
runtime.entry = { url: row.url, status: row.status, ok: row.ok, ...(row.failure ? { failure: row.failure } : {}) };
|
|
511
|
+
} else {
|
|
512
|
+
runtime.entry = { url: bounded(bootstrap?.entry ?? ""), ok: false, failure: runtime.pinned ? "invalid_url" : "undeclared" };
|
|
518
513
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
let parsed;
|
|
522
|
-
if (row.ok) {
|
|
523
|
-
try {
|
|
524
|
-
parsed = JSON.parse(body);
|
|
525
|
-
} catch {
|
|
526
|
-
row.ok = false;
|
|
527
|
-
row.failure = "invalid_json";
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
return { name, row, parsed, url };
|
|
531
|
-
});
|
|
514
|
+
// Declared is not resolvable: a module URL that is missing, invalid or
|
|
515
|
+
// points off an admitted origin is a failed observation, not a silent omission.
|
|
532
516
|
const capabilityUrls = [];
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (!module) return { tag, failure: "invalid_url" };
|
|
543
|
-
if (!context.allowedOrigins.has(module.origin)) return { tag, url: module.href, failure: "origin_not_allowed" };
|
|
544
|
-
return { tag, url: module.href };
|
|
545
|
-
});
|
|
546
|
-
const resolvable = outcomes.filter((outcome) => outcome.failure === undefined);
|
|
547
|
-
runtime[name] = {
|
|
548
|
-
url: bounded(row.url),
|
|
549
|
-
status: row.status,
|
|
550
|
-
ok: row.ok,
|
|
551
|
-
...(row.failure ? { failure: row.failure } : {}),
|
|
552
|
-
version: typeof parsed?.version === "string" ? bounded(parsed.version) : "",
|
|
553
|
-
majorVersion: parsed?.majorVersion === undefined ? "" : bounded(String(parsed.majorVersion)),
|
|
554
|
-
entry: typeof parsed?.entry === "string" ? bounded(parsed.entry) : "",
|
|
555
|
-
cacheControl: row.cacheControl ?? "",
|
|
556
|
-
capabilities: Object.keys(capabilities).length,
|
|
557
|
-
declaredCapabilities: declared.length,
|
|
558
|
-
resolvableCapabilities: resolvable.length,
|
|
559
|
-
};
|
|
560
|
-
if (name === "majorPointer" && row.ok) {
|
|
561
|
-
for (const outcome of outcomes) {
|
|
562
|
-
if (outcome.failure) capabilityUrls.push({ tag: outcome.tag, url: outcome.url ?? "", failure: outcome.failure });
|
|
563
|
-
else if (capabilityUrls.filter((entry) => !entry.failure).length < DELIVERY_LIMITS.capabilities) capabilityUrls.push(outcome);
|
|
564
|
-
else capabilityUrls.push({ tag: outcome.tag, url: outcome.url, unchecked: true });
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
const pointer = runtime.majorPointer;
|
|
569
|
-
if (pointer?.ok && pointer.entry) {
|
|
570
|
-
const entry = resolveReference(pointer.entry, pointer.url);
|
|
571
|
-
if (entry && context.allowedOrigins.has(entry.origin)) {
|
|
572
|
-
const { row } = await probe(context, entry.href, "runtime-entry");
|
|
573
|
-
runtime.entry = { url: row.url, status: row.status, ok: row.ok, ...(row.failure ? { failure: row.failure } : {}) };
|
|
517
|
+
let resolvable = 0;
|
|
518
|
+
// A page that does not pin a runtime declares no chunk URLs to check.
|
|
519
|
+
for (const tag of runtime.pinned ? runtime.capabilities : []) {
|
|
520
|
+
const declared = bootstrap.capabilities[tag];
|
|
521
|
+
const module = declared ? resolveReference(declared, origin) : undefined;
|
|
522
|
+
if (!module) {
|
|
523
|
+
capabilityUrls.push({ tag, url: "", failure: declared ? "invalid_url" : "undeclared" });
|
|
524
|
+
} else if (!context.allowedOrigins.has(module.origin)) {
|
|
525
|
+
capabilityUrls.push({ tag, url: module.href, failure: "origin_not_allowed" });
|
|
574
526
|
} else {
|
|
575
|
-
|
|
527
|
+
resolvable += 1;
|
|
528
|
+
const checkable = capabilityUrls.filter((entry) => !entry.failure && !entry.unchecked).length;
|
|
529
|
+
capabilityUrls.push(checkable < DELIVERY_LIMITS.capabilities ? { tag, url: module.href } : { tag, url: module.href, unchecked: true });
|
|
576
530
|
}
|
|
577
|
-
} else {
|
|
578
|
-
runtime.entry = { url: "", ok: false, failure: "pointer_unavailable" };
|
|
579
531
|
}
|
|
580
532
|
const capabilityResults = await mapBounded(capabilityUrls, async ({ tag, url, failure, unchecked }) => {
|
|
581
533
|
if (failure) return { tag: bounded(tag), url: bounded(url), kind: "capability", status: 0, ok: false, attempts: 0, failure };
|
|
@@ -592,13 +544,10 @@ async function observe(context, origin, sampledRoutes) {
|
|
|
592
544
|
unchecked: uncheckedCapabilities,
|
|
593
545
|
items: capabilityResults.filter((row) => !row.ok && !row.unchecked).slice(0, DELIVERY_LIMITS.capabilities),
|
|
594
546
|
};
|
|
595
|
-
runtime.
|
|
596
|
-
|
|
597
|
-
&& compareVersions(pointer.version, runtime.fallback.version) === -1;
|
|
598
|
-
runtime.capabilitiesResolvable = Boolean(pointer?.ok)
|
|
599
|
-
&& pointer.resolvableCapabilities === runtime.capabilities.length
|
|
547
|
+
runtime.capabilitiesResolvable = runtime.declared
|
|
548
|
+
&& resolvable === runtime.capabilities.length
|
|
600
549
|
&& runtime.capabilityModules.failed === 0;
|
|
601
|
-
runtime.compatible = runtime.declared && runtime.
|
|
550
|
+
runtime.compatible = runtime.declared && runtime.pinned && runtime.entry.ok && runtime.capabilitiesResolvable;
|
|
602
551
|
|
|
603
552
|
const assetResults = await mapBounded(assetRows, async ({ url, kind }) => {
|
|
604
553
|
const { row } = await probe(context, url, kind);
|
|
@@ -616,18 +565,14 @@ async function observe(context, origin, sampledRoutes) {
|
|
|
616
565
|
...assetResults.filter((row) => !row.ok).map((row) => `${row.kind} ${row.url}: ${row.failure}`),
|
|
617
566
|
...linkResults.filter((row) => !row.ok).map((row) => `link ${row.path}: ${row.failure}`),
|
|
618
567
|
...(runtime.declared ? [] : ["runtime: the home page declares no runtime bootstrap"]),
|
|
619
|
-
...(runtime.declared && !runtime.
|
|
620
|
-
? [
|
|
621
|
-
: []),
|
|
622
|
-
...(runtime.declared && !runtime.entry.ok ? [`runtime: entry ${runtime.entry.url || "(undeclared)"}: ${runtime.entry.failure}`] : []),
|
|
623
|
-
...(runtime.declared && runtime.fallback && !runtime.fallback.ok
|
|
624
|
-
? [`runtime: fallback ${runtime.fallback.url || "(undeclared)"}: ${runtime.fallback.failure}`]
|
|
568
|
+
...(runtime.declared && !runtime.pinned
|
|
569
|
+
? ["runtime: the page does not pin a runtime version and entry"]
|
|
625
570
|
: []),
|
|
626
|
-
...(runtime.declared && !runtime.
|
|
627
|
-
? [`runtime: ${runtime.
|
|
571
|
+
...(runtime.declared && runtime.pinned && !runtime.entry.ok
|
|
572
|
+
? [`runtime: entry ${runtime.entry.url || "(undeclared)"} (version ${runtime.version}): ${runtime.entry.failure}`]
|
|
628
573
|
: []),
|
|
629
|
-
...(runtime.
|
|
630
|
-
? [`runtime:
|
|
574
|
+
...(runtime.declared && runtime.pinned && !runtime.capabilitiesResolvable
|
|
575
|
+
? [`runtime: ${runtime.capabilityModules.failed} of ${runtime.capabilities.length} declared capability module(s) were undeclared, invalid or did not load`]
|
|
631
576
|
: []),
|
|
632
577
|
...([...localOnly].slice(0, DELIVERY_LIMITS.localReferences).map((reference) => `local-only reference: ${bounded(reference)}`)),
|
|
633
578
|
].map((value) => [...sanitizeDiagnostic(value, "")].slice(0, DELIVERY_LIMITS.urlScalars).join(""));
|
|
@@ -714,7 +659,7 @@ export async function checkDelivery({
|
|
|
714
659
|
let observed = await observe(context, origin, sampledRoutes);
|
|
715
660
|
// One bounded re-observation of everything, for edge propagation: it waits
|
|
716
661
|
// the caller's budget once and observes again from the home page down, so a
|
|
717
|
-
// recovered route, asset
|
|
662
|
+
// recovered route, asset or bootstrap is seen fresh. It never loops.
|
|
718
663
|
let waited = 0;
|
|
719
664
|
if (waitSeconds > 0 && observed.failures.length > 0) {
|
|
720
665
|
await context.sleep(waitSeconds * 1000);
|
|
@@ -802,10 +747,9 @@ function withinOutputBudget(report, failuresTotal) {
|
|
|
802
747
|
const { runtime } = report;
|
|
803
748
|
report.runtime = {
|
|
804
749
|
declared: runtime.declared,
|
|
805
|
-
|
|
750
|
+
version: runtime.version,
|
|
751
|
+
pinned: runtime.pinned,
|
|
806
752
|
capabilities: runtime.capabilities.length,
|
|
807
|
-
majorStreamMatches: runtime.majorStreamMatches,
|
|
808
|
-
pointerBehindFallback: runtime.pointerBehindFallback,
|
|
809
753
|
capabilitiesResolvable: runtime.capabilitiesResolvable,
|
|
810
754
|
compatible: runtime.compatible,
|
|
811
755
|
entryOk: Boolean(runtime.entry?.ok),
|
package/src/reference-help.js
CHANGED
|
@@ -478,11 +478,10 @@ const WORKFLOW_REFERENCES = Object.freeze({
|
|
|
478
478
|
"Assets are what the home page declares: favicon links, module preloads, the site bundle, and up to six "
|
|
479
479
|
+ "images. A 200 is not enough: the content type must match the kind, so an HTML error page served as an "
|
|
480
480
|
+ "image fails.",
|
|
481
|
-
"Runtime: the page's bootstrap
|
|
482
|
-
+ "
|
|
483
|
-
+ "
|
|
484
|
-
|
|
485
|
-
"Browser: a fresh and a returning (same-context, cached) load are compared against the pointer's entry when "
|
|
481
|
+
"Runtime: the page's bootstrap pins one runtime version, its entry module and the module each declared "
|
|
482
|
+
+ "capability resolves to. The entry must load as JavaScript and every declared capability must resolve and "
|
|
483
|
+
+ "load. A page whose bootstrap does not pin a runtime version and entry fails the runtime check.",
|
|
484
|
+
"Browser: a fresh and a returning (same-context, cached) load are compared against the page's pinned entry when "
|
|
486
485
|
+ "Playwright resolves from the CLI's own install: npm install --global @taprootio/site-authoring@latest "
|
|
487
486
|
+ "playwright && npx playwright install chromium, then rerun through the installed taproot-site command (a "
|
|
488
487
|
+ "copy run through npx lives in npm's cache and cannot see a global Playwright). A DevTools "
|
|
@@ -505,8 +504,8 @@ const WORKFLOW_REFERENCES = Object.freeze({
|
|
|
505
504
|
+ "not delivery), local-only references (localhost, *.test, *.local) are reported rather than fetched, a "
|
|
506
505
|
+ "redirect Location that could reflect a credential is withheld, bodies are read up to a fixed bound, "
|
|
507
506
|
+ "member-only content is not exercised, and nothing is purged, republished or rolled back.",
|
|
508
|
-
"Verdicts: delivered (no findings), degraded (every route served and the runtime is compatible,
|
|
509
|
-
+ "
|
|
507
|
+
"Verdicts: delivered (no findings), degraded (every route served and the runtime is compatible, "
|
|
508
|
+
+ "but an asset, link or browser load did not match), failed (a route or the runtime did not deliver).",
|
|
510
509
|
]),
|
|
511
510
|
example: Object.freeze({
|
|
512
511
|
command: `${CLI_BINARY_NAME} delivery check --production --url https://www.example.com/ --wait 30`,
|