payload-live-preview 2.0.0 → 2.0.2

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 (92) hide show
  1. package/README.md +41 -34
  2. package/dist/adapters/astro/RichText.astro +1 -1
  3. package/dist/adapters/astro/index.d.ts +8 -8
  4. package/dist/adapters/astro/index.js +1 -1
  5. package/dist/adapters/astro/index.js.map +1 -1
  6. package/dist/adapters/astro/middleware-entry.d.ts +3 -3
  7. package/dist/adapters/astro/middleware-entry.js +1 -1
  8. package/dist/adapters/astro/middleware-entry.js.map +1 -1
  9. package/dist/adapters/nextjs/index.d.ts +2 -2
  10. package/dist/adapters/nextjs/index.js +1 -1
  11. package/dist/adapters/nextjs/index.js.map +1 -1
  12. package/dist/adapters/nuxt/index.d.ts +4 -4
  13. package/dist/adapters/nuxt/index.js +1 -1
  14. package/dist/adapters/nuxt/index.js.map +1 -1
  15. package/dist/adapters/nuxt/module.d.ts +13 -5
  16. package/dist/adapters/nuxt/module.js.map +1 -1
  17. package/dist/adapters/react/index.d.ts +8 -6
  18. package/dist/adapters/react/index.js.map +1 -1
  19. package/dist/adapters/sveltekit/index.d.ts +5 -5
  20. package/dist/adapters/sveltekit/index.js +1 -1
  21. package/dist/adapters/sveltekit/index.js.map +1 -1
  22. package/dist/adapters/vue/index.d.ts +3 -3
  23. package/dist/adapters/vue/index.js.map +1 -1
  24. package/dist/annotate.js +1 -1
  25. package/dist/annotate.js.map +1 -1
  26. package/dist/client.cjs +1 -1
  27. package/dist/client.cjs.map +1 -1
  28. package/dist/client.js +1 -1
  29. package/dist/client.js.map +1 -1
  30. package/dist/codegen-astro.d.ts +4 -1
  31. package/dist/codegen-astro.js.map +1 -1
  32. package/dist/codegen-cli.js +1 -1
  33. package/dist/codegen-cli.js.map +1 -1
  34. package/dist/codegen.cjs +1 -1
  35. package/dist/codegen.cjs.map +1 -1
  36. package/dist/codegen.js +1 -1
  37. package/dist/codegen.js.map +1 -1
  38. package/dist/core.cjs +1 -1
  39. package/dist/core.cjs.map +1 -1
  40. package/dist/core.d.cts +1 -1
  41. package/dist/core.d.ts +1 -1
  42. package/dist/core.js +1 -1
  43. package/dist/core.js.map +1 -1
  44. package/dist/doctor-cli.d.ts +1 -1
  45. package/dist/doctor-cli.js +1 -1
  46. package/dist/doctor-cli.js.map +1 -1
  47. package/dist/doctor.d.ts +4 -2
  48. package/dist/doctor.js +1 -1
  49. package/dist/doctor.js.map +1 -1
  50. package/dist/{fragment-endpoint-3Mlz7GDz.d.ts → fragment-endpoint-DATKU183.d.ts} +2 -2
  51. package/dist/fragment.cjs.map +1 -1
  52. package/dist/fragment.d.cts +6 -3
  53. package/dist/fragment.d.ts +6 -3
  54. package/dist/fragment.js.map +1 -1
  55. package/dist/index.cjs +1 -1
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.d.cts +10 -10
  58. package/dist/index.d.ts +10 -10
  59. package/dist/index.js +1 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/lean.cjs +1 -1
  62. package/dist/lean.cjs.map +1 -1
  63. package/dist/lean.d.cts +3 -3
  64. package/dist/lean.d.ts +3 -3
  65. package/dist/lean.js +1 -1
  66. package/dist/lean.js.map +1 -1
  67. package/dist/lexical.cjs +1 -1
  68. package/dist/lexical.cjs.map +1 -1
  69. package/dist/lexical.d.cts +11 -1
  70. package/dist/lexical.d.ts +11 -1
  71. package/dist/lexical.js +1 -1
  72. package/dist/lexical.js.map +1 -1
  73. package/dist/{locals-DBXDMxOA.d.ts → locals-CNUGYbA_.d.ts} +1 -1
  74. package/dist/{middleware-Bx9I7PXW.d.ts → middleware-CWObDO37.d.ts} +2 -2
  75. package/dist/migrate.d.ts +6 -0
  76. package/dist/migrate.js +1 -1
  77. package/dist/migrate.js.map +1 -1
  78. package/dist/{options-BW1eqXvc.d.ts → options-uHKrDM2U.d.ts} +2 -1
  79. package/dist/plugins.cjs +1 -1
  80. package/dist/plugins.cjs.map +1 -1
  81. package/dist/plugins.js +1 -1
  82. package/dist/plugins.js.map +1 -1
  83. package/dist/{probe-CELeUciC.d.ts → probe-DqaItcwX.d.ts} +15 -0
  84. package/dist/server.cjs.map +1 -1
  85. package/dist/server.d.cts +1 -1
  86. package/dist/server.d.ts +1 -1
  87. package/dist/server.js.map +1 -1
  88. package/dist/structural.cjs +2 -2
  89. package/dist/structural.cjs.map +1 -1
  90. package/dist/structural.js +2 -2
  91. package/dist/structural.js.map +1 -1
  92. package/package.json +6 -4
package/dist/doctor.d.ts CHANGED
@@ -1,9 +1,11 @@
1
- import { a as DoctorProbe, b as DoctorContext, c as DoctorReport, d as DoctorFinding } from './probe-CELeUciC.js';
2
- export { e as DIAGNOSTIC_CODES, f as DiagnosticCode, D as DoctorFetch, g as DoctorLevel, h as DoctorResponse, R as RunDoctorOptions, l as lowercaseHeaders, r as runDoctor } from './probe-CELeUciC.js';
1
+ import { a as DoctorProbe, b as DoctorContext, c as DoctorReport, d as DoctorFinding } from './probe-DqaItcwX.js';
2
+ export { e as DIAGNOSTIC_CODES, f as DiagnosticCode, D as DoctorFetch, g as DoctorLevel, h as DoctorResponse, R as RunDoctorOptions, l as lowercaseHeaders, r as runDoctor } from './probe-DqaItcwX.js';
3
3
 
4
4
  /** Audit a probed deployment. */
5
5
  declare function analyzeProbe(probe: DoctorProbe, context: DoctorContext & {
6
6
  readonly v2?: boolean;
7
+ /** Whether the preview probe carried caller-supplied headers (`--header`). */
8
+ readonly credentials?: boolean;
7
9
  }): DoctorReport;
8
10
 
9
11
  /**
package/dist/doctor.js CHANGED
@@ -1,2 +1,2 @@
1
- var e=Object.freeze({NoTrustedOrigin:"LP0101",ReferrerOnlyTrust:"LP0102",PluginIncompatible:"LP0103",ProfileFeatureOmitted:"LP0104",OrphanField:"LP0201",UnattributableUpdate:"LP0202",VisibilityGateDeferred:"LP0301",UnsafeAttributeWrite:"LP0401",TextTargetHasChildren:"LP0402",MissingArrayTemplate:"LP0403",StructuralItemUnkeyed:"LP0404",StructuralDuplicateKey:"LP0405",StructuralUnstableKeys:"LP0406",UnsupportedStrategy:"LP0407",UnknownValueFormat:"LP0408",SanitizerDroppedAttribute:"LP0409",UnrenderedBlockKept:"LP0410",UnfaithfulPatch:"LP0411",ServerFormatReplaced:"LP0412",UnrenderedBlockLost:"LP0413",MessageRejected:"LP0501",TokenRejected:"LP0502",ProtocolShapeUnknown:"LP0503",HandlerThrew:"LP0601",TransformThrew:"LP0602",RendererThrew:"LP0603",StartupFailed:"LP0605",ReadyFailed:"LP0606",HydrationWaitTimedOut:"LP0607",AuditRuntimeMissing:"LP0701",AuditNoFrameAncestors:"LP0702",AuditFrameOptionsBlocks:"LP0703",AuditBindingsExposed:"LP0704",AuditGateThresholdExceeded:"LP0705",AuditUnownedBindings:"LP0706",AuditNoBindings:"LP0707",AuditNotAPage:"LP0708",FragmentRequestFailed:"LP0801",FragmentResponseInvalid:"LP0802",FragmentUnauthorized:"LP0803",FragmentSuperseded:"LP0804",RouteRefreshLoop:"LP0805",FragmentStrategyUnavailable:"LP0806",UnboundChangeRefresh:"LP0807",V2ReadinessGap:"LP0709",RuntimeOnPublicPage:"LP0710"}),t={http:"80",https:"443",ws:"80",wss:"443"},n=/^([a-z][a-z0-9+.-]*):$/u,i=/^(?:([a-z][a-z0-9+.-]*):\/\/)?(\*|(?:\*\.)?[a-z0-9-]+(?:\.[a-z0-9-]+)*\.?)(?::(\*|\d+))?(?:\/.*)?$/u;function r(e){return e.protocol.slice(0,-1).toLowerCase()}function o(e){return""===e.port?t[r(e)]??"":e.port}function a(e,t){return e===t||"http"===e&&"https"===t||"ws"===e&&"wss"===t}function s(e,t){if(e.hostname!==t.hostname)return!1;const n=r(e),i=r(t);return(n===i||"http"===i&&"https"===n)&&(o(e)===o(t)||""===e.port&&""===t.port)}function d(e,t,s){const d=n.exec(e);if(void 0!==d?.[1])return a(d[1],r(t));const l=i.exec(e);if(null===l)return!1;const c=l[1],u=l[2]??"",h=l[3];return!!a(c??r(s),r(t))&&!!function(e,t){if("*"===e)return!0;const n=e.replace(/\.$/u,""),i=t.replace(/\.$/u,"");return n.startsWith("*.")?i.endsWith(n.slice(1))&&i.length>n.length-1:n===i}(u,t.hostname)&&(void 0===h?""===t.port:"*"===h||h===o(t))}var l=["allowedOrigins","serverURL","apiRoute","mergeDepth","debug","debounceMs","enableA11y","heartbeatMs","disableVisibilityGate","visibilityGateThreshold","intersectionRootMargin","disableReferrerDetection","disableLocalhostMatching","scopeBindingsByOwner","skipUnchanged","eventSourcePolicy","sanitizerPolicy","fragmentEndpoint","revealEditedField","routeStrategy","onUnboundChange","onUnfaithfulPatch","autoBind","hydration","defaults"],c=Object.freeze({disableReferrerDetection:!0,eventSourcePolicy:"parent-or-opener",skipUnchanged:!0,sanitizerPolicy:"strict"}),u=Object.freeze({disableReferrerDetection:!1,eventSourcePolicy:"any",skipUnchanged:!1,sanitizerPolicy:"compat"}),h=l.indexOf("defaults"),p=[{key:"disableReferrerDetection",title:"Referrer trust is still on",effect:"the runtime accepts the admin referer as a preview signal.",condition:"no preview flow relies on the admin referer alone"},{key:"eventSourcePolicy",title:"Messages are accepted from any window",effect:"any window that passes the origin check may post updates.",condition:"no window but the parent or opener posts updates"},{key:"sanitizerPolicy",title:"Sanitizer is in compat mode",effect:"id and every data-* pass.",condition:"rich text no longer relies on `id` or `data-*`"},{key:"skipUnchanged",title:"Unchanged bindings are re-applied every message",effect:"every binding is written again on every message.",condition:"no renderer relies on running again for a value that did not change"}],f={profile:"The script was generated with `defaults: 'v1'`.",explicit:"The script sets it explicitly.",unmarked:"The script names no defaults, so its empty slot is read as the 1.x value."},g={code:"LP0709",level:"info",title:"Could not read the inline configuration for a v2 readiness check",detail:"The preview response carried no readable `__LIVE_PREVIEW_CONFIG__` inline script.",remedy:"Run this against a page with the inline runtime (not loader mode without a preview context)."},m={code:"LP0709",level:"info",title:"The inline configuration does not say which defaults it was generated against",detail:"Scripts carry a `defaults` marker from 2.0.0-rc.1 on; this one has none, so a 1.x or 2.0.0-beta.0 generator wrote it. An empty slot is read as its 1.x value, which is what a 1.x runtime runs.",remedy:"On 1.x the warnings stand as written. On 2.0.0-beta.0 an empty slot already runs its 2.0 value, so a warning about one does not apply there; upgrade and run `pll doctor --v2` again to read the marker."};function y(e){return"string"==typeof e?`'${e}'`:JSON.stringify(e)}function v(e,t,n){const i=`\`${t.key}: ${y(c[t.key])}\``;switch(e){case"profile":return`Set ${i} or drop \`defaults: 'v1'\` once ${t.condition}.`;case"explicit":return`Drop the explicit \`${t.key}: ${y(n)}\` once ${t.condition}; 2.0 defaults to ${i}.`;case"unmarked":return`Upgrading to 2.0 sets ${i}; check first that ${t.condition}.`}}function w(e,t,n){return{code:"LP0709",level:"warning",title:e.title,detail:`${e.key} is ${y(t)}: ${e.effect} ${f[n]}`,remedy:v(n,e,t)}}function b(e){return{code:"LP0709",level:"info",title:"The inline configuration names defaults this doctor does not know",detail:`Its \`defaults\` marker is ${JSON.stringify(e)}; this version reads 'v1' and 'v2', so no row was judged.`,remedy:"Run the `pll doctor` of the package version that generated the page."}}function P(e){const t=function(e){const t=e.indexOf("var __LIVE_PREVIEW_CONFIG__=");if(-1===t)return;const n=t+28;if("["!==e[n])return;let i=0,r=!1,o="",a="";for(let t=n;t<e.length;t+=1){const n=e[t]??"";if(r)a+=n,"\\"===n?(a+=e[t+1]??"",t+=1):'"'===n&&(r=!1,o='"');else if('"'!==n){if(!/\s/u.test(n)&&((","===n&&(","===o||"["===o)||"]"===n&&","===o)&&(a+="null"),a+=n,o=n,"["===n&&(i+=1),"]"===n&&(i-=1,0===i)))break}else r=!0,a+=n}if(0===i)try{const e=JSON.parse(a);return Array.isArray(e)?e:void 0}catch{return}}(e.previewResponse.body);if(void 0===t)return[g];const n=t[h]??void 0;if(void 0!==n&&"v1"!==n&&"v2"!==n)return[b(n)];const i=n,r="v2"===i?c:u,o=p.flatMap(e=>{const n=t[l.indexOf(e.key)]??void 0,o=n??r[e.key];return o===c[e.key]?[]:[w(e,o,"v1"===i?"profile":void 0===n?"unmarked":"explicit")]});return void 0===i?[...o,m]:o}var L="__LIVE_PREVIEW_CONFIG__",S=/\bdata-payload-field\s*=/gu,R=/\bdata-payload-owner\s*=/gu,$={error:0,warning:1,info:2},k=/<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>|<style\b[^>]*>[\s\S]*?<\/style\b[^>]*>|<!--[\s\S]*?-->/giu;function O(e){let t=e;for(;;){const e=t.replace(k,"");if(e===t)return t;t=e}}function x(e,t){return e.match(t)?.length??0}function T(e){if(void 0!==e)try{return new URL(e)}catch{return}}function A(t,n){const{publicResponse:i,previewResponse:r}=t,o=function(e){if(e.status>=300&&e.status<=399){const t=e.headers.location;return{code:"LP0708",level:"error",title:`The preview request was redirected (${String(e.status)})${void 0===t?"":` to ${t}`}`,detail:"The audit does not follow redirects: the page at the end of one is a different resource, usually a login or a canonical host, and judging it would say nothing about this URL.",remedy:"Probe the final URL directly. A redirect to a login means the route needs authentication that a plain probe cannot supply."}}if(e.status<200||e.status>299)return{code:"LP0708",level:"error",title:`The preview request returned ${String(e.status)}, not a page`,detail:"Nothing below this can be judged: a status outside 2xx means the body is an error page or empty.",remedy:"Check the URL."};if(""===e.body.trim())return{code:"LP0708",level:"error",title:`The preview request returned ${String(e.status)} with an empty body`,detail:"A 2xx status with nothing in it — 204 and 205 are defined that way — is not a page, so its missing runtime and missing bindings say nothing.",remedy:"Point the audit at a route that renders a document."};const t=e.headers["content-type"];return void 0===t||t.toLowerCase().includes("html")?void 0:{code:"LP0708",level:"error",title:`The preview request returned ${t}, not HTML`,detail:"The audit reads a rendered page. A non-HTML response has no runtime and no bindings by definition, so reporting their absence would say nothing.",remedy:"Point the audit at a page route rather than an API or an asset."}}(r);if(void 0!==o)return{url:n.url,findings:[o],errors:1,warnings:0};const a=r.body.includes(L),l=[];return a||l.push({code:"LP0701",level:"warning",title:"No inline runtime in the preview response",detail:`A request carrying Sec-Fetch-Dest: iframe returned ${String(r.status)} without the inline runtime. Two readings, and this audit cannot tell them apart from the response alone: an adapter that did not recognise the request as a preview, or a consumer that starts LivePreviewClient itself and never wanted the inline build.`,remedy:"If you use an adapter, check its inject mode and whether a proxy strips Sec-Fetch-Dest. If you start the client yourself, this line is expected."}),l.push(...function(e,t,n){const i=[],r=T(n.url),o=T(n.adminOrigin),a=void 0!==o&&r?.origin===o.origin,l=function(e){if(void 0!==e)for(const t of e.split(";")){const e=t.trim();if(/^frame-ancestors(?:\s|$)/iu.test(e))return e}}(e.headers["content-security-policy"]);if(t&&void 0===l)i.push({code:"LP0702",level:"warning",title:"Preview response declares no frame-ancestors",detail:"The runtime is injected but the response carries no frame-ancestors directive, so nothing states which origins may embed this page.",remedy:"Let the adapter manage CSP, or set frame-ancestors yourself to the admin origin. Without it you are relying on the absence of a policy."});else if(void 0!==l&&void 0!==n.adminOrigin){const e=void 0!==o&&void 0!==r&&function(e,t,n){const i=e.split(/\s+/u).slice(1).filter(e=>e.length>0).map(e=>e.toLowerCase());if(0===i.length||1===i.length&&"'none'"===i[0])return!1;for(const e of i)if("'self'"===e){if(s(t,n))return!0}else{if("*"===e)return!0;if(!e.startsWith("'")&&d(e,t,n))return!0}return!1}(l,o,r);e||i.push({code:"LP0702",level:"error",title:"frame-ancestors does not admit the admin origin",detail:void 0===o?`"${n.adminOrigin}" is not an absolute URL, so no source expression can admit it.`:`Served: "${l}". Expected it to allow ${o.origin}.`,remedy:"Add the admin origin to allowedOrigins so the adapter merges it into frame-ancestors, or add it to your own policy."})}const c=e.headers["x-frame-options"];if(void 0!==c&&(/deny/iu.test(c)||/sameorigin/iu.test(c)&&!a)){const e=void 0!==o||/deny/iu.test(c);i.push(e?{code:"LP0703",level:"error",title:`X-Frame-Options: ${c} blocks the preview iframe`,detail:"This header is older than CSP and browsers honour it independently. No frame-ancestors directive can override it.",remedy:"Remove X-Frame-Options for preview responses. It is usually set by a proxy or a security middleware rather than by the app."}:{code:"LP0703",level:"warning",title:`X-Frame-Options: ${c} blocks every frame from another origin`,detail:"This header is older than CSP and browsers honour it independently, and no frame-ancestors directive can override it. SAMEORIGIN refuses a frame from another origin only, and no admin origin was given: whether it blocks this preview depends on where the admin is served, which this probe cannot see.",remedy:"Re-run with --admin <origin> to have it judged. If the admin is on another origin, remove X-Frame-Options for preview responses; a proxy or a security middleware usually sets it."})}return i}(r,a,n)),l.push(...function(e,t,n){const i=[],r=x(e,S),o=x(t,S),a=x(e,R);o>0&&i.push({code:"LP0704",level:"warning",title:`${String(o)} binding attribute(s) served to anonymous visitors`,detail:"A request with no preview intent received data-payload-field attributes. These disclose the CMS field taxonomy, and any CSS keyed on them changes public layout the moment emission is gated.",remedy:"Gate binding emission on an authorized preview context — see createPreviewBindings(), whose suppressed form emits nothing at all."}),r>50&&i.push({code:"LP0705",level:"warning",title:`${String(r)} bindings exceed the default visibility gate`,detail:`Above visibilityGateThreshold (${String(50)}) the scheduler stops writing offscreen elements and buffers them until they scroll into view. On a page nobody scrolls, that is never.`,remedy:"Raise visibilityGateThreshold if the whole page must stay live, or confirm that deferring below the fold is acceptable here."}),n&&0===r&&i.push({code:"LP0707",level:"error",title:"Preview response carries no bindings",detail:"The runtime is present but there is not one data-payload-field element for it to write into, so no edit can ever be visible on this page.",remedy:"Add binding attributes to the markup. pll-codegen --inventory lists every field the schema makes addressable."});const s=function(e){const t=e.search(/\bdata-payload-owner\s*=/u);return-1===t?0:x(e.slice(0,t),S)}(e);return a>0&&s>0&&i.push({code:"LP0706",level:"warning",title:`${String(s)} binding(s) outside every owner marker`,detail:`The page declares ${String(a)} owner marker(s), so owner scoping is in use. Bindings that belong to no owner receive nothing once scopeBindingsByOwner is on — they fail closed.`,remedy:"Give every binding an owning ancestor, or leave scopeBindingsByOwner off until they all have one. Counted before the first owner marker only, so the real number may be higher."}),i}(O(r.body),O(i.body),a)),i.body.includes(L)&&l.push({code:e.RuntimeOnPublicPage,level:"info",title:"Runtime is also served to anonymous visitors",detail:"A request with no preview intent received the inline runtime. That is correct for inject: 'always' and unintended for 'preview-only'.",remedy:""}),!0===n.v2&&l.push(...P(t)),l.sort((e,t)=>$[e.level]-$[t.level]),{url:n.url,findings:l,errors:l.filter(e=>"error"===e.level).length,warnings:l.filter(e=>"warning"===e.level).length}}var F={error:"ERROR",warning:"WARN ",info:"INFO "};function U(e){const t=[`pll doctor — ${e.url}`,""];if(0===e.findings.length)return t.push("No findings. The preview response carries the runtime, a frame-ancestors"),t.push("policy, and bindings; the public response carries none of them.",""),t.join("\n");for(const n of e.findings)t.push(`${F[n.level]} ${n.code} ${n.title}`),t.push(` ${n.detail}`),""!==n.remedy&&t.push(` → ${n.remedy}`),t.push("");return t.push(`${String(e.errors)} error(s), ${String(e.warnings)} warning(s).`,""),t.join("\n")}function I(e){const t={};return e.forEach((e,n)=>{t[n.toLowerCase()]=e}),t}async function E(e,t){const n=e.body?.getReader();if(void 0===n)return e.text();const i=new TextDecoder;let r=0,o="";for(;;){const{done:e,value:a}=await n.read();if(e)break;if(r+=a.byteLength,r>t)throw await n.cancel(),new Error(`the response body exceeds ${String(t)} bytes, which is not a page the audit can judge`);o+=i.decode(a,{stream:!0})}return o+i.decode()}function C(e){try{return new URL(e).href}catch{return`${e.replace(/\/+$/u,"")}/`}}async function _(e){const t=e.fetchImpl??function(e={}){const t=e.timeoutMs??15e3,n=e.maxBodyBytes??10485760;return async(i,r)=>{const o=e.fetchFn??globalThis.fetch;let a;try{a=await o(i,{headers:r.headers,redirect:"manual",signal:AbortSignal.timeout(t)})}catch(e){if(e instanceof Error&&"TimeoutError"===e.name)throw new Error(`no response within ${String(t/1e3)} s`,{cause:e});throw e}return{status:a.status,headers:I(a.headers),body:await E(a,n)}}}();return A({publicResponse:await t(e.url,{headers:{Accept:"text/html","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate"}}),previewResponse:await t(e.url,{headers:{Accept:"text/html","Sec-Fetch-Dest":"iframe","Sec-Fetch-Mode":"navigate",...void 0===e.adminOrigin?{}:{Referer:C(e.adminOrigin)}}})},{url:e.url,adminOrigin:e.adminOrigin,...!0===e.v2?{v2:!0}:{}})}export{e as DIAGNOSTIC_CODES,A as analyzeProbe,P as analyzeV2Readiness,U as formatReport,I as lowercaseHeaders,_ as runDoctor};
1
+ var e=Object.freeze({NoTrustedOrigin:"LP0101",ReferrerOnlyTrust:"LP0102",PluginIncompatible:"LP0103",ProfileFeatureOmitted:"LP0104",OrphanField:"LP0201",UnattributableUpdate:"LP0202",VisibilityGateDeferred:"LP0301",UnsafeAttributeWrite:"LP0401",TextTargetHasChildren:"LP0402",MissingArrayTemplate:"LP0403",StructuralItemUnkeyed:"LP0404",StructuralDuplicateKey:"LP0405",StructuralUnstableKeys:"LP0406",UnsupportedStrategy:"LP0407",UnknownValueFormat:"LP0408",SanitizerDroppedAttribute:"LP0409",UnrenderedBlockKept:"LP0410",UnfaithfulPatch:"LP0411",ServerFormatReplaced:"LP0412",UnrenderedBlockLost:"LP0413",MessageRejected:"LP0501",TokenRejected:"LP0502",ProtocolShapeUnknown:"LP0503",HandlerThrew:"LP0601",TransformThrew:"LP0602",RendererThrew:"LP0603",StartupFailed:"LP0605",ReadyFailed:"LP0606",HydrationWaitTimedOut:"LP0607",AuditRuntimeMissing:"LP0701",AuditNoFrameAncestors:"LP0702",AuditFrameOptionsBlocks:"LP0703",AuditBindingsExposed:"LP0704",AuditGateThresholdExceeded:"LP0705",AuditUnownedBindings:"LP0706",AuditNoBindings:"LP0707",AuditNotAPage:"LP0708",FragmentRequestFailed:"LP0801",FragmentResponseInvalid:"LP0802",FragmentUnauthorized:"LP0803",FragmentSuperseded:"LP0804",RouteRefreshLoop:"LP0805",FragmentStrategyUnavailable:"LP0806",UnboundChangeRefresh:"LP0807",V2ReadinessGap:"LP0709",RuntimeOnPublicPage:"LP0710"}),t={http:"80",https:"443",ws:"80",wss:"443"},r=/^([a-z][a-z0-9+.-]*):$/u,n=/^(?:([a-z][a-z0-9+.-]*):\/\/)?(\*|(?:\*\.)?[a-z0-9-]+(?:\.[a-z0-9-]+)*\.?)(?::(\*|\d+))?(?:\/.*)?$/u;function i(e){return e.protocol.slice(0,-1).toLowerCase()}function o(e){return""===e.port?t[i(e)]??"":e.port}function a(e,t){return e===t||"http"===e&&"https"===t||"ws"===e&&"wss"===t}function s(e,t){if(e.hostname!==t.hostname)return!1;const r=i(e),n=i(t);return(r===n||"http"===n&&"https"===r)&&(o(e)===o(t)||""===e.port&&""===t.port)}function d(e,t,s){const d=r.exec(e);if(void 0!==d?.[1])return a(d[1],i(t));const l=n.exec(e);if(null===l)return!1;const c=l[1],u=l[2]??"",h=l[3];return!!a(c??i(s),i(t))&&!!function(e,t){if("*"===e)return!0;const r=e.replace(/\.$/u,""),n=t.replace(/\.$/u,"");return r.startsWith("*.")?n.endsWith(r.slice(1))&&n.length>r.length-1:r===n}(u,t.hostname)&&(void 0===h?""===t.port:"*"===h||h===o(t))}var l=["allowedOrigins","serverURL","apiRoute","mergeDepth","debug","debounceMs","enableA11y","heartbeatMs","disableVisibilityGate","visibilityGateThreshold","intersectionRootMargin","disableReferrerDetection","disableLocalhostMatching","scopeBindingsByOwner","skipUnchanged","eventSourcePolicy","sanitizerPolicy","fragmentEndpoint","revealEditedField","routeStrategy","onUnboundChange","onUnfaithfulPatch","autoBind","hydration","defaults"],c=Object.freeze({disableReferrerDetection:!0,eventSourcePolicy:"parent-or-opener",skipUnchanged:!0,sanitizerPolicy:"strict"}),u=Object.freeze({disableReferrerDetection:!1,eventSourcePolicy:"any",skipUnchanged:!1,sanitizerPolicy:"compat"}),h=l.indexOf("defaults"),p=[{key:"disableReferrerDetection",title:"Referrer trust is still on",effect:"the runtime accepts the admin referer as a preview signal.",condition:"no preview flow relies on the admin referer alone"},{key:"eventSourcePolicy",title:"Messages are accepted from any window",effect:"any window that passes the origin check may post updates.",condition:"no window but the parent or opener posts updates"},{key:"sanitizerPolicy",title:"Sanitizer is in compat mode",effect:"id and every data-* pass.",condition:"rich text no longer relies on `id` or `data-*`"},{key:"skipUnchanged",title:"Unchanged bindings are re-applied every message",effect:"every binding is written again on every message.",condition:"no renderer relies on running again for a value that did not change"}],f={profile:"The script was generated with `defaults: 'v1'`.",explicit:"The script sets it explicitly.",unmarked:"The script names no defaults, so its empty slot is read as the 1.x value."},g={code:"LP0709",level:"info",title:"Could not read the inline configuration for a v2 readiness check",detail:"The preview response carried no readable `__LIVE_PREVIEW_CONFIG__` inline script.",remedy:"Run this against a page with the inline runtime (not loader mode without a preview context). A preview behind authorizePreview serves it only to a request with credentials: pass them with --header, or a signed token as ?previewToken=… in the URL."},m={code:"LP0709",level:"info",title:"The inline configuration does not say which defaults it was generated against",detail:"Scripts carry a `defaults` marker from 2.0.0-rc.1 on; this one has none, so a 1.x or 2.0.0-beta.0 generator wrote it. An empty slot is read as its 1.x value, which is what a 1.x runtime runs.",remedy:"On 1.x the warnings stand as written. On 2.0.0-beta.0 an empty slot already runs its 2.0 value, so a warning about one does not apply there; upgrade and run `pll doctor --v2` again to read the marker."};function v(e){return"string"==typeof e?`'${e}'`:JSON.stringify(e)}function w(e,t,r){const n=`\`${t.key}: ${v(c[t.key])}\``;switch(e){case"profile":return`Set ${n} or drop \`defaults: 'v1'\` once ${t.condition}.`;case"explicit":return`Drop the explicit \`${t.key}: ${v(r)}\` once ${t.condition}; 2.0 defaults to ${n}.`;case"unmarked":return`Upgrading to 2.0 sets ${n}; check first that ${t.condition}.`}}function y(e,t,r){return{code:"LP0709",level:"warning",title:e.title,detail:`${e.key} is ${v(t)}: ${e.effect} ${f[r]}`,remedy:w(r,e,t)}}function b(e){return{code:"LP0709",level:"info",title:"The inline configuration names defaults this doctor does not know",detail:`Its \`defaults\` marker is ${JSON.stringify(e)}; this version reads 'v1' and 'v2', so no row was judged.`,remedy:"Run the `pll doctor` of the package version that generated the page."}}function P(e){const t=function(e){const t=e.indexOf("var __LIVE_PREVIEW_CONFIG__=");if(-1===t)return;const r=t+28;if("["!==e[r])return;let n=0,i=!1,o="",a="";for(let t=r;t<e.length;t+=1){const r=e[t]??"";if(i)a+=r,"\\"===r?(a+=e[t+1]??"",t+=1):'"'===r&&(i=!1,o='"');else if('"'!==r){if(!/\s/u.test(r)&&((","===r&&(","===o||"["===o)||"]"===r&&","===o)&&(a+="null"),a+=r,o=r,"["===r&&(n+=1),"]"===r&&(n-=1,0===n)))break}else i=!0,a+=r}if(0===n)try{const e=JSON.parse(a);return Array.isArray(e)?e:void 0}catch{return}}(e.previewResponse.body);if(void 0===t)return[g];const r=t[h]??void 0;if(void 0!==r&&"v1"!==r&&"v2"!==r)return[b(r)];const n=r,i="v2"===n?c:u,o=p.flatMap(e=>{const r=t[l.indexOf(e.key)]??void 0,o=r??i[e.key];return o===c[e.key]?[]:[y(e,o,"v1"===n?"profile":void 0===r?"unmarked":"explicit")]});return void 0===n?[...o,m]:o}var L="__LIVE_PREVIEW_CONFIG__",$=/\bdata-payload-field\s*=/gu,k=/\bdata-payload-owner\s*=/gu,R={error:0,warning:1,info:2},S=/<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>|<style\b[^>]*>[\s\S]*?<\/style\b[^>]*>|<!--[\s\S]*?-->/giu;function O(e){let t=e;for(;;){const e=t.replace(S,"");if(e===t)return t;t=e}}function T(e,t){return e.match(t)?.length??0}function x(e){if(void 0!==e)try{return new URL(e)}catch{return}}function U(t,r){const{publicResponse:n,previewResponse:i}=t,o=function(e){if(e.status>=300&&e.status<=399){const t=e.headers.location;return{code:"LP0708",level:"error",title:`The preview request was redirected (${String(e.status)})${void 0===t?"":` to ${t}`}`,detail:"The audit does not follow redirects: the page at the end of one is a different resource, usually a login or a canonical host, and judging it would say nothing about this URL.",remedy:"The request carried the intent parameter (?preview=true, or the --param name), the way the admin's iframe loads the page, so a rule that redirects on that query redirects the iframe too. A redirect to a login means the route needs authentication that a plain probe cannot supply: pass an editor's credentials with --header, or a signed token as ?previewToken=… in the URL. Probe the final URL directly only when the redirect has nothing to do with either."}}if(e.status<200||e.status>299)return{code:"LP0708",level:"error",title:`The preview request returned ${String(e.status)}, not a page`,detail:"Nothing below this can be judged: a status outside 2xx means the body is an error page or empty.",remedy:"Check the URL."};if(""===e.body.trim())return{code:"LP0708",level:"error",title:`The preview request returned ${String(e.status)} with an empty body`,detail:"A 2xx status with nothing in it — 204 and 205 are defined that way — is not a page, so its missing runtime and missing bindings say nothing.",remedy:"Point the audit at a route that renders a document."};const t=e.headers["content-type"];return void 0===t||t.toLowerCase().includes("html")?void 0:{code:"LP0708",level:"error",title:`The preview request returned ${t}, not HTML`,detail:"The audit reads a rendered page. A non-HTML response has no runtime and no bindings by definition, so reporting their absence would say nothing.",remedy:"Point the audit at a page route rather than an API or an asset."}}(i);if(void 0!==o)return{url:r.url,findings:[o],errors:1,warnings:0};const a=i.body.includes(L),l=[];return a||l.push(!0===r.credentials?{code:"LP0701",level:"warning",title:"No inline runtime in the preview response",detail:`The preview request returned ${String(i.status)} without the inline runtime, although it carried credentials (--header, or ?previewToken= in the URL). Three readings: those credentials were not accepted — expired, issued for another page, or not what authorizePreview reads — an adapter that did not recognise the request as a preview, or a consumer that starts LivePreviewClient itself.`,remedy:"Check that the credentials are current and are the ones authorizePreview reads; if you use an adapter, check its inject mode and previewSignals. If you start the client yourself, this line is expected."}:{code:"LP0701",level:"warning",title:"No inline runtime in the preview response",detail:`The preview request returned ${String(i.status)} without the inline runtime. Three readings, and this audit cannot tell them apart from the response alone: a preview behind authorizePreview — the strict 2.0 default — which answers a request without credentials exactly like this, an adapter that did not recognise the request as a preview, or a consumer that starts LivePreviewClient itself.`,remedy:"To audit a gated preview, send what an editor's browser sends: --header 'Cookie: payload-token=…' for a Payload session; for a signed token, the URL with ?previewToken=… (the default transport, dropped from the visitor request), or --header 'x-preview-token: …' where the strategy sets transport: { kind: 'header' }. Otherwise check the adapter's inject mode and previewSignals; if you start the client yourself, this line is expected."}),l.push(...function(e,t,r){const n=[],i=x(r.url),o=x(r.adminOrigin),a=void 0!==o&&i?.origin===o.origin,l=function(e){if(void 0!==e)for(const t of e.split(";")){const e=t.trim();if(/^frame-ancestors(?:\s|$)/iu.test(e))return e}}(e.headers["content-security-policy"]);if(t&&void 0===l)n.push({code:"LP0702",level:"warning",title:"Preview response declares no frame-ancestors",detail:"The runtime is injected but the response carries no frame-ancestors directive, so nothing states which origins may embed this page.",remedy:"Let the adapter manage CSP, or set frame-ancestors yourself to the admin origin. Without it you are relying on the absence of a policy."});else if(void 0!==l&&void 0!==r.adminOrigin){const e=void 0!==o&&void 0!==i&&function(e,t,r){const n=e.split(/\s+/u).slice(1).filter(e=>e.length>0).map(e=>e.toLowerCase());if(0===n.length||1===n.length&&"'none'"===n[0])return!1;for(const e of n)if("'self'"===e){if(s(t,r))return!0}else{if("*"===e)return!0;if(!e.startsWith("'")&&d(e,t,r))return!0}return!1}(l,o,i);e||n.push({code:"LP0702",level:"error",title:"frame-ancestors does not admit the admin origin",detail:void 0===o?`"${r.adminOrigin}" is not an absolute URL, so no source expression can admit it.`:`Served: "${l}". Expected it to allow ${o.origin}.`,remedy:"Add the admin origin to allowedOrigins so the adapter merges it into frame-ancestors, or add it to your own policy."})}const c=e.headers["x-frame-options"];if(void 0!==c&&(/deny/iu.test(c)||/sameorigin/iu.test(c)&&!a)){const e=void 0!==o||/deny/iu.test(c);n.push(e?{code:"LP0703",level:"error",title:`X-Frame-Options: ${c} blocks the preview iframe`,detail:"This header is older than CSP and browsers honour it independently. No frame-ancestors directive can override it.",remedy:"Remove X-Frame-Options for preview responses. It is usually set by a proxy or a security middleware rather than by the app."}:{code:"LP0703",level:"warning",title:`X-Frame-Options: ${c} blocks every frame from another origin`,detail:"This header is older than CSP and browsers honour it independently, and no frame-ancestors directive can override it. SAMEORIGIN refuses a frame from another origin only, and no admin origin was given: whether it blocks this preview depends on where the admin is served, which this probe cannot see.",remedy:"Re-run with --admin <origin> to have it judged. If the admin is on another origin, remove X-Frame-Options for preview responses; a proxy or a security middleware usually sets it."})}return n}(i,a,r)),l.push(...function(e,t,r){const n=[],i=T(e,$),o=T(t,$),a=T(e,k);o>0&&n.push({code:"LP0704",level:"warning",title:`${String(o)} binding attribute(s) served to anonymous visitors`,detail:"A request with no preview intent received data-payload-field attributes. These disclose the CMS field taxonomy, and any CSS keyed on them changes public layout the moment emission is gated.",remedy:"Gate binding emission on an authorized preview context — see createPreviewBindings(), whose suppressed form emits nothing at all."}),i>50&&n.push({code:"LP0705",level:"warning",title:`${String(i)} bindings exceed the default visibility gate`,detail:`Above visibilityGateThreshold (${String(50)}) the scheduler stops writing offscreen elements and buffers them until they scroll into view. On a page nobody scrolls, that is never.`,remedy:"Raise visibilityGateThreshold if the whole page must stay live, or confirm that deferring below the fold is acceptable here."}),r&&0===i&&n.push({code:"LP0707",level:"error",title:"Preview response carries no bindings",detail:"The runtime is present but there is not one data-payload-field element for it to write into, so no edit can ever be visible on this page.",remedy:"Add binding attributes to the markup. pll-codegen --inventory lists every field the schema makes addressable."});const s=function(e){const t=e.search(/\bdata-payload-owner\s*=/u);return-1===t?0:T(e.slice(0,t),$)}(e);return a>0&&s>0&&n.push({code:"LP0706",level:"warning",title:`${String(s)} binding(s) outside every owner marker`,detail:`The page declares ${String(a)} owner marker(s), so owner scoping is in use. Bindings that belong to no owner receive nothing once scopeBindingsByOwner is on — they fail closed.`,remedy:"Give every binding an owning ancestor, or leave scopeBindingsByOwner off until they all have one. Counted before the first owner marker only, so the real number may be higher."}),n}(O(i.body),O(n.body),a)),n.body.includes(L)&&l.push({code:e.RuntimeOnPublicPage,level:"info",title:"Runtime is also served to anonymous visitors",detail:"A request with no preview intent received the inline runtime. That is correct for inject: 'always' and unintended for 'preview-only'.",remedy:""}),!0===r.v2&&l.push(...P(t)),l.sort((e,t)=>R[e.level]-R[t.level]),{url:r.url,findings:l,errors:l.filter(e=>"error"===e.level).length,warnings:l.filter(e=>"warning"===e.level).length}}var A={error:"ERROR",warning:"WARN ",info:"INFO "};function F(e){const t=[`pll doctor — ${e.url}`,""];if(0===e.findings.length)return t.push("No findings. The preview response carries the runtime, a frame-ancestors"),t.push("policy, and bindings; the public response carries none of them.",""),t.join("\n");for(const r of e.findings)t.push(`${A[r.level]} ${r.code} ${r.title}`),t.push(` ${r.detail}`),""!==r.remedy&&t.push(` → ${r.remedy}`),t.push("");return t.push(`${String(e.errors)} error(s), ${String(e.warnings)} warning(s).`,""),t.join("\n")}var C=["preview","draft","livePreview"];function j(e){const t=e.indexOf("#"),r=-1===t?"":e.slice(t),n=-1===t?e:e.slice(0,t),i=n.indexOf("?");return-1===i?{base:n,parts:[],hash:r}:{base:n.slice(0,i),parts:n.slice(i+1).split("&"),hash:r}}function I(e){const t=e.indexOf("="),r=e=>{try{return decodeURIComponent(e.replace(/\+/gu," "))}catch{return e}};return-1===t?[r(e),""]:[r(e.slice(0,t)),r(e.slice(t+1))]}var E="previewToken";function z(e){const{base:t,parts:r,hash:n}=j(e),i=r.map(e=>{const t=e.indexOf("=");return-1===t||I(e)[0]!==E?e:`${e.slice(0,t)}=…`});return i.some((e,t)=>e!==r[t])?`${t}?${i.join("&")}${n}`:e}function N(e){const t={};return e.forEach((e,r)=>{t[r.toLowerCase()]=e}),t}async function _(e,t){const r=e.body?.getReader();if(void 0===r)return e.text();const n=new TextDecoder;let i=0,o="";for(;;){const{done:e,value:a}=await r.read();if(e)break;if(i+=a.byteLength,i>t)throw await r.cancel(),new Error(`the response body exceeds ${String(t)} bytes, which is not a page the audit can judge`);o+=n.decode(a,{stream:!0})}return o+n.decode()}function B(e){try{return new URL(e).href}catch{return`${e.replace(/\/+$/u,"")}/`}}async function q(e){const t=e.fetchImpl??function(e={}){const t=e.timeoutMs??15e3,r=e.maxBodyBytes??10485760;return async(n,i)=>{const o=e.fetchFn??globalThis.fetch;let a;try{a=await o(n,{headers:i.headers,redirect:"manual",signal:AbortSignal.timeout(t)})}catch(e){if(e instanceof Error&&"TimeoutError"===e.name)throw new Error(`no response within ${String(t/1e3)} s`,{cause:e});throw e}return{status:a.status,headers:N(a.headers),body:await _(a,r)}}}(),r=void 0===e.previewQueryParams||0===e.previewQueryParams.length?C:e.previewQueryParams,n=await t(function(e,t){const{base:r,parts:n,hash:i}=j(e),o=/* @__PURE__ */new Set([...t,E]),a=n.filter(e=>!o.has(I(e)[0]));return a.length===n.length?e:`${r}${0===a.length?"":`?${a.join("&")}`}${i}`}(e.url,r),{headers:{Accept:"text/html","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate"}}),i={Accept:"text/html","Sec-Fetch-Dest":"iframe","Sec-Fetch-Mode":"navigate",...void 0===e.adminOrigin?{}:{Referer:B(e.adminOrigin)}},o=function(e,t){const r=new Set(Object.keys(t).map(e=>e.toLowerCase()));return Object.fromEntries(Object.entries(e??{}).filter(([e])=>!r.has(e.toLowerCase())))}(e.previewHeaders,i),a=await t(function(e,t){const{base:r,parts:n,hash:i}=j(e);if(n.some(e=>{const[r,n]=I(e);return t.includes(r)&&function(e){return"true"===e||"1"===e}(n)}))return e;const o=n.join("&"),a=""===o?"?":o.endsWith("&")?"":"&",s=`${encodeURIComponent(t[0]??"preview")}=true`;return`${r}${0===n.length?"?":`?${o}${a}`}${s}${i}`}(e.url,r),{headers:{...o,...i}});return U({publicResponse:n,previewResponse:a},{url:z(e.url),adminOrigin:e.adminOrigin,...!0===e.v2?{v2:!0}:{},...Object.keys(o).length>0||(s=e.url,j(s).parts.some(e=>{const[t,r]=I(e);return t===E&&""!==r}))?{credentials:!0}:{}});var s}export{e as DIAGNOSTIC_CODES,U as analyzeProbe,P as analyzeV2Readiness,F as formatReport,N as lowercaseHeaders,q as runDoctor};
2
2
  //# sourceMappingURL=doctor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","names":["DIAGNOSTIC_CODES","Object","freeze","NoTrustedOrigin","ReferrerOnlyTrust","PluginIncompatible","ProfileFeatureOmitted","OrphanField","UnattributableUpdate","VisibilityGateDeferred","UnsafeAttributeWrite","TextTargetHasChildren","MissingArrayTemplate","StructuralItemUnkeyed","StructuralDuplicateKey","StructuralUnstableKeys","UnsupportedStrategy","UnknownValueFormat","SanitizerDroppedAttribute","UnrenderedBlockKept","UnfaithfulPatch","ServerFormatReplaced","UnrenderedBlockLost","MessageRejected","TokenRejected","ProtocolShapeUnknown","HandlerThrew","TransformThrew","RendererThrew","StartupFailed","ReadyFailed","HydrationWaitTimedOut","AuditRuntimeMissing","AuditNoFrameAncestors","AuditFrameOptionsBlocks","AuditBindingsExposed","AuditGateThresholdExceeded","AuditUnownedBindings","AuditNoBindings","AuditNotAPage","FragmentRequestFailed","FragmentResponseInvalid","FragmentUnauthorized","FragmentSuperseded","RouteRefreshLoop","FragmentStrategyUnavailable","UnboundChangeRefresh","V2ReadinessGap","RuntimeOnPublicPage","DEFAULT_PORT","http","https","ws","wss","SCHEME_SOURCE","HOST_SOURCE","schemeOf","url","protocol","slice","toLowerCase","portOf","port","schemeMatches","expression","actual","isSelf","admin","page","hostname","adminScheme","pageScheme","sourceMatches","source","scheme","exec","host","expressionScheme","expressionHost","expressionPort","pattern","wanted","replace","startsWith","endsWith","length","hostMatches","INLINE_CONFIG_KEYS","V2_RUNTIME_DEFAULTS","disableReferrerDetection","eventSourcePolicy","skipUnchanged","sanitizerPolicy","V1_RUNTIME_DEFAULTS","DEFAULTS_SLOT","indexOf","ROWS","key","title","effect","condition","ORIGIN_DETAIL","profile","explicit","unmarked","UNREADABLE","code","level","detail","remedy","UNMARKED","literal","value","JSON","stringify","remedyFor","origin","row","gap","unknownGeneration","marker","analyzeV2Readiness","probe","config","body","start","from","CONFIG_MARKER","depth","inString","previous","json","index","char","test","parse","Array","isArray","readInlineConfig","previewResponse","generation","fallback","findings","flatMap","slot","RUNTIME_MARKER","BINDING_ATTRIBUTE","OWNER_ATTRIBUTE","LEVEL_ORDER","error","warning","info","NON_MARKUP","visibleMarkup","markup","stripped","count","haystack","match","tryUrl","URL","analyzeProbe","context","publicResponse","pub","preview","notAPage","status","location","headers","String","trim","contentType","includes","describeNonPage","errors","warnings","runtimeInPreview","push","adminOrigin","sameOrigin","frameAncestors","csp","directive","split","trimmed","frameAncestorsOf","admitted","sources","filter","map","frameAncestorsAdmits","frameOptions","judged","framingFindings","previewMarkup","publicMarkup","runtime","previewBindings","publicBindings","owners","unowned","firstOwner","search","unownedBindingsBeforeFirstOwner","bindingFindings","v2","sort","a","b","finding","LEVEL_LABEL","formatReport","report","lines","join","lowercaseHeaders","out","forEach","async","readBody","response","limit","reader","getReader","text","decoder","TextDecoder","received","done","read","byteLength","cancel","Error","decode","stream","previewReferer","href","runDoctor","options","fetchImpl","timeoutMs","maxBodyBytes","init","fetchFn","globalThis","fetch","redirect","signal","AbortSignal","timeout","name","cause","createDefaultFetch","Accept","Referer"],"sources":["../src/core/diagnostic-codes.ts","../src/doctor/csp.ts","../src/types/inline-config.ts","../src/types/defaults-profile.ts","../src/doctor/readiness.ts","../src/doctor/analyze.ts","../src/doctor/format.ts","../src/doctor/probe.ts"],"sourcesContent":["/**\n * Every diagnostic code the runtime and tools emit, so a consumer can branch\n * on `e.code` instead of matching message text. A code is never reused for a\n * different meaning; a retired one stays reserved.\n *\n * Browser-side reporting sites write the literal (`code: 'LP0603'`) rather than\n * reading it from this record: the frozen table would then ship in the inline\n * runtime for the sake of six characters — it did, 1 287 B in every page, for\n * the one read Z6 left in the strategy runner, and `diagnostic-codes.test.ts`\n * now holds the artifact free of it. Server-side tools may import it.\n */\n\n/**\n * Every diagnostic code the runtime can report, by name.\n *\n * ```ts\n * client.events.on('error', (e) => {\n * if (e.code === DIAGNOSTIC_CODES.TransformThrew) reportToSentry(e.error);\n * });\n * ```\n */\nexport const DIAGNOSTIC_CODES = Object.freeze({\n /** No trusted origin configured in production; nothing will be accepted. */\n NoTrustedOrigin: 'LP0101',\n /** Origin trust rests on `document.referrer`, so any framing site is trusted. */\n ReferrerOnlyTrust: 'LP0102',\n /** A plugin declares a runtime range this runtime does not satisfy; it was refused. */\n PluginIncompatible: 'LP0103',\n /** The page carries the lean runtime and needs a feature that profile leaves out. */\n ProfileFeatureOmitted: 'LP0104',\n\n /** An update named a field with no binding anchor on the page. */\n OrphanField: 'LP0201',\n /** Owner scoping is on and the update names no document it could belong to. */\n UnattributableUpdate: 'LP0202',\n\n /** The visibility gate held offscreen writes back until they scroll into view. */\n VisibilityGateDeferred: 'LP0301',\n\n /** A value was refused because the attribute or the value itself is unsafe. */\n UnsafeAttributeWrite: 'LP0401',\n /** A text element has structured children, so replacing its text was skipped. */\n TextTargetHasChildren: 'LP0402',\n /** A structural container has no array template, so the update was skipped. */\n MissingArrayTemplate: 'LP0403',\n /** A structural item has no `id`; it pairs positionally, so an insert re-renders every row after it. */\n StructuralItemUnkeyed: 'LP0404',\n /** Two structural items share a key; later ones pair positionally. */\n StructuralDuplicateKey: 'LP0405',\n /** Every structural key changed while the length did not; the source generates keys per message. */\n StructuralUnstableKeys: 'LP0406',\n /** A binding asks for a delivery strategy this release does not have; it is left unchanged. */\n UnsupportedStrategy: 'LP0407',\n /** `data-payload-format` names a format the vocabulary does not contain; the default formatting was used instead. */\n UnknownValueFormat: 'LP0408',\n /** The strict sanitizer removed an attribute the 1.x `'compat'` policy kept. */\n SanitizerDroppedAttribute: 'LP0409',\n /** A Lexical block has no renderer; the markup the server rendered for it is kept instead. */\n UnrenderedBlockKept: 'LP0410',\n /** A patch cannot reach what the server would have drawn; `onUnfaithfulPatch` decided what happens next. */\n UnfaithfulPatch: 'LP0411',\n /** The first write to a date, number or checkbox binding replaced a different reading of the same value. */\n ServerFormatReplaced: 'LP0412',\n /** A Lexical block has no renderer, and the write could not keep the markup the server rendered for it; it is gone. */\n UnrenderedBlockLost: 'LP0413',\n\n /** A message was rejected before it reached the update pipeline. */\n MessageRejected: 'LP0501',\n /** A preview token was rejected. */\n TokenRejected: 'LP0502',\n /** A trusted origin sent a message this protocol version does not recognise. */\n ProtocolShapeUnknown: 'LP0503',\n\n /** A consumer event handler threw. */\n HandlerThrew: 'LP0601',\n /** A consumer transform threw; the original value was kept. */\n TransformThrew: 'LP0602',\n /** A renderer threw while writing a value. */\n RendererThrew: 'LP0603',\n // LP0604 is reserved and unassigned; codes are never reused or renumbered.\n /** Runtime startup failed. */\n StartupFailed: 'LP0605',\n /** Sending the ready handshake failed. */\n ReadyFailed: 'LP0606',\n /** The page declared hydration, but no React commit or Vue mount came within the cap; the runtime started without waiting. */\n HydrationWaitTimedOut: 'LP0607',\n\n /** The audit found no runtime in a response that carried preview intent. */\n AuditRuntimeMissing: 'LP0701',\n /** The preview response declares no `frame-ancestors`. */\n AuditNoFrameAncestors: 'LP0702',\n /** `X-Frame-Options` forbids framing, which no CSP can undo; `SAMEORIGIN` forbids it from another origin only. */\n AuditFrameOptionsBlocks: 'LP0703',\n /** Binding attributes are served to anonymous visitors. */\n AuditBindingsExposed: 'LP0704',\n /** More bindings than the default visibility gate will write eagerly. */\n AuditGateThresholdExceeded: 'LP0705',\n /** Owner markers exist, but some bindings are outside all of them. */\n AuditUnownedBindings: 'LP0706',\n /** The preview response carries no bindings at all. */\n AuditNoBindings: 'LP0707',\n /** The URL did not return an HTML page, so nothing else can be judged. */\n AuditNotAPage: 'LP0708',\n /** A fragment request failed (network, timeout, server error); the boundary was patched instead. */\n FragmentRequestFailed: 'LP0801',\n /** The fragment response had the wrong content type or shape; the boundary was patched instead. */\n FragmentResponseInvalid: 'LP0802',\n /** The fragment endpoint refused the preview (not authorized); the boundary was patched instead. */\n FragmentUnauthorized: 'LP0803',\n /** A fragment response arrived for a revision that was already superseded and was discarded. */\n FragmentSuperseded: 'LP0804',\n /** A route refresh was refused: the loop guard, or the strategy's minimum interval. */\n RouteRefreshLoop: 'LP0805',\n /** A boundary asks for the fragment strategy but no fragment handler is configured; it is patched. */\n FragmentStrategyUnavailable: 'LP0806',\n /** A revision changed a field with no binding; `onUnfaithfulPatch` refreshed the route. */\n UnboundChangeRefresh: 'LP0807',\n /** A readiness row is not yet at its 2.0 value; `pll doctor --v2` reports it. */\n V2ReadinessGap: 'LP0709',\n /** The preview runtime is served to anonymous visitors, not only inside the admin frame. */\n RuntimeOnPublicPage: 'LP0710',\n} as const);\n\n/** A diagnostic code, as it appears in log output and on the `error` event. */\nexport type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[keyof typeof DIAGNOSTIC_CODES];\n","/**\n * `frame-ancestors` matching by the CSP3 source-expression rules. The audit\n * judges origins, so path parts of a host-source are ignored.\n */\n\nconst DEFAULT_PORT: Readonly<Record<string, string>> = {\n http: '80',\n https: '443',\n ws: '80',\n wss: '443',\n};\nconst SCHEME_SOURCE = /^([a-z][a-z0-9+.-]*):$/u;\nconst HOST_SOURCE =\n /^(?:([a-z][a-z0-9+.-]*):\\/\\/)?(\\*|(?:\\*\\.)?[a-z0-9-]+(?:\\.[a-z0-9-]+)*\\.?)(?::(\\*|\\d+))?(?:\\/.*)?$/u;\n\n/** The `frame-ancestors` directive of a policy, verbatim, or `undefined` when it has none. */\nexport function frameAncestorsOf(csp: string | undefined): string | undefined {\n if (csp === undefined) return undefined;\n for (const directive of csp.split(';')) {\n const trimmed = directive.trim();\n if (/^frame-ancestors(?:\\s|$)/iu.test(trimmed)) return trimmed;\n }\n return undefined;\n}\n\nfunction schemeOf(url: URL): string {\n return url.protocol.slice(0, -1).toLowerCase();\n}\n\nfunction portOf(url: URL): string {\n return url.port === '' ? (DEFAULT_PORT[schemeOf(url)] ?? '') : url.port;\n}\n\nfunction schemeMatches(expression: string, actual: string): boolean {\n return (\n expression === actual ||\n (expression === 'http' && actual === 'https') ||\n (expression === 'ws' && actual === 'wss')\n );\n}\n\nfunction hostMatches(pattern: string, host: string): boolean {\n if (pattern === '*') return true;\n const wanted = pattern.replace(/\\.$/u, '');\n const actual = host.replace(/\\.$/u, '');\n if (wanted.startsWith('*.')) {\n return actual.endsWith(wanted.slice(1)) && actual.length > wanted.length - 1;\n }\n return wanted === actual;\n}\n\nfunction isSelf(admin: URL, page: URL): boolean {\n if (admin.hostname !== page.hostname) return false;\n const adminScheme = schemeOf(admin);\n const pageScheme = schemeOf(page);\n if (adminScheme !== pageScheme && !(pageScheme === 'http' && adminScheme === 'https')) {\n return false;\n }\n return portOf(admin) === portOf(page) || (admin.port === '' && page.port === '');\n}\n\nfunction sourceMatches(source: string, admin: URL, page: URL): boolean {\n const scheme = SCHEME_SOURCE.exec(source);\n if (scheme?.[1] !== undefined) return schemeMatches(scheme[1], schemeOf(admin));\n const host = HOST_SOURCE.exec(source);\n if (host === null) return false;\n const expressionScheme = host[1];\n const expressionHost = host[2] ?? '';\n const expressionPort = host[3];\n const requiredScheme = expressionScheme ?? schemeOf(page);\n if (!schemeMatches(requiredScheme, schemeOf(admin))) return false;\n if (!hostMatches(expressionHost, admin.hostname)) return false;\n if (expressionPort === undefined) return admin.port === '';\n return expressionPort === '*' || expressionPort === portOf(admin);\n}\n\n/** Whether the directive admits `admin` as an ancestor of `page`. */\nexport function frameAncestorsAdmits(directive: string, admin: URL, page: URL): boolean {\n const sources = directive\n .split(/\\s+/u)\n .slice(1)\n .filter((source) => source.length > 0)\n .map((source) => source.toLowerCase());\n if (sources.length === 0 || (sources.length === 1 && sources[0] === \"'none'\")) return false;\n for (const source of sources) {\n if (source === \"'self'\") {\n if (isSelf(admin, page)) return true;\n } else if (source === '*') {\n return true;\n } else if (!source.startsWith(\"'\") && sourceMatches(source, admin, page)) {\n return true;\n }\n }\n return false;\n}\n","/**\n * The inline script's configuration and its positional wire order: the\n * generator writes the tuple, `src/core/runtime.ts` destructures it, and\n * `INLINE_CONFIG_KEYS` is where they agree. Append only, never reorder.\n */\n\nimport type { DefaultsProfile } from '@/types/defaults-profile';\n\nexport interface InlineScriptConfig {\n /** Trusted admin origins, merged with the detected ones. */\n readonly allowedOrigins?: readonly string[];\n /** Payload origin; every update is then re-fetched through the REST API with credentials. Requires `mergeDepth`. */\n readonly serverURL?: string;\n /** REST route prefix used with `serverURL`. Default `/api`. */\n readonly apiRoute?: string;\n /** Population depth used with `serverURL`. Required with `serverURL` unless `defaults: 'v1'`. */\n readonly mergeDepth?: number;\n /** Verbose console logging. Default `false`. */\n readonly debug?: boolean;\n /** Debounce window for incoming updates in ms. Default `50`. */\n readonly debounceMs?: number;\n /** Mount the screen-reader live region. Default `true`. */\n readonly enableA11y?: boolean;\n /** Heartbeat timeout in ms; `0` disables it (default), because the admin posts only on edits. */\n readonly heartbeatMs?: number;\n /** Apply every update regardless of viewport position. Default `false`. */\n readonly disableVisibilityGate?: boolean;\n /** Cache size above which off-screen updates wait for intersection. Default `50`. */\n readonly visibilityGateThreshold?: number;\n /** `rootMargin` of the IntersectionObserver. Default `'200px'`. */\n readonly intersectionRootMargin?: string;\n /** Ignore `document.referrer` as an origin source. Default `true`; `defaults: 'v1'` sets `false`. */\n readonly disableReferrerDetection?: boolean;\n /** Disable the dev-mode localhost matcher (any port on `localhost`/`127.0.0.1`). Default `false`. */\n readonly disableLocalhostMatching?: boolean;\n /** Update only bindings under a matching `data-payload-owner`. Default `false`. */\n readonly scopeBindingsByOwner?: boolean;\n /** Skip bindings whose value did not change. Default `true`; `defaults: 'v1'` sets `false`. */\n readonly skipUnchanged?: boolean;\n /** Scroll the preview to the field being edited. Default `false`. */\n readonly revealEditedField?: boolean;\n /** Which windows may post updates. Default `'parent-or-opener'`; `defaults: 'v1'` sets `'any'`. */\n readonly eventSourcePolicy?: 'any' | 'parent-or-opener';\n /** Sanitizer policy for rich text and HTML writes. Default `'strict'`; `defaults: 'v1'` sets `'compat'`. */\n readonly sanitizerPolicy?: 'compat' | 'strict';\n /** Same-origin path of a fragment endpoint; the script then carries the fragment client ahead of the runtime (ADR 0011). */\n readonly fragmentEndpoint?: string;\n /**\n * Carry the route strategy, so a binding in `<head>` or one marked\n * `data-payload-strategy=\"route\"` refreshes the route. `fragmentEndpoint`\n * implies it — the fragment prelude already contains it. Default `false`.\n */\n readonly routeStrategy?: boolean;\n /**\n * The 2.0 name for `onUnfaithfulPatch`, kept until 3.0. `'route'` means\n * `'escalate'`, `'ignore'` means `'ignore'`.\n *\n * @deprecated Renamed to `onUnfaithfulPatch`.\n */\n readonly onUnboundChange?: 'ignore' | 'route';\n /**\n * What to do when the runtime knows a patch cannot reach what the server\n * would have drawn. `'escalate'` (the default) hands the region to the\n * fragment strategy when a boundary covers it and to the route otherwise, so\n * it needs `fragmentEndpoint` or `routeStrategy` to do anything. `'warn'`\n * reports LP0411 and keeps the patch; `'ignore'` keeps it silently.\n */\n readonly onUnfaithfulPatch?: 'ignore' | 'warn' | 'escalate';\n /**\n * Find bindings by value on the connection's first message (ADR 0014): a\n * scalar whose value is the whole content of exactly one element is bound\n * to it as if `data-payload-field` stood there. Default `'off'`.\n */\n readonly autoBind?: 'off' | 'unique';\n /**\n * The framework that hydrates this page (ADR 0015). Under `'react'` the\n * runtime holds its first write until React has committed the tree that\n * holds the bindings — otherwise React finds markup it did not render,\n * throws `Hydration failed` and regenerates the tree, and the write is gone.\n * Under `'vue'` it holds the write until Vue has mounted the app around them\n * — otherwise Vue's hydration repairs the write back to the server's value,\n * quietly. The Next.js and Nuxt adapters set it on every script they emit; a\n * page built by hand with `generateInlineScript()` may. Omitted: the runtime\n * starts on `DOMContentLoaded`, as on a static page.\n */\n readonly hydration?: 'react' | 'vue';\n /**\n * Which defaults the omitted options fall back to, resolved by the generator\n * rather than the runtime, whose own fallbacks are the 2.0 rows: `'v1'`\n * writes its four runtime rows into their slots (an explicit option still\n * wins) and relaxes the `mergeDepth` check. The resolved value always\n * travels, in the last slot, so a reader of the served page knows what an\n * empty slot means instead of guessing it — `pll doctor --v2` reads it. The\n * runtime does not: every row the profile decides is already in its slot.\n */\n readonly defaults?: DefaultsProfile;\n /**\n * A runtime artifact to embed instead of the full one — today only\n * `LEAN_RUNTIME` from `payload-live-preview/lean`, which leaves out the\n * strategies, the keyed morph, the structural arrays, the item templates and\n * the announcer (about 5.5 KB gzip less on the page) and reports LP0104 when\n * a page needs one of them.\n *\n * It is an imported value rather than a `profile: 'lean'` string on purpose:\n * a second artifact behind a string option would sit in every build that can\n * reach the generator, which measured +24 KB gzip in each adapter entry. This\n * way the bytes follow the import.\n *\n * Not serialized: it decides which bytes are emitted, not how they behave.\n */\n readonly runtime?: RuntimeArtifact;\n}\n\n/**\n * A runtime build this package produces. `source` is the IIFE the page runs;\n * the two digests describe the same bytes as a servable asset, so asset\n * delivery can hash-name and SRI-verify whichever artifact was chosen without\n * hashing anything at request time.\n */\nexport interface RuntimeArtifact {\n readonly profile: 'lean';\n readonly source: string;\n /** Short content hash, for the asset's file name. */\n readonly contentHash: string;\n /** `sha384-…`, for the bootstrap's `integrity` attribute. */\n readonly integrity: string;\n}\n\n/** Keys that travel in the wire tuple, in slot order. */\nexport const INLINE_CONFIG_KEYS = [\n 'allowedOrigins',\n 'serverURL',\n 'apiRoute',\n 'mergeDepth',\n 'debug',\n 'debounceMs',\n 'enableA11y',\n 'heartbeatMs',\n 'disableVisibilityGate',\n 'visibilityGateThreshold',\n 'intersectionRootMargin',\n 'disableReferrerDetection',\n 'disableLocalhostMatching',\n 'scopeBindingsByOwner',\n 'skipUnchanged',\n 'eventSourcePolicy',\n 'sanitizerPolicy',\n 'fragmentEndpoint',\n 'revealEditedField',\n 'routeStrategy',\n 'onUnboundChange',\n 'onUnfaithfulPatch',\n 'autoBind',\n 'hydration',\n 'defaults',\n] as const satisfies readonly Exclude<keyof InlineScriptConfig, 'runtime'>[];\n","/**\n * The two defaults profiles, row by row. 2.0 ships `v2`; `defaults: 'v1'` opts\n * back into the 1.x table for a staged migration. Each row belongs to exactly\n * one side — the adapter's request decision or the browser runtime — and a\n * test asserts the profile assigns every readiness row that has an option, so\n * a row added later cannot be forgotten. See ADR 0007.\n *\n * It lives in the leaf `types` domain, like `merge-depth.ts`, because the\n * inline generator resolves the profile too, and the generator is server-side\n * code that may not reach the browser runtime in `core`.\n */\n\n/** `'v2'` is the 2.0 default; `'v1'` is the 1.x table. */\nexport type DefaultsProfile = 'v1' | 'v2';\n\n/** How the runtime decides which windows may post updates. */\nexport type EventSourcePolicy = 'any' | 'parent-or-opener';\n\n/** Readiness rows that are options, keyed by the option they set. */\nexport const READINESS_ROWS = Object.freeze({\n strict: 'production response changes require an authorized context',\n previewSignals: 'query-only intent signal',\n disableReferrerDetection: 'referrer trust off outside local dev',\n eventSourcePolicy: 'messages must come from parent/opener',\n skipUnchanged: 'skip unchanged bindings by default',\n sanitizerPolicy: 'hardened sanitizer id/data-* policy',\n} as const);\n\n/** Options an adapter's request decision reads from the profile. */\nexport interface AdapterProfileDefaults {\n readonly strict: boolean;\n readonly previewSignals: readonly ('query' | 'fetch-dest' | 'referer')[];\n}\n\n/** Options the browser runtime reads from the profile (through the inline configuration). */\nexport interface RuntimeProfileDefaults {\n readonly disableReferrerDetection: boolean;\n readonly eventSourcePolicy: EventSourcePolicy;\n readonly skipUnchanged: boolean;\n readonly sanitizerPolicy: 'compat' | 'strict';\n}\n\nexport const V2_ADAPTER_DEFAULTS: AdapterProfileDefaults = Object.freeze({\n strict: true,\n previewSignals: Object.freeze(['query'] as const),\n});\n\nexport const V2_RUNTIME_DEFAULTS: RuntimeProfileDefaults = Object.freeze({\n disableReferrerDetection: true,\n eventSourcePolicy: 'parent-or-opener',\n skipUnchanged: true,\n sanitizerPolicy: 'strict',\n});\n\n/** The 1.x table, spelled out so the profiles compare row by row. */\nexport const V1_ADAPTER_DEFAULTS: AdapterProfileDefaults = Object.freeze({\n strict: false,\n previewSignals: Object.freeze(['query', 'fetch-dest', 'referer'] as const),\n});\n\nexport const V1_RUNTIME_DEFAULTS: RuntimeProfileDefaults = Object.freeze({\n disableReferrerDetection: false,\n eventSourcePolicy: 'any',\n skipUnchanged: false,\n sanitizerPolicy: 'compat',\n});\n\n/** Only an explicit `'v1'` opts out; everything else gets the hardened rows. */\nexport function adapterDefaultsFor(profile: DefaultsProfile | undefined): AdapterProfileDefaults {\n return profile === 'v1' ? V1_ADAPTER_DEFAULTS : V2_ADAPTER_DEFAULTS;\n}\n\nexport function runtimeDefaultsFor(profile: DefaultsProfile | undefined): RuntimeProfileDefaults {\n return profile === 'v1' ? V1_RUNTIME_DEFAULTS : V2_RUNTIME_DEFAULTS;\n}\n","/**\n * `pll doctor --v2`: read the served inline configuration and report each\n * runtime row still at its 1.x value (ADR 0007).\n *\n * An empty slot means whatever the generation that wrote the script meant by\n * it, and that is exactly what changed between 1.x and 2.0. So the script\n * names the defaults it was resolved against, in its last slot, and this reads\n * the answer instead of guessing it. A script without the marker predates it —\n * 1.x or 2.0.0-beta.0 — and is read as 1.x, with a line that says so.\n */\nimport { INLINE_CONFIG_KEYS } from '@/types/inline-config';\nimport {\n V1_RUNTIME_DEFAULTS,\n V2_RUNTIME_DEFAULTS,\n type RuntimeProfileDefaults,\n} from '@/types/defaults-profile';\nimport type { DoctorFinding, DoctorProbe } from './types';\n\nconst CONFIG_MARKER = 'var __LIVE_PREVIEW_CONFIG__=';\nconst DEFAULTS_SLOT = INLINE_CONFIG_KEYS.indexOf('defaults');\n\n/** A runtime row of the readiness table, as a finding words it. */\ninterface ReadinessRow {\n readonly key: keyof RuntimeProfileDefaults;\n readonly title: string;\n /** What the 1.x value does. */\n readonly effect: string;\n /** What has to hold before the row can take its 2.0 value. */\n readonly condition: string;\n}\n\nconst ROWS: readonly ReadinessRow[] = [\n {\n key: 'disableReferrerDetection',\n title: 'Referrer trust is still on',\n effect: 'the runtime accepts the admin referer as a preview signal.',\n condition: 'no preview flow relies on the admin referer alone',\n },\n {\n key: 'eventSourcePolicy',\n title: 'Messages are accepted from any window',\n effect: 'any window that passes the origin check may post updates.',\n condition: 'no window but the parent or opener posts updates',\n },\n {\n key: 'sanitizerPolicy',\n title: 'Sanitizer is in compat mode',\n effect: 'id and every data-* pass.',\n condition: 'rich text no longer relies on `id` or `data-*`',\n },\n {\n key: 'skipUnchanged',\n title: 'Unchanged bindings are re-applied every message',\n effect: 'every binding is written again on every message.',\n condition: 'no renderer relies on running again for a value that did not change',\n },\n];\n\n/** Where a row's value came from, which decides what the reader can change. */\ntype Origin = 'profile' | 'explicit' | 'unmarked';\n\nconst ORIGIN_DETAIL: Readonly<Record<Origin, string>> = {\n profile: \"The script was generated with `defaults: 'v1'`.\",\n explicit: 'The script sets it explicitly.',\n unmarked: 'The script names no defaults, so its empty slot is read as the 1.x value.',\n};\n\nconst UNREADABLE: DoctorFinding = {\n code: 'LP0709',\n level: 'info',\n title: 'Could not read the inline configuration for a v2 readiness check',\n detail: 'The preview response carried no readable `__LIVE_PREVIEW_CONFIG__` inline script.',\n remedy:\n 'Run this against a page with the inline runtime (not loader mode without a preview context).',\n};\n\nconst UNMARKED: DoctorFinding = {\n code: 'LP0709',\n level: 'info',\n title: 'The inline configuration does not say which defaults it was generated against',\n detail:\n 'Scripts carry a `defaults` marker from 2.0.0-rc.1 on; this one has none, so a 1.x or 2.0.0-beta.0 generator wrote it. An empty slot is read as its 1.x value, which is what a 1.x runtime runs.',\n remedy:\n 'On 1.x the warnings stand as written. On 2.0.0-beta.0 an empty slot already runs its 2.0 value, so a warning about one does not apply there; upgrade and run `pll doctor --v2` again to read the marker.',\n};\n\n/**\n * The array literal after the marker, read as JSON. The generator writes `,,`\n * for unset slots, which JSON cannot express, so elisions outside strings\n * become `null`. Never evaluated: a page controls this text.\n */\nexport function readInlineConfig(body: string): readonly unknown[] | undefined {\n const start = body.indexOf(CONFIG_MARKER);\n if (start === -1) return undefined;\n const from = start + CONFIG_MARKER.length;\n if (body[from] !== '[') return undefined;\n let depth = 0;\n let inString = false;\n let previous = '';\n let json = '';\n for (let index = from; index < body.length; index += 1) {\n const char = body[index] ?? '';\n if (inString) {\n json += char;\n if (char === '\\\\') {\n json += body[index + 1] ?? '';\n index += 1;\n } else if (char === '\"') {\n inString = false;\n // A closed string is a value: without this, the next `,` reads as an elision.\n previous = '\"';\n }\n continue;\n }\n if (char === '\"') {\n inString = true;\n json += char;\n continue;\n }\n if (/\\s/u.test(char)) continue;\n const elision =\n (char === ',' && (previous === ',' || previous === '[')) ||\n (char === ']' && previous === ',');\n if (elision) json += 'null';\n json += char;\n previous = char;\n if (char === '[') depth += 1;\n if (char === ']') {\n depth -= 1;\n if (depth === 0) break;\n }\n }\n if (depth !== 0) return undefined;\n try {\n const value: unknown = JSON.parse(json);\n return Array.isArray(value) ? (value as readonly unknown[]) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** `'compat'` rather than `\"compat\"`: remedies are read as source code. */\nfunction literal(value: unknown): string {\n return typeof value === 'string' ? `'${value}'` : JSON.stringify(value);\n}\n\nfunction remedyFor(origin: Origin, row: ReadinessRow, value: unknown): string {\n const wanted = `\\`${row.key}: ${literal(V2_RUNTIME_DEFAULTS[row.key])}\\``;\n switch (origin) {\n case 'profile':\n return `Set ${wanted} or drop \\`defaults: 'v1'\\` once ${row.condition}.`;\n case 'explicit':\n return `Drop the explicit \\`${row.key}: ${literal(value)}\\` once ${row.condition}; 2.0 defaults to ${wanted}.`;\n case 'unmarked':\n // 1.8.1 has no `defaults` option and three of the four rows are not\n // inline options there, so the way out is the upgrade itself.\n return `Upgrading to 2.0 sets ${wanted}; check first that ${row.condition}.`;\n }\n}\n\nfunction gap(row: ReadinessRow, value: unknown, origin: Origin): DoctorFinding {\n return {\n code: 'LP0709',\n level: 'warning',\n title: row.title,\n detail: `${row.key} is ${literal(value)}: ${row.effect} ${ORIGIN_DETAIL[origin]}`,\n remedy: remedyFor(origin, row, value),\n };\n}\n\nfunction unknownGeneration(marker: unknown): DoctorFinding {\n return {\n code: 'LP0709',\n level: 'info',\n title: 'The inline configuration names defaults this doctor does not know',\n detail: `Its \\`defaults\\` marker is ${JSON.stringify(marker)}; this version reads 'v1' and 'v2', so no row was judged.`,\n remedy: 'Run the `pll doctor` of the package version that generated the page.',\n };\n}\n\n/**\n * One `LP0709` finding per runtime row not yet at its 2.0 value. An empty slot\n * has the value of the generation the script names, and the 1.x value when it\n * names none; a generation this version does not know is not judged at all.\n * @internal\n */\nexport function analyzeV2Readiness(probe: DoctorProbe): readonly DoctorFinding[] {\n const config = readInlineConfig(probe.previewResponse.body);\n if (config === undefined) return [UNREADABLE];\n const marker = config[DEFAULTS_SLOT] ?? undefined;\n if (marker !== undefined && marker !== 'v1' && marker !== 'v2') {\n return [unknownGeneration(marker)];\n }\n const generation = marker;\n const fallback = generation === 'v2' ? V2_RUNTIME_DEFAULTS : V1_RUNTIME_DEFAULTS;\n const findings = ROWS.flatMap((row) => {\n const slot = config[INLINE_CONFIG_KEYS.indexOf(row.key)] ?? undefined;\n const value = slot ?? fallback[row.key];\n if (value === V2_RUNTIME_DEFAULTS[row.key]) return [];\n const origin: Origin =\n generation === 'v1' ? 'profile' : slot === undefined ? 'unmarked' : 'explicit';\n return [gap(row, value, origin)];\n });\n return generation === undefined ? [...findings, UNMARKED] : findings;\n}\n","/**\n * The `pll doctor` audit: judge what a deployment serves from two fetches of\n * one URL, as a visitor and as the admin's iframe. Pure — the caller fetches.\n * Markup is read with regular expressions, which is enough to count\n * attributes and find the runtime marker; findings say what was observed.\n */\nimport { DIAGNOSTIC_CODES } from '../core/diagnostic-codes';\nimport { frameAncestorsAdmits, frameAncestorsOf } from './csp';\nimport { analyzeV2Readiness } from './readiness';\nimport type {\n DoctorContext,\n DoctorFinding,\n DoctorProbe,\n DoctorReport,\n DoctorResponse,\n} from './types';\n\n/** Survives minification; the banner comment does not. */\nconst RUNTIME_MARKER = '__LIVE_PREVIEW_CONFIG__';\nconst BINDING_ATTRIBUTE = /\\bdata-payload-field\\s*=/gu;\nconst OWNER_ATTRIBUTE = /\\bdata-payload-owner\\s*=/gu;\n/** Mirrors the scheduler default; the audit must not import the runtime. */\nconst DEFAULT_VISIBILITY_GATE_THRESHOLD = 50;\nconst LEVEL_ORDER = { error: 0, warning: 1, info: 2 } as const;\n// An end tag may carry junk before its `>`; browsers still close on it, so the\n// pattern must too, or a `</script foo>` would leave the script body counted.\nconst NON_MARKUP =\n /<script\\b[^>]*>[\\s\\S]*?<\\/script\\b[^>]*>|<style\\b[^>]*>[\\s\\S]*?<\\/style\\b[^>]*>|<!--[\\s\\S]*?-->/giu;\n\n/**\n * The runtime's own source spells `data-payload-field=` in a message, so\n * scripts must not be counted. This is a counter's view of the page, not a\n * sanitiser — nothing here is rendered — but it strips to a fixed point all\n * the same, so a block that only appears once its neighbour is gone is still\n * removed rather than counted.\n */\nfunction visibleMarkup(body: string): string {\n let markup = body;\n for (;;) {\n const stripped = markup.replace(NON_MARKUP, '');\n if (stripped === markup) return markup;\n markup = stripped;\n }\n}\n\nfunction count(haystack: string, pattern: RegExp): number {\n return haystack.match(pattern)?.length ?? 0;\n}\n\n/** Bindings before the first owner marker are certainly unowned; nesting is not modelled, so this under-reports. */\nfunction unownedBindingsBeforeFirstOwner(markup: string): number {\n const firstOwner = markup.search(/\\bdata-payload-owner\\s*=/u);\n return firstOwner === -1 ? 0 : count(markup.slice(0, firstOwner), BINDING_ATTRIBUTE);\n}\n\nfunction tryUrl(value: string | undefined): URL | undefined {\n if (value === undefined) return undefined;\n try {\n return new URL(value);\n } catch {\n return undefined;\n }\n}\n\n/** Why the preview response cannot be audited as a page; the public one may legitimately be gated. */\nfunction describeNonPage(preview: DoctorResponse): DoctorFinding | undefined {\n if (preview.status >= 300 && preview.status <= 399) {\n const location = preview.headers['location'];\n return {\n code: 'LP0708',\n level: 'error',\n title: `The preview request was redirected (${String(preview.status)})${location === undefined ? '' : ` to ${location}`}`,\n detail:\n 'The audit does not follow redirects: the page at the end of one is a different ' +\n 'resource, usually a login or a canonical host, and judging it would say nothing ' +\n 'about this URL.',\n remedy:\n 'Probe the final URL directly. A redirect to a login means the route needs ' +\n 'authentication that a plain probe cannot supply.',\n };\n }\n if (preview.status < 200 || preview.status > 299) {\n return {\n code: 'LP0708',\n level: 'error',\n title: `The preview request returned ${String(preview.status)}, not a page`,\n detail:\n 'Nothing below this can be judged: a status outside 2xx means the body is an ' +\n 'error page or empty.',\n remedy: 'Check the URL.',\n };\n }\n if (preview.body.trim() === '') {\n return {\n code: 'LP0708',\n level: 'error',\n title: `The preview request returned ${String(preview.status)} with an empty body`,\n detail:\n 'A 2xx status with nothing in it — 204 and 205 are defined that way — is not a page, ' +\n 'so its missing runtime and missing bindings say nothing.',\n remedy: 'Point the audit at a route that renders a document.',\n };\n }\n const contentType = preview.headers['content-type'];\n if (contentType !== undefined && !contentType.toLowerCase().includes('html')) {\n return {\n code: 'LP0708',\n level: 'error',\n title: `The preview request returned ${contentType}, not HTML`,\n detail:\n 'The audit reads a rendered page. A non-HTML response has no runtime and no ' +\n 'bindings by definition, so reporting their absence would say nothing.',\n remedy: 'Point the audit at a page route rather than an API or an asset.',\n };\n }\n return undefined;\n}\n\nfunction framingFindings(\n preview: DoctorResponse,\n runtimeInPreview: boolean,\n context: DoctorContext,\n): DoctorFinding[] {\n const findings: DoctorFinding[] = [];\n const page = tryUrl(context.url);\n const admin = tryUrl(context.adminOrigin);\n const sameOrigin = admin !== undefined && page?.origin === admin.origin;\n const frameAncestors = frameAncestorsOf(preview.headers['content-security-policy']);\n if (runtimeInPreview && frameAncestors === undefined) {\n findings.push({\n code: 'LP0702',\n level: 'warning',\n title: 'Preview response declares no frame-ancestors',\n detail:\n 'The runtime is injected but the response carries no frame-ancestors directive, ' +\n 'so nothing states which origins may embed this page.',\n remedy:\n 'Let the adapter manage CSP, or set frame-ancestors yourself to the admin origin. ' +\n 'Without it you are relying on the absence of a policy.',\n });\n } else if (frameAncestors !== undefined && context.adminOrigin !== undefined) {\n const admitted =\n admin !== undefined &&\n page !== undefined &&\n frameAncestorsAdmits(frameAncestors, admin, page);\n if (!admitted) {\n findings.push({\n code: 'LP0702',\n level: 'error',\n title: 'frame-ancestors does not admit the admin origin',\n detail:\n admin === undefined\n ? `\"${context.adminOrigin}\" is not an absolute URL, so no source expression can admit it.`\n : `Served: \"${frameAncestors}\". Expected it to allow ${admin.origin}.`,\n remedy:\n 'Add the admin origin to allowedOrigins so the adapter merges it into ' +\n 'frame-ancestors, or add it to your own policy.',\n });\n }\n }\n const frameOptions = preview.headers['x-frame-options'];\n const blocks =\n frameOptions !== undefined &&\n (/deny/iu.test(frameOptions) || (/sameorigin/iu.test(frameOptions) && !sameOrigin));\n if (blocks) {\n // `SAMEORIGIN` refuses a frame from *another* origin, so it is a verdict\n // about this preview only once the admin origin is known. Without --admin\n // the audit says what it saw and names the condition instead of asserting a\n // block it cannot have established: where the admin and the site share an\n // origin, the preview runs. `DENY` needs no origin to be a verdict.\n const judged = admin !== undefined || /deny/iu.test(frameOptions);\n findings.push(\n judged\n ? {\n code: 'LP0703',\n level: 'error',\n title: `X-Frame-Options: ${frameOptions} blocks the preview iframe`,\n detail:\n 'This header is older than CSP and browsers honour it independently. No ' +\n 'frame-ancestors directive can override it.',\n remedy:\n 'Remove X-Frame-Options for preview responses. It is usually set by a proxy ' +\n 'or a security middleware rather than by the app.',\n }\n : {\n code: 'LP0703',\n level: 'warning',\n title: `X-Frame-Options: ${frameOptions} blocks every frame from another origin`,\n detail:\n 'This header is older than CSP and browsers honour it independently, and no ' +\n 'frame-ancestors directive can override it. SAMEORIGIN refuses a frame from ' +\n 'another origin only, and no admin origin was given: whether it blocks this ' +\n 'preview depends on where the admin is served, which this probe cannot see.',\n remedy:\n 'Re-run with --admin <origin> to have it judged. If the admin is on another ' +\n 'origin, remove X-Frame-Options for preview responses; a proxy or a security ' +\n 'middleware usually sets it.',\n },\n );\n }\n return findings;\n}\n\nfunction bindingFindings(\n previewMarkup: string,\n publicMarkup: string,\n runtime: boolean,\n): DoctorFinding[] {\n const findings: DoctorFinding[] = [];\n const previewBindings = count(previewMarkup, BINDING_ATTRIBUTE);\n const publicBindings = count(publicMarkup, BINDING_ATTRIBUTE);\n const owners = count(previewMarkup, OWNER_ATTRIBUTE);\n if (publicBindings > 0) {\n findings.push({\n code: 'LP0704',\n level: 'warning',\n title: `${String(publicBindings)} binding attribute(s) served to anonymous visitors`,\n detail:\n 'A request with no preview intent received data-payload-field attributes. These ' +\n 'disclose the CMS field taxonomy, and any CSS keyed on them changes public layout ' +\n 'the moment emission is gated.',\n remedy:\n 'Gate binding emission on an authorized preview context — see ' +\n 'createPreviewBindings(), whose suppressed form emits nothing at all.',\n });\n }\n if (previewBindings > DEFAULT_VISIBILITY_GATE_THRESHOLD) {\n findings.push({\n code: 'LP0705',\n level: 'warning',\n title: `${String(previewBindings)} bindings exceed the default visibility gate`,\n detail:\n `Above visibilityGateThreshold (${String(DEFAULT_VISIBILITY_GATE_THRESHOLD)}) the ` +\n 'scheduler stops writing offscreen elements and buffers them until they scroll into ' +\n 'view. On a page nobody scrolls, that is never.',\n remedy:\n 'Raise visibilityGateThreshold if the whole page must stay live, or confirm that ' +\n 'deferring below the fold is acceptable here.',\n });\n }\n if (runtime && previewBindings === 0) {\n findings.push({\n code: 'LP0707',\n level: 'error',\n title: 'Preview response carries no bindings',\n detail:\n 'The runtime is present but there is not one data-payload-field element for it to ' +\n 'write into, so no edit can ever be visible on this page.',\n remedy:\n 'Add binding attributes to the markup. pll-codegen --inventory lists every field ' +\n 'the schema makes addressable.',\n });\n }\n const unowned = unownedBindingsBeforeFirstOwner(previewMarkup);\n if (owners > 0 && unowned > 0) {\n findings.push({\n code: 'LP0706',\n level: 'warning',\n title: `${String(unowned)} binding(s) outside every owner marker`,\n detail:\n `The page declares ${String(owners)} owner marker(s), so owner scoping is in use. ` +\n 'Bindings that belong to no owner receive nothing once scopeBindingsByOwner is on — ' +\n 'they fail closed.',\n remedy:\n 'Give every binding an owning ancestor, or leave scopeBindingsByOwner off until they ' +\n 'all have one. Counted before the first owner marker only, so the real number may be ' +\n 'higher.',\n });\n }\n return findings;\n}\n\n/** Audit a probed deployment. */\nexport function analyzeProbe(\n probe: DoctorProbe,\n context: DoctorContext & { readonly v2?: boolean },\n): DoctorReport {\n const { publicResponse: pub, previewResponse: preview } = probe;\n const notAPage = describeNonPage(preview);\n if (notAPage !== undefined) {\n return { url: context.url, findings: [notAPage], errors: 1, warnings: 0 };\n }\n const runtimeInPreview = preview.body.includes(RUNTIME_MARKER);\n const findings: DoctorFinding[] = [];\n if (!runtimeInPreview) {\n findings.push({\n code: 'LP0701',\n level: 'warning',\n title: 'No inline runtime in the preview response',\n detail:\n `A request carrying Sec-Fetch-Dest: iframe returned ${String(preview.status)} without ` +\n 'the inline runtime. Two readings, and this audit cannot tell them apart from the ' +\n 'response alone: an adapter that did not recognise the request as a preview, or a ' +\n 'consumer that starts LivePreviewClient itself and never wanted the inline build.',\n remedy:\n 'If you use an adapter, check its inject mode and whether a proxy strips ' +\n 'Sec-Fetch-Dest. If you start the client yourself, this line is expected.',\n });\n }\n findings.push(...framingFindings(preview, runtimeInPreview, context));\n findings.push(\n ...bindingFindings(visibleMarkup(preview.body), visibleMarkup(pub.body), runtimeInPreview),\n );\n if (pub.body.includes(RUNTIME_MARKER)) {\n findings.push({\n code: DIAGNOSTIC_CODES.RuntimeOnPublicPage,\n level: 'info',\n title: 'Runtime is also served to anonymous visitors',\n detail:\n 'A request with no preview intent received the inline runtime. That is correct for ' +\n \"inject: 'always' and unintended for 'preview-only'.\",\n remedy: '',\n });\n }\n if (context.v2 === true) findings.push(...analyzeV2Readiness(probe));\n findings.sort((a, b) => LEVEL_ORDER[a.level] - LEVEL_ORDER[b.level]);\n return {\n url: context.url,\n findings,\n errors: findings.filter((finding) => finding.level === 'error').length,\n warnings: findings.filter((finding) => finding.level === 'warning').length,\n };\n}\n","/** Terminal rendering of a doctor report. */\nimport type { DoctorReport } from './types';\n\nconst LEVEL_LABEL = { error: 'ERROR', warning: 'WARN ', info: 'INFO ' } as const;\n\n/** @internal */\nexport function formatReport(report: DoctorReport): string {\n const lines: string[] = [`pll doctor — ${report.url}`, ''];\n if (report.findings.length === 0) {\n lines.push('No findings. The preview response carries the runtime, a frame-ancestors');\n lines.push('policy, and bindings; the public response carries none of them.', '');\n return lines.join('\\n');\n }\n for (const finding of report.findings) {\n lines.push(`${LEVEL_LABEL[finding.level]} ${finding.code} ${finding.title}`);\n lines.push(` ${finding.detail}`);\n if (finding.remedy !== '') lines.push(` → ${finding.remedy}`);\n lines.push('');\n }\n lines.push(`${String(report.errors)} error(s), ${String(report.warnings)} warning(s).`, '');\n return lines.join('\\n');\n}\n","/**\n * Fetching for `pll doctor`: the two probes, and the default fetch that talks\n * to a real server. Not in the barrel so it stays in the coverage report.\n */\nimport { analyzeProbe } from './analyze';\nimport type { DoctorReport, DoctorResponse } from './types';\n\n/** Injectable so tests can drive the audit without a server. */\nexport type DoctorFetch = (\n url: string,\n init: { readonly headers: Readonly<Record<string, string>> },\n) => Promise<DoctorResponse>;\n\nexport interface RunDoctorOptions {\n readonly url: string;\n /** Admin origin the preview is meant to be embedded from, when known. */\n readonly adminOrigin?: string | undefined;\n /** Defaults to a fetch built on `globalThis.fetch`. */\n readonly fetchImpl?: DoctorFetch | undefined;\n /** Also check the served page against the 2.0 readiness table. */\n readonly v2?: boolean;\n}\n\nexport const DEFAULT_TIMEOUT_MS = 15_000;\nexport const MAX_BODY_BYTES = 10 * 1024 * 1024;\n\nexport interface DefaultFetchOptions {\n readonly fetchFn?: typeof fetch;\n readonly timeoutMs?: number;\n readonly maxBodyBytes?: number;\n}\n\n/** Header names lowercased; every check downstream reads a lowercase key. @internal */\nexport function lowercaseHeaders(headers: Headers): Record<string, string> {\n const out: Record<string, string> = {};\n headers.forEach((value, key) => {\n out[key.toLowerCase()] = value;\n });\n return out;\n}\n\n/** The error's message plus its cause's, which is where undici puts `self-signed certificate` and friends. */\nexport function describeFailure(error: unknown): string {\n if (!(error instanceof Error)) return String(error);\n const cause = error.cause;\n if (cause instanceof Error && cause.message !== '' && !error.message.includes(cause.message)) {\n return `${error.message} (${cause.message})`;\n }\n return error.message;\n}\n\nasync function readBody(response: Response, limit: number): Promise<string> {\n const reader = response.body?.getReader();\n if (reader === undefined) return response.text();\n const decoder = new TextDecoder();\n let received = 0;\n let text = '';\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n received += value.byteLength;\n if (received > limit) {\n await reader.cancel();\n throw new Error(\n `the response body exceeds ${String(limit)} bytes, which is not a page the audit can judge`,\n );\n }\n text += decoder.decode(value, { stream: true });\n }\n return text + decoder.decode();\n}\n\n/** Redirects are reported, not followed; a hanging origin is abandoned; a runaway body is refused. */\nexport function createDefaultFetch(options: DefaultFetchOptions = {}): DoctorFetch {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const limit = options.maxBodyBytes ?? MAX_BODY_BYTES;\n return async (url, init) => {\n const fetchFn = options.fetchFn ?? globalThis.fetch;\n let response: Response;\n try {\n response = await fetchFn(url, {\n headers: init.headers,\n redirect: 'manual',\n signal: AbortSignal.timeout(timeoutMs),\n });\n } catch (error) {\n if (error instanceof Error && error.name === 'TimeoutError') {\n throw new Error(`no response within ${String(timeoutMs / 1000)} s`, { cause: error });\n }\n throw error;\n }\n return {\n status: response.status,\n headers: lowercaseHeaders(response.headers),\n body: await readBody(response, limit),\n };\n };\n}\n\n/** The referer the admin's iframe would send: the origin (or the given admin path) with one slash. */\nexport function previewReferer(adminOrigin: string): string {\n try {\n return new URL(adminOrigin).href;\n } catch {\n return `${adminOrigin.replace(/\\/+$/u, '')}/`;\n }\n}\n\n/** Fetch the URL twice — as a visitor and as the admin's iframe — and audit the difference. */\nexport async function runDoctor(options: RunDoctorOptions): Promise<DoctorReport> {\n const fetchImpl = options.fetchImpl ?? createDefaultFetch();\n // No referer on the visitor probe: an admin referer is itself a preview signal.\n const publicResponse = await fetchImpl(options.url, {\n headers: {\n Accept: 'text/html',\n 'Sec-Fetch-Dest': 'document',\n 'Sec-Fetch-Mode': 'navigate',\n },\n });\n const previewResponse = await fetchImpl(options.url, {\n headers: {\n Accept: 'text/html',\n 'Sec-Fetch-Dest': 'iframe',\n 'Sec-Fetch-Mode': 'navigate',\n ...(options.adminOrigin === undefined\n ? {}\n : { Referer: previewReferer(options.adminOrigin) }),\n },\n });\n return analyzeProbe(\n { publicResponse, previewResponse },\n {\n url: options.url,\n adminOrigin: options.adminOrigin,\n ...(options.v2 === true ? { v2: true } : {}),\n },\n );\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"doctor.js","names":["DIAGNOSTIC_CODES","Object","freeze","NoTrustedOrigin","ReferrerOnlyTrust","PluginIncompatible","ProfileFeatureOmitted","OrphanField","UnattributableUpdate","VisibilityGateDeferred","UnsafeAttributeWrite","TextTargetHasChildren","MissingArrayTemplate","StructuralItemUnkeyed","StructuralDuplicateKey","StructuralUnstableKeys","UnsupportedStrategy","UnknownValueFormat","SanitizerDroppedAttribute","UnrenderedBlockKept","UnfaithfulPatch","ServerFormatReplaced","UnrenderedBlockLost","MessageRejected","TokenRejected","ProtocolShapeUnknown","HandlerThrew","TransformThrew","RendererThrew","StartupFailed","ReadyFailed","HydrationWaitTimedOut","AuditRuntimeMissing","AuditNoFrameAncestors","AuditFrameOptionsBlocks","AuditBindingsExposed","AuditGateThresholdExceeded","AuditUnownedBindings","AuditNoBindings","AuditNotAPage","FragmentRequestFailed","FragmentResponseInvalid","FragmentUnauthorized","FragmentSuperseded","RouteRefreshLoop","FragmentStrategyUnavailable","UnboundChangeRefresh","V2ReadinessGap","RuntimeOnPublicPage","DEFAULT_PORT","http","https","ws","wss","SCHEME_SOURCE","HOST_SOURCE","schemeOf","url","protocol","slice","toLowerCase","portOf","port","schemeMatches","expression","actual","isSelf","admin","page","hostname","adminScheme","pageScheme","sourceMatches","source","scheme","exec","host","expressionScheme","expressionHost","expressionPort","pattern","wanted","replace","startsWith","endsWith","length","hostMatches","INLINE_CONFIG_KEYS","V2_RUNTIME_DEFAULTS","disableReferrerDetection","eventSourcePolicy","skipUnchanged","sanitizerPolicy","V1_RUNTIME_DEFAULTS","DEFAULTS_SLOT","indexOf","ROWS","key","title","effect","condition","ORIGIN_DETAIL","profile","explicit","unmarked","UNREADABLE","code","level","detail","remedy","UNMARKED","literal","value","JSON","stringify","remedyFor","origin","row","gap","unknownGeneration","marker","analyzeV2Readiness","probe","config","body","start","from","CONFIG_MARKER","depth","inString","previous","json","index","char","test","parse","Array","isArray","readInlineConfig","previewResponse","generation","fallback","findings","flatMap","slot","RUNTIME_MARKER","BINDING_ATTRIBUTE","OWNER_ATTRIBUTE","LEVEL_ORDER","error","warning","info","NON_MARKUP","visibleMarkup","markup","stripped","count","haystack","match","tryUrl","URL","analyzeProbe","context","publicResponse","pub","preview","notAPage","status","location","headers","String","trim","contentType","includes","describeNonPage","errors","warnings","runtimeInPreview","push","credentials","adminOrigin","sameOrigin","frameAncestors","csp","directive","split","trimmed","frameAncestorsOf","admitted","sources","filter","map","frameAncestorsAdmits","frameOptions","judged","framingFindings","previewMarkup","publicMarkup","runtime","previewBindings","publicBindings","owners","unowned","firstOwner","search","unownedBindingsBeforeFirstOwner","bindingFindings","v2","sort","a","b","finding","LEVEL_LABEL","formatReport","report","lines","join","DEFAULT_INTENT_PARAMS","splitUrl","hashAt","hash","beforeHash","queryAt","base","parts","decodePart","part","at","decode","text","decodeURIComponent","TOKEN_PARAM","redactToken","shown","some","lowercaseHeaders","out","forEach","async","readBody","response","limit","reader","getReader","decoder","TextDecoder","received","done","read","byteLength","cancel","Error","stream","previewReferer","href","runDoctor","options","fetchImpl","timeoutMs","maxBodyBytes","init","fetchFn","globalThis","fetch","redirect","signal","AbortSignal","timeout","name","cause","createDefaultFetch","params","previewQueryParams","dropped","Set","kept","has","visitorUrl","Accept","probeHeaders","Referer","sent","own","taken","keys","fromEntries","entries","callerHeaders","previewHeaders","isIntentValue","query","joiner","intent","encodeURIComponent","previewUrl"],"sources":["../src/core/diagnostic-codes.ts","../src/doctor/csp.ts","../src/types/inline-config.ts","../src/types/defaults-profile.ts","../src/doctor/readiness.ts","../src/doctor/analyze.ts","../src/doctor/format.ts","../src/doctor/probe.ts"],"sourcesContent":["/**\n * Every diagnostic code the runtime and tools emit, so a consumer can branch\n * on `e.code` instead of matching message text. A code is never reused for a\n * different meaning; a retired one stays reserved.\n *\n * Browser-side reporting sites write the literal (`code: 'LP0603'`) rather than\n * reading it from this record: the frozen table would then ship in the inline\n * runtime for the sake of six characters — it did, 1 287 B in every page, for\n * the one read Z6 left in the strategy runner, and `diagnostic-codes.test.ts`\n * now holds the artifact free of it. Server-side tools may import it.\n */\n\n/**\n * Every diagnostic code the runtime can report, by name.\n *\n * ```ts\n * client.events.on('error', (e) => {\n * if (e.code === DIAGNOSTIC_CODES.TransformThrew) reportToSentry(e.error);\n * });\n * ```\n */\nexport const DIAGNOSTIC_CODES = Object.freeze({\n /** No trusted origin configured in production; nothing will be accepted. */\n NoTrustedOrigin: 'LP0101',\n /** Origin trust rests on `document.referrer`, so any framing site is trusted. */\n ReferrerOnlyTrust: 'LP0102',\n /** A plugin declares a runtime range this runtime does not satisfy; it was refused. */\n PluginIncompatible: 'LP0103',\n /** The page carries the lean runtime and needs a feature that profile leaves out. */\n ProfileFeatureOmitted: 'LP0104',\n\n /** An update named a field with no binding anchor on the page. */\n OrphanField: 'LP0201',\n /** Owner scoping is on and the update names no document it could belong to. */\n UnattributableUpdate: 'LP0202',\n\n /** The visibility gate held offscreen writes back until they scroll into view. */\n VisibilityGateDeferred: 'LP0301',\n\n /** A value was refused because the attribute or the value itself is unsafe. */\n UnsafeAttributeWrite: 'LP0401',\n /** A text element has structured children, so replacing its text was skipped. */\n TextTargetHasChildren: 'LP0402',\n /** A structural container has no array template, so the update was skipped. */\n MissingArrayTemplate: 'LP0403',\n /** A structural item has no `id`; it pairs positionally, so an insert re-renders every row after it. */\n StructuralItemUnkeyed: 'LP0404',\n /** Two structural items share a key; later ones pair positionally. */\n StructuralDuplicateKey: 'LP0405',\n /** Every structural key changed while the length did not; the source generates keys per message. */\n StructuralUnstableKeys: 'LP0406',\n /** A binding asks for a delivery strategy this release does not have; it is left unchanged. */\n UnsupportedStrategy: 'LP0407',\n /** `data-payload-format` names a format the vocabulary does not contain; the default formatting was used instead. */\n UnknownValueFormat: 'LP0408',\n /** The strict sanitizer removed an attribute the 1.x `'compat'` policy kept. */\n SanitizerDroppedAttribute: 'LP0409',\n /** A Lexical block has no renderer; the markup the server rendered for it is kept instead. */\n UnrenderedBlockKept: 'LP0410',\n /** A patch cannot reach what the server would have drawn; `onUnfaithfulPatch` decided what happens next. */\n UnfaithfulPatch: 'LP0411',\n /** The first write to a date, number or checkbox binding replaced a different reading of the same value. */\n ServerFormatReplaced: 'LP0412',\n /** A Lexical block has no renderer, and the write could not keep the markup the server rendered for it; it is gone. */\n UnrenderedBlockLost: 'LP0413',\n\n /** A message was rejected before it reached the update pipeline. */\n MessageRejected: 'LP0501',\n /** A preview token was rejected. */\n TokenRejected: 'LP0502',\n /** A trusted origin sent a message this protocol version does not recognise. */\n ProtocolShapeUnknown: 'LP0503',\n\n /** A consumer event handler threw. */\n HandlerThrew: 'LP0601',\n /** A consumer transform threw; the original value was kept. */\n TransformThrew: 'LP0602',\n /** A renderer threw while writing a value. */\n RendererThrew: 'LP0603',\n // LP0604 is reserved and unassigned; codes are never reused or renumbered.\n /** Runtime startup failed. */\n StartupFailed: 'LP0605',\n /** Sending the ready handshake failed. */\n ReadyFailed: 'LP0606',\n /** The page declared hydration, but no React commit or Vue mount came within the cap; the runtime started without waiting. */\n HydrationWaitTimedOut: 'LP0607',\n\n /** The audit found no runtime in a response that carried preview intent. */\n AuditRuntimeMissing: 'LP0701',\n /** The preview response declares no `frame-ancestors`. */\n AuditNoFrameAncestors: 'LP0702',\n /** `X-Frame-Options` forbids framing, which no CSP can undo; `SAMEORIGIN` forbids it from another origin only. */\n AuditFrameOptionsBlocks: 'LP0703',\n /** Binding attributes are served to anonymous visitors. */\n AuditBindingsExposed: 'LP0704',\n /** More bindings than the default visibility gate will write eagerly. */\n AuditGateThresholdExceeded: 'LP0705',\n /** Owner markers exist, but some bindings are outside all of them. */\n AuditUnownedBindings: 'LP0706',\n /** The preview response carries no bindings at all. */\n AuditNoBindings: 'LP0707',\n /** The URL did not return an HTML page, so nothing else can be judged. */\n AuditNotAPage: 'LP0708',\n /** A fragment request failed (network, timeout, server error); the boundary was patched instead. */\n FragmentRequestFailed: 'LP0801',\n /** The fragment response had the wrong content type or shape; the boundary was patched instead. */\n FragmentResponseInvalid: 'LP0802',\n /** The fragment endpoint refused the preview (not authorized); the boundary was patched instead. */\n FragmentUnauthorized: 'LP0803',\n /** A fragment response arrived for a revision that was already superseded and was discarded. */\n FragmentSuperseded: 'LP0804',\n /** A route refresh was refused: the loop guard, or the strategy's minimum interval. */\n RouteRefreshLoop: 'LP0805',\n /** A boundary asks for the fragment strategy but no fragment handler is configured; it is patched. */\n FragmentStrategyUnavailable: 'LP0806',\n /** A revision changed a field with no binding; `onUnfaithfulPatch` refreshed the route. */\n UnboundChangeRefresh: 'LP0807',\n /** A readiness row is not yet at its 2.0 value; `pll doctor --v2` reports it. */\n V2ReadinessGap: 'LP0709',\n /** The preview runtime is served to anonymous visitors, not only inside the admin frame. */\n RuntimeOnPublicPage: 'LP0710',\n} as const);\n\n/** A diagnostic code, as it appears in log output and on the `error` event. */\nexport type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[keyof typeof DIAGNOSTIC_CODES];\n","/**\n * `frame-ancestors` matching by the CSP3 source-expression rules. The audit\n * judges origins, so path parts of a host-source are ignored.\n */\n\nconst DEFAULT_PORT: Readonly<Record<string, string>> = {\n http: '80',\n https: '443',\n ws: '80',\n wss: '443',\n};\nconst SCHEME_SOURCE = /^([a-z][a-z0-9+.-]*):$/u;\nconst HOST_SOURCE =\n /^(?:([a-z][a-z0-9+.-]*):\\/\\/)?(\\*|(?:\\*\\.)?[a-z0-9-]+(?:\\.[a-z0-9-]+)*\\.?)(?::(\\*|\\d+))?(?:\\/.*)?$/u;\n\n/** The `frame-ancestors` directive of a policy, verbatim, or `undefined` when it has none. */\nexport function frameAncestorsOf(csp: string | undefined): string | undefined {\n if (csp === undefined) return undefined;\n for (const directive of csp.split(';')) {\n const trimmed = directive.trim();\n if (/^frame-ancestors(?:\\s|$)/iu.test(trimmed)) return trimmed;\n }\n return undefined;\n}\n\nfunction schemeOf(url: URL): string {\n return url.protocol.slice(0, -1).toLowerCase();\n}\n\nfunction portOf(url: URL): string {\n return url.port === '' ? (DEFAULT_PORT[schemeOf(url)] ?? '') : url.port;\n}\n\nfunction schemeMatches(expression: string, actual: string): boolean {\n return (\n expression === actual ||\n (expression === 'http' && actual === 'https') ||\n (expression === 'ws' && actual === 'wss')\n );\n}\n\nfunction hostMatches(pattern: string, host: string): boolean {\n if (pattern === '*') return true;\n const wanted = pattern.replace(/\\.$/u, '');\n const actual = host.replace(/\\.$/u, '');\n if (wanted.startsWith('*.')) {\n return actual.endsWith(wanted.slice(1)) && actual.length > wanted.length - 1;\n }\n return wanted === actual;\n}\n\nfunction isSelf(admin: URL, page: URL): boolean {\n if (admin.hostname !== page.hostname) return false;\n const adminScheme = schemeOf(admin);\n const pageScheme = schemeOf(page);\n if (adminScheme !== pageScheme && !(pageScheme === 'http' && adminScheme === 'https')) {\n return false;\n }\n return portOf(admin) === portOf(page) || (admin.port === '' && page.port === '');\n}\n\nfunction sourceMatches(source: string, admin: URL, page: URL): boolean {\n const scheme = SCHEME_SOURCE.exec(source);\n if (scheme?.[1] !== undefined) return schemeMatches(scheme[1], schemeOf(admin));\n const host = HOST_SOURCE.exec(source);\n if (host === null) return false;\n const expressionScheme = host[1];\n const expressionHost = host[2] ?? '';\n const expressionPort = host[3];\n const requiredScheme = expressionScheme ?? schemeOf(page);\n if (!schemeMatches(requiredScheme, schemeOf(admin))) return false;\n if (!hostMatches(expressionHost, admin.hostname)) return false;\n if (expressionPort === undefined) return admin.port === '';\n return expressionPort === '*' || expressionPort === portOf(admin);\n}\n\n/** Whether the directive admits `admin` as an ancestor of `page`. */\nexport function frameAncestorsAdmits(directive: string, admin: URL, page: URL): boolean {\n const sources = directive\n .split(/\\s+/u)\n .slice(1)\n .filter((source) => source.length > 0)\n .map((source) => source.toLowerCase());\n if (sources.length === 0 || (sources.length === 1 && sources[0] === \"'none'\")) return false;\n for (const source of sources) {\n if (source === \"'self'\") {\n if (isSelf(admin, page)) return true;\n } else if (source === '*') {\n return true;\n } else if (!source.startsWith(\"'\") && sourceMatches(source, admin, page)) {\n return true;\n }\n }\n return false;\n}\n","/**\n * The inline script's configuration and its positional wire order: the\n * generator writes the tuple, `src/core/runtime.ts` destructures it, and\n * `INLINE_CONFIG_KEYS` is where they agree. Append only, never reorder.\n */\n\nimport type { DefaultsProfile } from '@/types/defaults-profile';\n\nexport interface InlineScriptConfig {\n /** Trusted admin origins, merged with the detected ones. */\n readonly allowedOrigins?: readonly string[];\n /** Payload origin; every update is then re-fetched through the REST API with credentials. Requires `mergeDepth`. */\n readonly serverURL?: string;\n /** REST route prefix used with `serverURL`. Default `/api`. */\n readonly apiRoute?: string;\n /** Population depth used with `serverURL`. Required with `serverURL` unless `defaults: 'v1'`. */\n readonly mergeDepth?: number;\n /** Verbose console logging. Default `false`. */\n readonly debug?: boolean;\n /** Debounce window for incoming updates in ms. Default `50`. */\n readonly debounceMs?: number;\n /** Mount the screen-reader live region. Default `true`. */\n readonly enableA11y?: boolean;\n /** Heartbeat timeout in ms; `0` disables it (default), because the admin posts only on edits. */\n readonly heartbeatMs?: number;\n /** Apply every update regardless of viewport position. Default `false`. */\n readonly disableVisibilityGate?: boolean;\n /** Cache size above which off-screen updates wait for intersection. Default `50`. */\n readonly visibilityGateThreshold?: number;\n /** `rootMargin` of the IntersectionObserver. Default `'200px'`. */\n readonly intersectionRootMargin?: string;\n /** Ignore `document.referrer` as an origin source. Default `true`; `defaults: 'v1'` sets `false`. */\n readonly disableReferrerDetection?: boolean;\n /** Disable the dev-mode localhost matcher (any port on `localhost`/`127.0.0.1`). Default `false`. */\n readonly disableLocalhostMatching?: boolean;\n /** Update only bindings under a matching `data-payload-owner`. Default `false`. */\n readonly scopeBindingsByOwner?: boolean;\n /** Skip bindings whose value did not change. Default `true`; `defaults: 'v1'` sets `false`. */\n readonly skipUnchanged?: boolean;\n /** Scroll the preview to the field being edited. Default `false`. */\n readonly revealEditedField?: boolean;\n /** Which windows may post updates. Default `'parent-or-opener'`; `defaults: 'v1'` sets `'any'`. */\n readonly eventSourcePolicy?: 'any' | 'parent-or-opener';\n /** Sanitizer policy for rich text and HTML writes. Default `'strict'`; `defaults: 'v1'` sets `'compat'`. */\n readonly sanitizerPolicy?: 'compat' | 'strict';\n /** Same-origin path of a fragment endpoint; the script then carries the fragment client ahead of the runtime (ADR 0011). */\n readonly fragmentEndpoint?: string;\n /**\n * Carry the route strategy, so a binding in `<head>` or one marked\n * `data-payload-strategy=\"route\"` refreshes the route. `fragmentEndpoint`\n * implies it — the fragment prelude already contains it. Default `false`.\n */\n readonly routeStrategy?: boolean;\n /**\n * The 2.0 name for `onUnfaithfulPatch`, kept until 3.0. `'route'` means\n * `'escalate'`, `'ignore'` means `'ignore'`.\n *\n * @deprecated Renamed to `onUnfaithfulPatch`.\n */\n readonly onUnboundChange?: 'ignore' | 'route';\n /**\n * What to do when the runtime knows a patch cannot reach what the server\n * would have drawn. `'escalate'` (the default) hands the region to the\n * fragment strategy when a boundary covers it and to the route otherwise, so\n * it needs `fragmentEndpoint` or `routeStrategy` to do anything. `'warn'`\n * reports LP0411 and keeps the patch; `'ignore'` keeps it silently.\n */\n readonly onUnfaithfulPatch?: 'ignore' | 'warn' | 'escalate';\n /**\n * Find bindings by value on the connection's first message (ADR 0014): a\n * scalar whose value is the whole content of exactly one element is bound\n * to it as if `data-payload-field` stood there. Default `'off'`.\n */\n readonly autoBind?: 'off' | 'unique';\n /**\n * The framework that hydrates this page (ADR 0015). Under `'react'` the\n * runtime holds its first write until React has committed the tree that\n * holds the bindings — otherwise React finds markup it did not render,\n * throws `Hydration failed` and regenerates the tree, and the write is gone.\n * Under `'vue'` it holds the write until Vue has mounted the app around them\n * — otherwise Vue's hydration repairs the write back to the server's value,\n * quietly. The Next.js and Nuxt adapters set it on every script they emit; a\n * page built by hand with `generateInlineScript()` may. Omitted: the runtime\n * starts on `DOMContentLoaded`, as on a static page.\n */\n readonly hydration?: 'react' | 'vue';\n /**\n * Which defaults the omitted options fall back to, resolved by the generator\n * rather than the runtime, whose own fallbacks are the 2.0 rows: `'v1'`\n * writes its four runtime rows into their slots (an explicit option still\n * wins) and relaxes the `mergeDepth` check. The resolved value always\n * travels, in the last slot, so a reader of the served page knows what an\n * empty slot means instead of guessing it — `pll doctor --v2` reads it. The\n * runtime does not: every row the profile decides is already in its slot.\n */\n readonly defaults?: DefaultsProfile;\n /**\n * A runtime artifact to embed instead of the full one — today only\n * `LEAN_RUNTIME` from `payload-live-preview/lean`, which leaves out the\n * strategies, the keyed morph, the structural arrays, the item templates, the\n * announcer and auto-binding (about 7 KB gzip less on the page) and reports\n * LP0104 when a page needs one of them.\n *\n * It is an imported value rather than a `profile: 'lean'` string on purpose:\n * a second artifact behind a string option would sit in every build that can\n * reach the generator; measured, it grew each adapter entry by the artifact's\n * whole size. This way the bytes follow the import.\n *\n * Not serialized: it decides which bytes are emitted, not how they behave.\n */\n readonly runtime?: RuntimeArtifact;\n}\n\n/**\n * A runtime build this package produces. `source` is the IIFE the page runs;\n * the two digests describe the same bytes as a servable asset, so asset\n * delivery can hash-name and SRI-verify whichever artifact was chosen without\n * hashing anything at request time.\n */\nexport interface RuntimeArtifact {\n readonly profile: 'lean';\n readonly source: string;\n /** Short content hash, for the asset's file name. */\n readonly contentHash: string;\n /** `sha384-…`, for the bootstrap's `integrity` attribute. */\n readonly integrity: string;\n}\n\n/** Keys that travel in the wire tuple, in slot order. */\nexport const INLINE_CONFIG_KEYS = [\n 'allowedOrigins',\n 'serverURL',\n 'apiRoute',\n 'mergeDepth',\n 'debug',\n 'debounceMs',\n 'enableA11y',\n 'heartbeatMs',\n 'disableVisibilityGate',\n 'visibilityGateThreshold',\n 'intersectionRootMargin',\n 'disableReferrerDetection',\n 'disableLocalhostMatching',\n 'scopeBindingsByOwner',\n 'skipUnchanged',\n 'eventSourcePolicy',\n 'sanitizerPolicy',\n 'fragmentEndpoint',\n 'revealEditedField',\n 'routeStrategy',\n 'onUnboundChange',\n 'onUnfaithfulPatch',\n 'autoBind',\n 'hydration',\n 'defaults',\n] as const satisfies readonly Exclude<keyof InlineScriptConfig, 'runtime'>[];\n","/**\n * The two defaults profiles, row by row. 2.0 ships `v2`; `defaults: 'v1'` opts\n * back into the 1.x table for a staged migration. Each row belongs to exactly\n * one side — the adapter's request decision or the browser runtime — and a\n * test asserts the profile assigns every readiness row that has an option, so\n * a row added later cannot be forgotten. See ADR 0007.\n *\n * It lives in the leaf `types` domain, like `merge-depth.ts`, because the\n * inline generator resolves the profile too, and the generator is server-side\n * code that may not reach the browser runtime in `core`.\n */\n\n/** `'v2'` is the 2.0 default; `'v1'` is the 1.x table. */\nexport type DefaultsProfile = 'v1' | 'v2';\n\n/** How the runtime decides which windows may post updates. */\nexport type EventSourcePolicy = 'any' | 'parent-or-opener';\n\n/** Readiness rows that are options, keyed by the option they set. */\nexport const READINESS_ROWS = Object.freeze({\n strict: 'production response changes require an authorized context',\n previewSignals: 'query-only intent signal',\n disableReferrerDetection: 'referrer trust off',\n eventSourcePolicy: 'messages must come from parent/opener',\n skipUnchanged: 'skip unchanged bindings by default',\n sanitizerPolicy: 'hardened sanitizer id/data-* policy',\n} as const);\n\n/** Options an adapter's request decision reads from the profile. */\nexport interface AdapterProfileDefaults {\n readonly strict: boolean;\n readonly previewSignals: readonly ('query' | 'fetch-dest' | 'referer')[];\n}\n\n/** Options the browser runtime reads from the profile (through the inline configuration). */\nexport interface RuntimeProfileDefaults {\n readonly disableReferrerDetection: boolean;\n readonly eventSourcePolicy: EventSourcePolicy;\n readonly skipUnchanged: boolean;\n readonly sanitizerPolicy: 'compat' | 'strict';\n}\n\nexport const V2_ADAPTER_DEFAULTS: AdapterProfileDefaults = Object.freeze({\n strict: true,\n previewSignals: Object.freeze(['query'] as const),\n});\n\nexport const V2_RUNTIME_DEFAULTS: RuntimeProfileDefaults = Object.freeze({\n disableReferrerDetection: true,\n eventSourcePolicy: 'parent-or-opener',\n skipUnchanged: true,\n sanitizerPolicy: 'strict',\n});\n\n/** The 1.x table, spelled out so the profiles compare row by row. */\nexport const V1_ADAPTER_DEFAULTS: AdapterProfileDefaults = Object.freeze({\n strict: false,\n previewSignals: Object.freeze(['query', 'fetch-dest', 'referer'] as const),\n});\n\nexport const V1_RUNTIME_DEFAULTS: RuntimeProfileDefaults = Object.freeze({\n disableReferrerDetection: false,\n eventSourcePolicy: 'any',\n skipUnchanged: false,\n sanitizerPolicy: 'compat',\n});\n\n/** Only an explicit `'v1'` opts out; everything else gets the hardened rows. */\nexport function adapterDefaultsFor(profile: DefaultsProfile | undefined): AdapterProfileDefaults {\n return profile === 'v1' ? V1_ADAPTER_DEFAULTS : V2_ADAPTER_DEFAULTS;\n}\n\nexport function runtimeDefaultsFor(profile: DefaultsProfile | undefined): RuntimeProfileDefaults {\n return profile === 'v1' ? V1_RUNTIME_DEFAULTS : V2_RUNTIME_DEFAULTS;\n}\n","/**\n * `pll doctor --v2`: read the served inline configuration and report each\n * runtime row still at its 1.x value (ADR 0007).\n *\n * An empty slot means whatever the generation that wrote the script meant by\n * it, and that is exactly what changed between 1.x and 2.0. So the script\n * names the defaults it was resolved against, in its last slot, and this reads\n * the answer instead of guessing it. A script without the marker predates it —\n * 1.x or 2.0.0-beta.0 — and is read as 1.x, with a line that says so.\n */\nimport { INLINE_CONFIG_KEYS } from '@/types/inline-config';\nimport {\n V1_RUNTIME_DEFAULTS,\n V2_RUNTIME_DEFAULTS,\n type RuntimeProfileDefaults,\n} from '@/types/defaults-profile';\nimport type { DoctorFinding, DoctorProbe } from './types';\n\nconst CONFIG_MARKER = 'var __LIVE_PREVIEW_CONFIG__=';\nconst DEFAULTS_SLOT = INLINE_CONFIG_KEYS.indexOf('defaults');\n\n/** A runtime row of the readiness table, as a finding words it. */\ninterface ReadinessRow {\n readonly key: keyof RuntimeProfileDefaults;\n readonly title: string;\n /** What the 1.x value does. */\n readonly effect: string;\n /** What has to hold before the row can take its 2.0 value. */\n readonly condition: string;\n}\n\nconst ROWS: readonly ReadinessRow[] = [\n {\n key: 'disableReferrerDetection',\n title: 'Referrer trust is still on',\n effect: 'the runtime accepts the admin referer as a preview signal.',\n condition: 'no preview flow relies on the admin referer alone',\n },\n {\n key: 'eventSourcePolicy',\n title: 'Messages are accepted from any window',\n effect: 'any window that passes the origin check may post updates.',\n condition: 'no window but the parent or opener posts updates',\n },\n {\n key: 'sanitizerPolicy',\n title: 'Sanitizer is in compat mode',\n effect: 'id and every data-* pass.',\n condition: 'rich text no longer relies on `id` or `data-*`',\n },\n {\n key: 'skipUnchanged',\n title: 'Unchanged bindings are re-applied every message',\n effect: 'every binding is written again on every message.',\n condition: 'no renderer relies on running again for a value that did not change',\n },\n];\n\n/** Where a row's value came from, which decides what the reader can change. */\ntype Origin = 'profile' | 'explicit' | 'unmarked';\n\nconst ORIGIN_DETAIL: Readonly<Record<Origin, string>> = {\n profile: \"The script was generated with `defaults: 'v1'`.\",\n explicit: 'The script sets it explicitly.',\n unmarked: 'The script names no defaults, so its empty slot is read as the 1.x value.',\n};\n\nconst UNREADABLE: DoctorFinding = {\n code: 'LP0709',\n level: 'info',\n title: 'Could not read the inline configuration for a v2 readiness check',\n detail: 'The preview response carried no readable `__LIVE_PREVIEW_CONFIG__` inline script.',\n remedy:\n 'Run this against a page with the inline runtime (not loader mode without a preview context). ' +\n 'A preview behind authorizePreview serves it only to a request with credentials: pass them with --header, or a signed token as ?previewToken=… in the URL.',\n};\n\nconst UNMARKED: DoctorFinding = {\n code: 'LP0709',\n level: 'info',\n title: 'The inline configuration does not say which defaults it was generated against',\n detail:\n 'Scripts carry a `defaults` marker from 2.0.0-rc.1 on; this one has none, so a 1.x or 2.0.0-beta.0 generator wrote it. An empty slot is read as its 1.x value, which is what a 1.x runtime runs.',\n remedy:\n 'On 1.x the warnings stand as written. On 2.0.0-beta.0 an empty slot already runs its 2.0 value, so a warning about one does not apply there; upgrade and run `pll doctor --v2` again to read the marker.',\n};\n\n/**\n * The array literal after the marker, read as JSON. The generator writes `,,`\n * for unset slots, which JSON cannot express, so elisions outside strings\n * become `null`. Never evaluated: a page controls this text.\n */\nexport function readInlineConfig(body: string): readonly unknown[] | undefined {\n const start = body.indexOf(CONFIG_MARKER);\n if (start === -1) return undefined;\n const from = start + CONFIG_MARKER.length;\n if (body[from] !== '[') return undefined;\n let depth = 0;\n let inString = false;\n let previous = '';\n let json = '';\n for (let index = from; index < body.length; index += 1) {\n const char = body[index] ?? '';\n if (inString) {\n json += char;\n if (char === '\\\\') {\n json += body[index + 1] ?? '';\n index += 1;\n } else if (char === '\"') {\n inString = false;\n // A closed string is a value: without this, the next `,` reads as an elision.\n previous = '\"';\n }\n continue;\n }\n if (char === '\"') {\n inString = true;\n json += char;\n continue;\n }\n if (/\\s/u.test(char)) continue;\n const elision =\n (char === ',' && (previous === ',' || previous === '[')) ||\n (char === ']' && previous === ',');\n if (elision) json += 'null';\n json += char;\n previous = char;\n if (char === '[') depth += 1;\n if (char === ']') {\n depth -= 1;\n if (depth === 0) break;\n }\n }\n if (depth !== 0) return undefined;\n try {\n const value: unknown = JSON.parse(json);\n return Array.isArray(value) ? (value as readonly unknown[]) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** `'compat'` rather than `\"compat\"`: remedies are read as source code. */\nfunction literal(value: unknown): string {\n return typeof value === 'string' ? `'${value}'` : JSON.stringify(value);\n}\n\nfunction remedyFor(origin: Origin, row: ReadinessRow, value: unknown): string {\n const wanted = `\\`${row.key}: ${literal(V2_RUNTIME_DEFAULTS[row.key])}\\``;\n switch (origin) {\n case 'profile':\n return `Set ${wanted} or drop \\`defaults: 'v1'\\` once ${row.condition}.`;\n case 'explicit':\n return `Drop the explicit \\`${row.key}: ${literal(value)}\\` once ${row.condition}; 2.0 defaults to ${wanted}.`;\n case 'unmarked':\n // 1.8.1 has no `defaults` option and three of the four rows are not\n // inline options there, so the way out is the upgrade itself.\n return `Upgrading to 2.0 sets ${wanted}; check first that ${row.condition}.`;\n }\n}\n\nfunction gap(row: ReadinessRow, value: unknown, origin: Origin): DoctorFinding {\n return {\n code: 'LP0709',\n level: 'warning',\n title: row.title,\n detail: `${row.key} is ${literal(value)}: ${row.effect} ${ORIGIN_DETAIL[origin]}`,\n remedy: remedyFor(origin, row, value),\n };\n}\n\nfunction unknownGeneration(marker: unknown): DoctorFinding {\n return {\n code: 'LP0709',\n level: 'info',\n title: 'The inline configuration names defaults this doctor does not know',\n detail: `Its \\`defaults\\` marker is ${JSON.stringify(marker)}; this version reads 'v1' and 'v2', so no row was judged.`,\n remedy: 'Run the `pll doctor` of the package version that generated the page.',\n };\n}\n\n/**\n * One `LP0709` finding per runtime row not yet at its 2.0 value. An empty slot\n * has the value of the generation the script names, and the 1.x value when it\n * names none; a generation this version does not know is not judged at all.\n * @internal\n */\nexport function analyzeV2Readiness(probe: DoctorProbe): readonly DoctorFinding[] {\n const config = readInlineConfig(probe.previewResponse.body);\n if (config === undefined) return [UNREADABLE];\n const marker = config[DEFAULTS_SLOT] ?? undefined;\n if (marker !== undefined && marker !== 'v1' && marker !== 'v2') {\n return [unknownGeneration(marker)];\n }\n const generation = marker;\n const fallback = generation === 'v2' ? V2_RUNTIME_DEFAULTS : V1_RUNTIME_DEFAULTS;\n const findings = ROWS.flatMap((row) => {\n const slot = config[INLINE_CONFIG_KEYS.indexOf(row.key)] ?? undefined;\n const value = slot ?? fallback[row.key];\n if (value === V2_RUNTIME_DEFAULTS[row.key]) return [];\n const origin: Origin =\n generation === 'v1' ? 'profile' : slot === undefined ? 'unmarked' : 'explicit';\n return [gap(row, value, origin)];\n });\n return generation === undefined ? [...findings, UNMARKED] : findings;\n}\n","/**\n * The `pll doctor` audit: judge what a deployment serves from two fetches of\n * one URL, as a visitor and as the admin's iframe. Pure — the caller fetches.\n * Markup is read with regular expressions, which is enough to count\n * attributes and find the runtime marker; findings say what was observed.\n */\nimport { DIAGNOSTIC_CODES } from '../core/diagnostic-codes';\nimport { frameAncestorsAdmits, frameAncestorsOf } from './csp';\nimport { analyzeV2Readiness } from './readiness';\nimport type {\n DoctorContext,\n DoctorFinding,\n DoctorProbe,\n DoctorReport,\n DoctorResponse,\n} from './types';\n\n/** Survives minification; the banner comment does not. */\nconst RUNTIME_MARKER = '__LIVE_PREVIEW_CONFIG__';\nconst BINDING_ATTRIBUTE = /\\bdata-payload-field\\s*=/gu;\nconst OWNER_ATTRIBUTE = /\\bdata-payload-owner\\s*=/gu;\n/** Mirrors the scheduler default; the audit must not import the runtime. */\nconst DEFAULT_VISIBILITY_GATE_THRESHOLD = 50;\nconst LEVEL_ORDER = { error: 0, warning: 1, info: 2 } as const;\n// An end tag may carry junk before its `>`; browsers still close on it, so the\n// pattern must too, or a `</script foo>` would leave the script body counted.\nconst NON_MARKUP =\n /<script\\b[^>]*>[\\s\\S]*?<\\/script\\b[^>]*>|<style\\b[^>]*>[\\s\\S]*?<\\/style\\b[^>]*>|<!--[\\s\\S]*?-->/giu;\n\n/**\n * The runtime's own source spells `data-payload-field=` in a message, so\n * scripts must not be counted. This is a counter's view of the page, not a\n * sanitiser — nothing here is rendered — but it strips to a fixed point all\n * the same, so a block that only appears once its neighbour is gone is still\n * removed rather than counted.\n */\nfunction visibleMarkup(body: string): string {\n let markup = body;\n for (;;) {\n const stripped = markup.replace(NON_MARKUP, '');\n if (stripped === markup) return markup;\n markup = stripped;\n }\n}\n\nfunction count(haystack: string, pattern: RegExp): number {\n return haystack.match(pattern)?.length ?? 0;\n}\n\n/** Bindings before the first owner marker are certainly unowned; nesting is not modelled, so this under-reports. */\nfunction unownedBindingsBeforeFirstOwner(markup: string): number {\n const firstOwner = markup.search(/\\bdata-payload-owner\\s*=/u);\n return firstOwner === -1 ? 0 : count(markup.slice(0, firstOwner), BINDING_ATTRIBUTE);\n}\n\nfunction tryUrl(value: string | undefined): URL | undefined {\n if (value === undefined) return undefined;\n try {\n return new URL(value);\n } catch {\n return undefined;\n }\n}\n\n/** Why the preview response cannot be audited as a page; the public one may legitimately be gated. */\nfunction describeNonPage(preview: DoctorResponse): DoctorFinding | undefined {\n if (preview.status >= 300 && preview.status <= 399) {\n const location = preview.headers['location'];\n return {\n code: 'LP0708',\n level: 'error',\n title: `The preview request was redirected (${String(preview.status)})${location === undefined ? '' : ` to ${location}`}`,\n detail:\n 'The audit does not follow redirects: the page at the end of one is a different ' +\n 'resource, usually a login or a canonical host, and judging it would say nothing ' +\n 'about this URL.',\n remedy:\n 'The request carried the intent parameter (?preview=true, or the --param name), the ' +\n \"way the admin's iframe loads the page, so a rule that redirects on that query redirects \" +\n 'the iframe too. A redirect to a login means the route needs authentication that a ' +\n \"plain probe cannot supply: pass an editor's credentials with --header, or a signed token as ?previewToken=… in the URL. Probe the final \" +\n 'URL directly only when the redirect has nothing to do with either.',\n };\n }\n if (preview.status < 200 || preview.status > 299) {\n return {\n code: 'LP0708',\n level: 'error',\n title: `The preview request returned ${String(preview.status)}, not a page`,\n detail:\n 'Nothing below this can be judged: a status outside 2xx means the body is an ' +\n 'error page or empty.',\n remedy: 'Check the URL.',\n };\n }\n if (preview.body.trim() === '') {\n return {\n code: 'LP0708',\n level: 'error',\n title: `The preview request returned ${String(preview.status)} with an empty body`,\n detail:\n 'A 2xx status with nothing in it — 204 and 205 are defined that way — is not a page, ' +\n 'so its missing runtime and missing bindings say nothing.',\n remedy: 'Point the audit at a route that renders a document.',\n };\n }\n const contentType = preview.headers['content-type'];\n if (contentType !== undefined && !contentType.toLowerCase().includes('html')) {\n return {\n code: 'LP0708',\n level: 'error',\n title: `The preview request returned ${contentType}, not HTML`,\n detail:\n 'The audit reads a rendered page. A non-HTML response has no runtime and no ' +\n 'bindings by definition, so reporting their absence would say nothing.',\n remedy: 'Point the audit at a page route rather than an API or an asset.',\n };\n }\n return undefined;\n}\n\nfunction framingFindings(\n preview: DoctorResponse,\n runtimeInPreview: boolean,\n context: DoctorContext,\n): DoctorFinding[] {\n const findings: DoctorFinding[] = [];\n const page = tryUrl(context.url);\n const admin = tryUrl(context.adminOrigin);\n const sameOrigin = admin !== undefined && page?.origin === admin.origin;\n const frameAncestors = frameAncestorsOf(preview.headers['content-security-policy']);\n if (runtimeInPreview && frameAncestors === undefined) {\n findings.push({\n code: 'LP0702',\n level: 'warning',\n title: 'Preview response declares no frame-ancestors',\n detail:\n 'The runtime is injected but the response carries no frame-ancestors directive, ' +\n 'so nothing states which origins may embed this page.',\n remedy:\n 'Let the adapter manage CSP, or set frame-ancestors yourself to the admin origin. ' +\n 'Without it you are relying on the absence of a policy.',\n });\n } else if (frameAncestors !== undefined && context.adminOrigin !== undefined) {\n const admitted =\n admin !== undefined &&\n page !== undefined &&\n frameAncestorsAdmits(frameAncestors, admin, page);\n if (!admitted) {\n findings.push({\n code: 'LP0702',\n level: 'error',\n title: 'frame-ancestors does not admit the admin origin',\n detail:\n admin === undefined\n ? `\"${context.adminOrigin}\" is not an absolute URL, so no source expression can admit it.`\n : `Served: \"${frameAncestors}\". Expected it to allow ${admin.origin}.`,\n remedy:\n 'Add the admin origin to allowedOrigins so the adapter merges it into ' +\n 'frame-ancestors, or add it to your own policy.',\n });\n }\n }\n const frameOptions = preview.headers['x-frame-options'];\n const blocks =\n frameOptions !== undefined &&\n (/deny/iu.test(frameOptions) || (/sameorigin/iu.test(frameOptions) && !sameOrigin));\n if (blocks) {\n // `SAMEORIGIN` refuses a frame from *another* origin, so it is a verdict\n // about this preview only once the admin origin is known. Without --admin\n // the audit says what it saw and names the condition instead of asserting a\n // block it cannot have established: where the admin and the site share an\n // origin, the preview runs. `DENY` needs no origin to be a verdict.\n const judged = admin !== undefined || /deny/iu.test(frameOptions);\n findings.push(\n judged\n ? {\n code: 'LP0703',\n level: 'error',\n title: `X-Frame-Options: ${frameOptions} blocks the preview iframe`,\n detail:\n 'This header is older than CSP and browsers honour it independently. No ' +\n 'frame-ancestors directive can override it.',\n remedy:\n 'Remove X-Frame-Options for preview responses. It is usually set by a proxy ' +\n 'or a security middleware rather than by the app.',\n }\n : {\n code: 'LP0703',\n level: 'warning',\n title: `X-Frame-Options: ${frameOptions} blocks every frame from another origin`,\n detail:\n 'This header is older than CSP and browsers honour it independently, and no ' +\n 'frame-ancestors directive can override it. SAMEORIGIN refuses a frame from ' +\n 'another origin only, and no admin origin was given: whether it blocks this ' +\n 'preview depends on where the admin is served, which this probe cannot see.',\n remedy:\n 'Re-run with --admin <origin> to have it judged. If the admin is on another ' +\n 'origin, remove X-Frame-Options for preview responses; a proxy or a security ' +\n 'middleware usually sets it.',\n },\n );\n }\n return findings;\n}\n\nfunction bindingFindings(\n previewMarkup: string,\n publicMarkup: string,\n runtime: boolean,\n): DoctorFinding[] {\n const findings: DoctorFinding[] = [];\n const previewBindings = count(previewMarkup, BINDING_ATTRIBUTE);\n const publicBindings = count(publicMarkup, BINDING_ATTRIBUTE);\n const owners = count(previewMarkup, OWNER_ATTRIBUTE);\n if (publicBindings > 0) {\n findings.push({\n code: 'LP0704',\n level: 'warning',\n title: `${String(publicBindings)} binding attribute(s) served to anonymous visitors`,\n detail:\n 'A request with no preview intent received data-payload-field attributes. These ' +\n 'disclose the CMS field taxonomy, and any CSS keyed on them changes public layout ' +\n 'the moment emission is gated.',\n remedy:\n 'Gate binding emission on an authorized preview context — see ' +\n 'createPreviewBindings(), whose suppressed form emits nothing at all.',\n });\n }\n if (previewBindings > DEFAULT_VISIBILITY_GATE_THRESHOLD) {\n findings.push({\n code: 'LP0705',\n level: 'warning',\n title: `${String(previewBindings)} bindings exceed the default visibility gate`,\n detail:\n `Above visibilityGateThreshold (${String(DEFAULT_VISIBILITY_GATE_THRESHOLD)}) the ` +\n 'scheduler stops writing offscreen elements and buffers them until they scroll into ' +\n 'view. On a page nobody scrolls, that is never.',\n remedy:\n 'Raise visibilityGateThreshold if the whole page must stay live, or confirm that ' +\n 'deferring below the fold is acceptable here.',\n });\n }\n if (runtime && previewBindings === 0) {\n findings.push({\n code: 'LP0707',\n level: 'error',\n title: 'Preview response carries no bindings',\n detail:\n 'The runtime is present but there is not one data-payload-field element for it to ' +\n 'write into, so no edit can ever be visible on this page.',\n remedy:\n 'Add binding attributes to the markup. pll-codegen --inventory lists every field ' +\n 'the schema makes addressable.',\n });\n }\n const unowned = unownedBindingsBeforeFirstOwner(previewMarkup);\n if (owners > 0 && unowned > 0) {\n findings.push({\n code: 'LP0706',\n level: 'warning',\n title: `${String(unowned)} binding(s) outside every owner marker`,\n detail:\n `The page declares ${String(owners)} owner marker(s), so owner scoping is in use. ` +\n 'Bindings that belong to no owner receive nothing once scopeBindingsByOwner is on — ' +\n 'they fail closed.',\n remedy:\n 'Give every binding an owning ancestor, or leave scopeBindingsByOwner off until they ' +\n 'all have one. Counted before the first owner marker only, so the real number may be ' +\n 'higher.',\n });\n }\n return findings;\n}\n\n/** Audit a probed deployment. */\nexport function analyzeProbe(\n probe: DoctorProbe,\n context: DoctorContext & {\n readonly v2?: boolean;\n /** Whether the preview probe carried caller-supplied headers (`--header`). */\n readonly credentials?: boolean;\n },\n): DoctorReport {\n const { publicResponse: pub, previewResponse: preview } = probe;\n const notAPage = describeNonPage(preview);\n if (notAPage !== undefined) {\n return { url: context.url, findings: [notAPage], errors: 1, warnings: 0 };\n }\n const runtimeInPreview = preview.body.includes(RUNTIME_MARKER);\n const findings: DoctorFinding[] = [];\n if (!runtimeInPreview) {\n findings.push(\n context.credentials === true\n ? {\n code: 'LP0701',\n level: 'warning',\n title: 'No inline runtime in the preview response',\n detail:\n `The preview request returned ${String(preview.status)} without the inline ` +\n 'runtime, although it carried credentials (--header, or ?previewToken= in the URL). Three readings: ' +\n 'those credentials were not accepted — expired, issued for another page, or not ' +\n 'what authorizePreview reads — an adapter that did not recognise the request as a ' +\n 'preview, or a consumer that starts LivePreviewClient itself.',\n remedy:\n 'Check that the credentials are current and are the ones authorizePreview reads; ' +\n 'if you use an adapter, check its inject mode and previewSignals. If you start the ' +\n 'client yourself, this line is expected.',\n }\n : {\n code: 'LP0701',\n level: 'warning',\n title: 'No inline runtime in the preview response',\n detail:\n `The preview request returned ${String(preview.status)} without the inline ` +\n 'runtime. Three readings, and this audit cannot tell them apart from the response ' +\n 'alone: a preview behind authorizePreview — the strict 2.0 default — which answers ' +\n 'a request without credentials exactly like this, an adapter that did not recognise ' +\n 'the request as a preview, or a consumer that starts LivePreviewClient itself.',\n remedy:\n \"To audit a gated preview, send what an editor's browser sends: \" +\n \"--header 'Cookie: payload-token=…' for a Payload session; for a signed token, \" +\n 'the URL with ?previewToken=… (the default transport, dropped from the visitor ' +\n \"request), or --header 'x-preview-token: …' where the strategy sets transport: \" +\n \"{ kind: 'header' }. Otherwise check the adapter's \" +\n 'inject mode and previewSignals; if you start the client yourself, this line is ' +\n 'expected.',\n },\n );\n }\n findings.push(...framingFindings(preview, runtimeInPreview, context));\n findings.push(\n ...bindingFindings(visibleMarkup(preview.body), visibleMarkup(pub.body), runtimeInPreview),\n );\n if (pub.body.includes(RUNTIME_MARKER)) {\n findings.push({\n code: DIAGNOSTIC_CODES.RuntimeOnPublicPage,\n level: 'info',\n title: 'Runtime is also served to anonymous visitors',\n detail:\n 'A request with no preview intent received the inline runtime. That is correct for ' +\n \"inject: 'always' and unintended for 'preview-only'.\",\n remedy: '',\n });\n }\n if (context.v2 === true) findings.push(...analyzeV2Readiness(probe));\n findings.sort((a, b) => LEVEL_ORDER[a.level] - LEVEL_ORDER[b.level]);\n return {\n url: context.url,\n findings,\n errors: findings.filter((finding) => finding.level === 'error').length,\n warnings: findings.filter((finding) => finding.level === 'warning').length,\n };\n}\n","/** Terminal rendering of a doctor report. */\nimport type { DoctorReport } from './types';\n\nconst LEVEL_LABEL = { error: 'ERROR', warning: 'WARN ', info: 'INFO ' } as const;\n\n/** @internal */\nexport function formatReport(report: DoctorReport): string {\n const lines: string[] = [`pll doctor — ${report.url}`, ''];\n if (report.findings.length === 0) {\n lines.push('No findings. The preview response carries the runtime, a frame-ancestors');\n lines.push('policy, and bindings; the public response carries none of them.', '');\n return lines.join('\\n');\n }\n for (const finding of report.findings) {\n lines.push(`${LEVEL_LABEL[finding.level]} ${finding.code} ${finding.title}`);\n lines.push(` ${finding.detail}`);\n if (finding.remedy !== '') lines.push(` → ${finding.remedy}`);\n lines.push('');\n }\n lines.push(`${String(report.errors)} error(s), ${String(report.warnings)} warning(s).`, '');\n return lines.join('\\n');\n}\n","/**\n * Fetching for `pll doctor`: the two probes, and the default fetch that talks\n * to a real server. Not in the barrel so it stays in the coverage report.\n */\nimport { analyzeProbe } from './analyze';\nimport type { DoctorReport, DoctorResponse } from './types';\n\n/** Injectable so tests can drive the audit without a server. */\nexport type DoctorFetch = (\n url: string,\n init: { readonly headers: Readonly<Record<string, string>> },\n) => Promise<DoctorResponse>;\n\nexport interface RunDoctorOptions {\n readonly url: string;\n /** Admin origin the preview is meant to be embedded from, when known. */\n readonly adminOrigin?: string | undefined;\n /** Defaults to a fetch built on `globalThis.fetch`. */\n readonly fetchImpl?: DoctorFetch | undefined;\n /** Also check the served page against the 2.0 readiness table. */\n readonly v2?: boolean;\n /**\n * Headers sent with the preview probe only, never with the visitor probe: the\n * credentials a preview behind `authorizePreview` needs, such as a Payload\n * session `Cookie`, or an `x-preview-token` where the token strategy reads a\n * header (a token in the URL is passed in `url` instead). Without them a gated page answers\n * the audit the way it answers any stranger. Their values never reach the report.\n */\n readonly previewHeaders?: Readonly<Record<string, string>> | undefined;\n /**\n * The query parameters the deployment reads as preview intent, when its\n * adapter sets `previewQueryParams`; the same list, since that option replaces\n * the default `preview`, `draft` and `livePreview`. The first one is what the\n * preview probe appends as `=true`; none of them reaches the visitor probe.\n */\n readonly previewQueryParams?: readonly string[] | undefined;\n}\n\n/**\n * The query parameters an adapter reads as preview intent by default; the same\n * list as `DEFAULT_QUERY_PARAMS` in adapters/shared/preview-request.ts, which\n * `previewQueryParams` replaces rather than extends.\n */\nconst DEFAULT_INTENT_PARAMS: readonly string[] = ['preview', 'draft', 'livePreview'];\n\nfunction isIntentValue(value: string): boolean {\n return value === 'true' || value === '1';\n}\n\n/** `key=value` and `#fragment` split off a URL, with the query kept as the caller spelled it. */\nfunction splitUrl(url: string): {\n readonly base: string;\n readonly parts: string[];\n readonly hash: string;\n} {\n const hashAt = url.indexOf('#');\n const hash = hashAt === -1 ? '' : url.slice(hashAt);\n const beforeHash = hashAt === -1 ? url : url.slice(0, hashAt);\n const queryAt = beforeHash.indexOf('?');\n if (queryAt === -1) return { base: beforeHash, parts: [], hash };\n return {\n base: beforeHash.slice(0, queryAt),\n parts: beforeHash.slice(queryAt + 1).split('&'),\n hash,\n };\n}\n\nfunction decodePart(part: string): readonly [string, string] {\n const at = part.indexOf('=');\n const decode = (text: string): string => {\n try {\n return decodeURIComponent(text.replace(/\\+/gu, ' '));\n } catch {\n return text;\n }\n };\n return at === -1 ? [decode(part), ''] : [decode(part.slice(0, at)), decode(part.slice(at + 1))];\n}\n\n/**\n * The query parameter the `signed-token` strategy reads by default. A token\n * audit puts the token in the URL, and it belongs to the preview request only:\n * in the visitor request it would authorize the \"anonymous\" probe and, behind a\n * replay store, spend the token before the preview request arrives.\n */\nconst TOKEN_PARAM = 'previewToken';\n\n/**\n * The URL as the audit prints it: the token's value replaced by `…`. Header\n * values never reach the report, and a token passed in the URL is the same\n * credential, so the report, `--json` and the failure line all show this form.\n * @internal\n */\nexport function redactToken(url: string): string {\n const { base, parts, hash } = splitUrl(url);\n const shown = parts.map((part) => {\n const at = part.indexOf('=');\n return at === -1 || decodePart(part)[0] !== TOKEN_PARAM ? part : `${part.slice(0, at)}=…`;\n });\n return shown.some((part, index) => part !== parts[index])\n ? `${base}?${shown.join('&')}${hash}`\n : url;\n}\n\n/** Whether the URL carries a non-empty token for the preview request. */\nfunction carriesToken(url: string): boolean {\n return splitUrl(url).parts.some((part) => {\n const [key, value] = decodePart(part);\n return key === TOKEN_PARAM && value !== '';\n });\n}\n\n/**\n * The page a visitor requests: the intent parameters removed, the rest of the\n * query byte for byte as given. A query is never re-serialised — `URLSearchParams`\n * would turn `%20` into `+` and add `=` to a bare key, and a page whose query is\n * signed by an edge token would answer a different request than a visitor makes.\n */\nfunction visitorUrl(url: string, params: readonly string[]): string {\n const { base, parts, hash } = splitUrl(url);\n const dropped = new Set([...params, TOKEN_PARAM]);\n const kept = parts.filter((part) => !dropped.has(decodePart(part)[0]));\n if (kept.length === parts.length) return url;\n return `${base}${kept.length === 0 ? '' : `?${kept.join('&')}`}${hash}`;\n}\n\n/**\n * The page the admin's iframe loads. A 2.0 adapter counts only the query as\n * intent (`previewSignals: ['query']`), and `buildLivePreviewUrl` writes\n * `preview=true`, so the first intent parameter is appended as `=true` unless the\n * URL already names one with a value that counts.\n */\nfunction previewUrl(url: string, params: readonly string[]): string {\n const { base, parts, hash } = splitUrl(url);\n const carries = parts.some((part) => {\n const [key, value] = decodePart(part);\n return params.includes(key) && isIntentValue(value);\n });\n if (carries) return url;\n const query = parts.join('&');\n const joiner = query === '' ? '?' : query.endsWith('&') ? '' : '&';\n const intent = `${encodeURIComponent(params[0] ?? 'preview')}=true`;\n return `${base}${parts.length === 0 ? '?' : `?${query}${joiner}`}${intent}${hash}`;\n}\n\nexport const DEFAULT_TIMEOUT_MS = 15_000;\nexport const MAX_BODY_BYTES = 10 * 1024 * 1024;\n\nexport interface DefaultFetchOptions {\n readonly fetchFn?: typeof fetch;\n readonly timeoutMs?: number;\n readonly maxBodyBytes?: number;\n}\n\n/** Header names lowercased; every check downstream reads a lowercase key. @internal */\nexport function lowercaseHeaders(headers: Headers): Record<string, string> {\n const out: Record<string, string> = {};\n headers.forEach((value, key) => {\n out[key.toLowerCase()] = value;\n });\n return out;\n}\n\n/** The error's message plus its cause's, which is where undici puts `self-signed certificate` and friends. */\nexport function describeFailure(error: unknown): string {\n if (!(error instanceof Error)) return String(error);\n const cause = error.cause;\n if (cause instanceof Error && cause.message !== '' && !error.message.includes(cause.message)) {\n return `${error.message} (${cause.message})`;\n }\n return error.message;\n}\n\nasync function readBody(response: Response, limit: number): Promise<string> {\n const reader = response.body?.getReader();\n if (reader === undefined) return response.text();\n const decoder = new TextDecoder();\n let received = 0;\n let text = '';\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n received += value.byteLength;\n if (received > limit) {\n await reader.cancel();\n throw new Error(\n `the response body exceeds ${String(limit)} bytes, which is not a page the audit can judge`,\n );\n }\n text += decoder.decode(value, { stream: true });\n }\n return text + decoder.decode();\n}\n\n/** Redirects are reported, not followed; a hanging origin is abandoned; a runaway body is refused. */\nexport function createDefaultFetch(options: DefaultFetchOptions = {}): DoctorFetch {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const limit = options.maxBodyBytes ?? MAX_BODY_BYTES;\n return async (url, init) => {\n const fetchFn = options.fetchFn ?? globalThis.fetch;\n let response: Response;\n try {\n response = await fetchFn(url, {\n headers: init.headers,\n redirect: 'manual',\n signal: AbortSignal.timeout(timeoutMs),\n });\n } catch (error) {\n if (error instanceof Error && error.name === 'TimeoutError') {\n throw new Error(`no response within ${String(timeoutMs / 1000)} s`, { cause: error });\n }\n throw error;\n }\n return {\n status: response.status,\n headers: lowercaseHeaders(response.headers),\n body: await readBody(response, limit),\n };\n };\n}\n\n/** The referer the admin's iframe would send: the origin (or the given admin path) with one slash. */\nexport function previewReferer(adminOrigin: string): string {\n try {\n return new URL(adminOrigin).href;\n } catch {\n return `${adminOrigin.replace(/\\/+$/u, '')}/`;\n }\n}\n\n/**\n * The caller's headers without the ones the probe sets itself. Header names are\n * case-insensitive, and `fetch` combines two spellings of one name into one\n * comma-joined value, so a caller's `sec-fetch-dest` would otherwise turn the\n * probe's `iframe` into `document, iframe`. The probe's own headers win.\n */\nfunction callerHeaders(\n headers: Readonly<Record<string, string>> | undefined,\n own: Readonly<Record<string, string>>,\n): Record<string, string> {\n const taken = new Set(Object.keys(own).map((name) => name.toLowerCase()));\n return Object.fromEntries(\n Object.entries(headers ?? {}).filter(([name]) => !taken.has(name.toLowerCase())),\n );\n}\n\n/** Fetch the URL twice — as a visitor and as the admin's iframe — and audit the difference. */\nexport async function runDoctor(options: RunDoctorOptions): Promise<DoctorReport> {\n const fetchImpl = options.fetchImpl ?? createDefaultFetch();\n const params =\n options.previewQueryParams === undefined || options.previewQueryParams.length === 0\n ? DEFAULT_INTENT_PARAMS\n : options.previewQueryParams;\n // No referer, no intent parameter and no credentials on the visitor probe:\n // each of them is a preview signal or a way past one.\n const publicResponse = await fetchImpl(visitorUrl(options.url, params), {\n headers: {\n Accept: 'text/html',\n 'Sec-Fetch-Dest': 'document',\n 'Sec-Fetch-Mode': 'navigate',\n },\n });\n const probeHeaders: Record<string, string> = {\n Accept: 'text/html',\n 'Sec-Fetch-Dest': 'iframe',\n 'Sec-Fetch-Mode': 'navigate',\n ...(options.adminOrigin === undefined ? {} : { Referer: previewReferer(options.adminOrigin) }),\n };\n const sent = callerHeaders(options.previewHeaders, probeHeaders);\n const previewResponse = await fetchImpl(previewUrl(options.url, params), {\n headers: { ...sent, ...probeHeaders },\n });\n return analyzeProbe(\n { publicResponse, previewResponse },\n {\n url: redactToken(options.url),\n adminOrigin: options.adminOrigin,\n ...(options.v2 === true ? { v2: true } : {}),\n ...(Object.keys(sent).length > 0 || carriesToken(options.url) ? { credentials: true } : {}),\n },\n );\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,4 +1,4 @@
1
- import { e as PreviewAuthorizationRequest, f as AuthorizedPreviewScope, A as AuthorizedPreviewContext, P as PreviewAdapterOptions } from './options-BW1eqXvc.js';
1
+ import { e as PreviewAuthorizationRequest, f as AuthorizedPreviewScope, A as AuthorizedPreviewContext, P as PreviewAdapterOptions } from './options-uHKrDM2U.js';
2
2
 
3
3
  /**
4
4
  * The `payload-session` strategy: forward exactly one named cookie to
@@ -24,7 +24,7 @@ interface PayloadSessionStrategy {
24
24
  readonly cookieName?: string;
25
25
  /** Upper bound for the `/me` round trip. Below 250 ms is raised to 250 ms; default 3000. */
26
26
  readonly timeoutMs?: number;
27
- /** Longest cookie value accepted; longer ones are refused as `invalid`. Default 4096. */
27
+ /** Longest cookie value accepted; longer ones are refused as `missing-credential`. Default 4096. */
28
28
  readonly maxCookieLength?: number;
29
29
  readonly fetch?: FetchLike;
30
30
  /** Clock, Unix milliseconds. Injectable for tests. */