payload-live-preview 2.2.2 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +20 -17
  2. package/dist/adapters/astro/PreviewBoundary.astro +17 -4
  3. package/dist/adapters/astro/RichText.astro +13 -2
  4. package/dist/adapters/astro/index.d.ts +7 -7
  5. package/dist/adapters/astro/index.js +1 -1
  6. package/dist/adapters/astro/index.js.map +1 -1
  7. package/dist/adapters/astro/middleware-entry.d.ts +3 -3
  8. package/dist/adapters/astro/middleware-entry.js +1 -1
  9. package/dist/adapters/astro/middleware-entry.js.map +1 -1
  10. package/dist/adapters/nextjs/index.d.ts +2 -2
  11. package/dist/adapters/nextjs/index.js +1 -1
  12. package/dist/adapters/nextjs/index.js.map +1 -1
  13. package/dist/adapters/nuxt/index.d.ts +4 -4
  14. package/dist/adapters/nuxt/index.js +1 -1
  15. package/dist/adapters/nuxt/index.js.map +1 -1
  16. package/dist/adapters/nuxt/module.d.ts +1 -1
  17. package/dist/adapters/react/index.d.ts +2 -2
  18. package/dist/adapters/react/index.js +1 -1
  19. package/dist/adapters/react/index.js.map +1 -1
  20. package/dist/adapters/sveltekit/index.d.ts +5 -5
  21. package/dist/adapters/sveltekit/index.js +1 -1
  22. package/dist/adapters/sveltekit/index.js.map +1 -1
  23. package/dist/adapters/vue/index.d.ts +2 -2
  24. package/dist/adapters/vue/index.js +1 -1
  25. package/dist/adapters/vue/index.js.map +1 -1
  26. package/dist/client.cjs +1 -1
  27. package/dist/client.cjs.map +1 -1
  28. package/dist/client.d.cts +15 -3
  29. package/dist/client.d.ts +15 -3
  30. package/dist/client.js +1 -1
  31. package/dist/client.js.map +1 -1
  32. package/dist/codegen-astro.js +1 -1
  33. package/dist/codegen-astro.js.map +1 -1
  34. package/dist/codegen-cli.js +1 -1
  35. package/dist/codegen-cli.js.map +1 -1
  36. package/dist/codegen.cjs +1 -1
  37. package/dist/codegen.cjs.map +1 -1
  38. package/dist/codegen.js +1 -1
  39. package/dist/codegen.js.map +1 -1
  40. package/dist/core.cjs +1 -1
  41. package/dist/core.cjs.map +1 -1
  42. package/dist/core.d.cts +139 -4
  43. package/dist/core.d.ts +139 -4
  44. package/dist/core.js +1 -1
  45. package/dist/core.js.map +1 -1
  46. package/dist/doctor-cli.d.ts +1 -1
  47. package/dist/doctor-cli.js +1 -1
  48. package/dist/doctor-cli.js.map +1 -1
  49. package/dist/doctor.d.ts +2 -2
  50. package/dist/doctor.js +1 -1
  51. package/dist/doctor.js.map +1 -1
  52. package/dist/{document-session-Bkd181PV.d.ts → document-session-BuYuO3ae.d.ts} +27 -2
  53. package/dist/{fragment-endpoint-CLRpNQ3n.d.ts → fragment-endpoint-CQE_7MKi.d.ts} +1 -1
  54. package/dist/fragment.cjs +1 -1
  55. package/dist/fragment.cjs.map +1 -1
  56. package/dist/fragment.d.cts +6 -2
  57. package/dist/fragment.d.ts +6 -2
  58. package/dist/fragment.js +1 -1
  59. package/dist/fragment.js.map +1 -1
  60. package/dist/index.cjs +1 -1
  61. package/dist/index.cjs.map +1 -1
  62. package/dist/index.d.cts +73 -12
  63. package/dist/index.d.ts +73 -12
  64. package/dist/index.js +1 -1
  65. package/dist/index.js.map +1 -1
  66. package/dist/lean.cjs +1 -1
  67. package/dist/lean.cjs.map +1 -1
  68. package/dist/lean.js +1 -1
  69. package/dist/lean.js.map +1 -1
  70. package/dist/lexical.cjs +1 -1
  71. package/dist/lexical.cjs.map +1 -1
  72. package/dist/lexical.d.cts +16 -4
  73. package/dist/lexical.d.ts +16 -4
  74. package/dist/lexical.js +1 -1
  75. package/dist/lexical.js.map +1 -1
  76. package/dist/{locals-zz2gi9dj.d.ts → locals-CyzDKfKU.d.ts} +7 -1
  77. package/dist/{middleware-CI-UTf6R.d.ts → middleware-Dnt6u4nF.d.ts} +2 -2
  78. package/dist/{options-CnYybG51.d.ts → options-DTzhFVJ4.d.ts} +13 -1
  79. package/dist/plugin.cjs +1 -1
  80. package/dist/plugin.cjs.map +1 -1
  81. package/dist/plugin.d.cts +15 -9
  82. package/dist/plugin.d.ts +15 -9
  83. package/dist/plugin.js +1 -1
  84. package/dist/plugin.js.map +1 -1
  85. package/dist/plugins.cjs +1 -1
  86. package/dist/plugins.cjs.map +1 -1
  87. package/dist/plugins.d.cts +8 -2
  88. package/dist/plugins.d.ts +8 -2
  89. package/dist/plugins.js +1 -1
  90. package/dist/plugins.js.map +1 -1
  91. package/dist/{probe-D-IHg9dF.d.ts → probe-afwdYebU.d.ts} +6 -2
  92. package/dist/server.cjs +1 -1
  93. package/dist/server.cjs.map +1 -1
  94. package/dist/server.d.cts +19 -1
  95. package/dist/server.d.ts +19 -1
  96. package/dist/server.js +1 -1
  97. package/dist/server.js.map +1 -1
  98. package/dist/structural.d.cts +2 -0
  99. package/dist/structural.d.ts +2 -0
  100. package/package.json +2 -1
package/dist/doctor.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as DoctorProbe, b as DoctorContext, c as DoctorReport, d as DoctorFinding } from './probe-D-IHg9dF.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-D-IHg9dF.js';
1
+ import { a as DoctorProbe, b as DoctorContext, c as DoctorReport, d as DoctorFinding } from './probe-afwdYebU.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-afwdYebU.js';
3
3
 
4
4
  /** Audit a probed deployment. */
5
5
  declare function analyzeProbe(probe: DoctorProbe, context: DoctorContext & {
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",UnboundField:"LP0203",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",EscalationUnavailable:"LP0808",IslandStartUnavailable:"LP0809",RouteRefreshUnconfirmed:"LP0810",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","softNavigationEvents","subfieldCoverage"],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="__LIVE_PREVIEW_CONFIG__",p=`var ${h}=`,f=/<!--[\s\S]*?-->|<script\b((?:[^>"']|"[^"]*"|'[^']*')*)>([\s\S]*?)<\/script\b[^>]*>/giu,g=/(?:^|\s)[iI][dD]\s*=\s*["']?__LIVE_PREVIEW_CONFIG__(?=["'\s]|$)/u,v=/(?:^|\s)type\s*=\s*["']?application\/json(?=["'\s;]|$)/iu,m=[{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"}],y={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."},w={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 or JSON configuration element.",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."},b={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 P(e){if(!e.includes(h))return;const t=function(e){const t=[];for(const[,r,n]of e.matchAll(f))void 0!==r&&void 0!==n&&t.push({attributes:r,text:n});return t}(e),r=t.find(({text:e})=>e.includes(p)),n=void 0===r?void 0:function(e){const t=e.indexOf(p);if(-1===t)return;const r=t+p.length;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}}(r.text);if(void 0!==n)return{source:"inline",value:e=>n[l.indexOf(e)]??void 0};const i=function(e){for(const{attributes:t,text:r}of e)if(g.test(t)){if(!v.test(t))return;try{const e=JSON.parse(r);return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}catch{return}}}(t);return void 0!==i?{source:"element",value:e=>Object.hasOwn(i,e)?i[e]??void 0:void 0}:void 0}function L(e){return"string"==typeof e?`'${e}'`:JSON.stringify(e)}function $(e,t,r){const n=`\`${t.key}: ${L(c[t.key])}\``;switch(e){case"profile":return`Set ${n} or drop \`defaults: 'v1'\` once ${t.condition}. 3.0 removes the profile; \`pll migrate\` writes its rows out, so each can go on its own.`;case"explicit":return`Drop the explicit \`${t.key}: ${L(r)}\` once ${t.condition}; 2.0 defaults to ${n}.`;case"unmarked":return`Upgrading to 2.0 sets ${n}; check first that ${t.condition}.`}}function k(e,t,r){return{code:"LP0709",level:"warning",title:e.title,detail:`${e.key} is ${L(t)}: ${e.effect} ${y[r]}`,remedy:$(r,e,t)}}function S(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 R(e){const t=P(e.previewResponse.body);if(void 0===t)return[w];const r=t.value("defaults");if(void 0!==r&&"v1"!==r&&"v2"!==r)return[S(r)];const n=r??("element"===t.source?"v2":void 0),i="v2"===n?c:u,o=m.flatMap(e=>{const r=t.value(e.key),o=r??i[e.key];return o===c[e.key]?[]:[k(e,o,"v1"===n?"profile":void 0===r?"unmarked":"explicit")]});return void 0===n?[...o,b]:o}var O="__LIVE_PREVIEW_CONFIG__",T=/<[a-zA-Z](?:"[^"]*"|'[^']*'|[^"'<>])*>/gu,x=/([^\s"'>/=]+)(\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/gu,A={error:0,warning:1,info:2},U=/<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>|<style\b[^>]*>[\s\S]*?<\/style\b[^>]*>|<!--[\s\S]*?-->/giu;function C(e){let t=e;for(;;){const e=t.replace(U,"");if(e===t)return t;t=e}}function F(e){const t=[];for(const r of e.matchAll(T))for(const[,e="",n,i,o,a]of r[0].matchAll(x))void 0!==n&&t.push({name:e.toLowerCase(),value:i??o??a??"",at:r.index});return t}function I(e,t){return e.filter(e=>e.name===t)}function E(e){if(void 0!==e)try{return new URL(e)}catch{return}}function j(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(O),l=[];if(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=E(r.url),o=E(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&&void 0===l&&(/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. A browser applies it to a response that has no frame-ancestors directive, and this response has no frame-ancestors.",remedy:"Remove X-Frame-Options for preview responses, or serve a frame-ancestors directive that admits the admin: the header is then ignored. 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. A browser applies it to a response that has no frame-ancestors directive, and this response has no frame-ancestors. 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 or serve a frame-ancestors directive that admits it; a proxy or a security middleware usually sets the header."})}return n}(i,a,r)),l.push(...function(e,t,r,n){const i=[],o=F(e),a=I(o,"data-payload-field").length,s=I(F(t),"data-payload-field").length,d=I(o,"data-payload-owner").length,l=I(o,"data-payload-fragment").filter(e=>e.value.length>0).length;s>0&&i.push({code:"LP0704",level:"warning",title:`${String(s)} 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."}),void 0!==n.threshold&&a>n.threshold&&i.push({code:"LP0705",level:"warning",title:`${String(a)} bindings exceed the ${n.configured?"configured":"default"} visibility gate`,detail:`Above visibilityGateThreshold (${String(n.threshold)}) 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, or set disableVisibilityGate, on the adapter, LivePreviewClient or generateInlineScript() if the whole page must stay live, or confirm that deferring below the fold is acceptable here."}),r&&0===a&&0===l&&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 and not one data-payload-fragment boundary for a server to render, so no edit can ever be visible on this page.",remedy:"Add binding attributes or fragment boundaries to the markup. pll-codegen --inventory lists every field the schema makes addressable."});const c=function(e){const t=I(e,"data-payload-owner")[0];return void 0===t?0:I(e,"data-payload-field").filter(e=>e.at<t.at).length}(o);return d>0&&c>0&&i.push({code:"LP0706",level:"warning",title:`${String(c)} binding(s) outside every owner marker`,detail:`The page declares ${String(d)} 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}(C(i.body),C(n.body),a,function(e){const t=P(e);if(!0===t?.value("disableVisibilityGate"))return{threshold:void 0,configured:!0};const r=t?.value("visibilityGateThreshold");return"number"==typeof r?{threshold:r,configured:!0}:{threshold:50,configured:!1}}(i.body))),n.body.includes(O)){const t=n.body.includes(`var ${O}=`);l.push({code:e.RuntimeOnPublicPage,level:"info",title:t?"Runtime is also served to anonymous visitors":"Preview configuration is also served to anonymous visitors",detail:t?"A request with no preview intent received the inline runtime. That is correct for inject: 'always' and unintended for 'preview-only'.":"A request with no preview intent received the configuration element a page writes for a client it starts itself. That is correct if the page starts the client for every request, and unintended if it writes the element only for a preview.",remedy:""})}return!0===r.v2&&l.push(...R(t)),l.sort((e,t)=>A[e.level]-A[t.level]),{url:r.url,findings:l,errors:l.filter(e=>"error"===e.level).length,warnings:l.filter(e=>"warning"===e.level).length}}var _={error:"ERROR",warning:"WARN ",info:"INFO "};function z(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(`${_[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 N=["preview","draft","livePreview"];function q(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 B(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 G="previewToken";function M(e,t=G){const{base:r,parts:n,hash:i}=q(e),o=n.map(e=>{const r=e.indexOf("=");return-1===r||B(e)[0]!==t?e:`${e.slice(0,r)}=…`});return o.some((e,t)=>e!==n[t])?`${r}?${o.join("&")}${i}`:e}function V(e,t){return q(e).parts.some(e=>{const[r,n]=B(e);return r===t&&""!==n})}function D(e){const t={};return e.forEach((e,r)=>{t[r.toLowerCase()]=e}),t}async function W(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 H(e){try{return new URL(e).href}catch{return`${e.replace(/\/+$/u,"")}/`}}async function J(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:D(a.headers),body:await W(a,r)}}}(),r=void 0===e.previewQueryParams||0===e.previewQueryParams.length?N:e.previewQueryParams,n=e.tokenQueryParam??G,i=await t(function(e,t,r){const{base:n,parts:i,hash:o}=q(e),a=/* @__PURE__ */new Set([...t,r]),s=i.filter(e=>!a.has(B(e)[0]));return s.length===i.length?e:`${n}${0===s.length?"":`?${s.join("&")}`}${o}`}(e.url,r,n),{headers:{Accept:"text/html","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate"}}),o={Accept:"text/html","Sec-Fetch-Dest":"iframe","Sec-Fetch-Mode":"navigate",...void 0===e.adminOrigin?{}:{Referer:H(e.adminOrigin)}},a=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,o),s=await t(function(e,t){const{base:r,parts:n,hash:i}=q(e);if(n.some(e=>{const[r,n]=B(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:{...a,...o}});return j({publicResponse:i,previewResponse:s},{url:M(e.url,n),adminOrigin:e.adminOrigin,...!0===e.v2?{v2:!0}:{},...Object.keys(a).length>0||V(e.url,n)?{credentials:!0}:{}})}export{e as DIAGNOSTIC_CODES,j as analyzeProbe,R as analyzeV2Readiness,z as formatReport,D as lowercaseHeaders,J as runDoctor};
1
+ var e=Object.freeze({NoTrustedOrigin:"LP0101",ReferrerOnlyTrust:"LP0102",PluginIncompatible:"LP0103",ProfileFeatureOmitted:"LP0104",OrphanField:"LP0201",UnattributableUpdate:"LP0202",UnboundField:"LP0203",CoverageDefaultChanges:"LP0204",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",EscalationUnavailable:"LP0808",IslandStartUnavailable:"LP0809",RouteRefreshUnconfirmed:"LP0810",V2ReadinessGap:"LP0709",RuntimeOnPublicPage:"LP0710",AuditModifierWithoutField:"LP0711"}),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","softNavigationEvents","subfieldCoverage","mergeTimeoutMs","internalLinkPattern"],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="__LIVE_PREVIEW_CONFIG__",p=`var ${h}=`,f=/<!--[\s\S]*?-->|<script\b((?:[^>"']|"[^"]*"|'[^']*')*)>([\s\S]*?)<\/script\b[^>]*>/giu,g=/(?:^|\s)[iI][dD]\s*=\s*["']?__LIVE_PREVIEW_CONFIG__(?=["'\s]|$)/u,m=/(?:^|\s)type\s*=\s*["']?application\/json(?=["'\s;]|$)/iu,v=[{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"}],y={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."},w={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 or JSON configuration element.",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."},b={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 P(e){if(!e.includes(h))return;const t=function(e){const t=[];for(const[,r,n]of e.matchAll(f))void 0!==r&&void 0!==n&&t.push({attributes:r,text:n});return t}(e),r=t.find(({text:e})=>e.includes(p)),n=void 0===r?void 0:function(e){const t=e.indexOf(p);if(-1===t)return;const r=t+p.length;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}}(r.text);if(void 0!==n)return{source:"inline",value:e=>n[l.indexOf(e)]??void 0};const i=function(e){for(const{attributes:t,text:r}of e)if(g.test(t)){if(!m.test(t))return;try{const e=JSON.parse(r);return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}catch{return}}}(t);return void 0!==i?{source:"element",value:e=>Object.hasOwn(i,e)?i[e]??void 0:void 0}:void 0}function L(e){return"string"==typeof e?`'${e}'`:JSON.stringify(e)}function $(e,t,r){const n=`\`${t.key}: ${L(c[t.key])}\``;switch(e){case"profile":return`Set ${n} or drop \`defaults: 'v1'\` once ${t.condition}. 3.0 removes the profile; \`pll migrate\` writes its rows out, so each can go on its own.`;case"explicit":return`Drop the explicit \`${t.key}: ${L(r)}\` once ${t.condition}; 2.0 defaults to ${n}.`;case"unmarked":return`Upgrading to 2.0 sets ${n}; check first that ${t.condition}.`}}function k(e,t,r){return{code:"LP0709",level:"warning",title:e.title,detail:`${e.key} is ${L(t)}: ${e.effect} ${y[r]}`,remedy:$(r,e,t)}}function S(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 R(e){const t=P(e.previewResponse.body);if(void 0===t)return[w];const r=t.value("defaults");if(void 0!==r&&"v1"!==r&&"v2"!==r)return[S(r)];const n=r??("element"===t.source?"v2":void 0),i="v2"===n?c:u,o=v.flatMap(e=>{const r=t.value(e.key),o=r??i[e.key];return o===c[e.key]?[]:[k(e,o,"v1"===n?"profile":void 0===r?"unmarked":"explicit")]});return void 0===n?[...o,b]:o}var O="__LIVE_PREVIEW_CONFIG__",T=/<[a-zA-Z](?:"[^"]*"|'[^']*'|[^"'<>])*>/gu,x=/([^\s"'>/=]+)(\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/gu,A={error:0,warning:1,info:2},U=/<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>|<style\b[^>]*>[\s\S]*?<\/style\b[^>]*>|<!--[\s\S]*?-->/giu;function C(e){let t=e;for(;;){const e=t.replace(U,"");if(e===t)return t;t=e}}var F=new Set(["type","attribute","href","src","alt","richtext","html","text","array","array-template","array-separator","structural","locale","format","boundary"].map(e=>`data-payload-${e}`));function I(e){const t=[];for(const r of e.matchAll(T))for(const[,e="",n,i,o,a]of r[0].matchAll(x))void 0!==n&&t.push({name:e.toLowerCase(),value:i??o??a??"",at:r.index});return t}function E(e,t){return e.filter(e=>e.name===t)}function j(e){if(void 0!==e)try{return new URL(e)}catch{return}}function _(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(O),l=[];if(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=j(r.url),o=j(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&&void 0===l&&(/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. A browser applies it to a response that has no frame-ancestors directive, and this response has no frame-ancestors.",remedy:"Remove X-Frame-Options for preview responses, or serve a frame-ancestors directive that admits the admin: the header is then ignored. 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. A browser applies it to a response that has no frame-ancestors directive, and this response has no frame-ancestors. 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 or serve a frame-ancestors directive that admits it; a proxy or a security middleware usually sets the header."})}return n}(i,a,r)),l.push(...function(e,t,r,n){const i=[],o=I(e),a=E(o,"data-payload-field").length,s=E(I(t),"data-payload-field").length,d=E(o,"data-payload-owner").length,l=E(o,"data-payload-fragment").filter(e=>e.value.length>0).length;s>0&&i.push({code:"LP0704",level:"warning",title:`${String(s)} 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."});const c=function(e){const t=new Set(E(e,"data-payload-field").map(e=>e.at)),r=/* @__PURE__ */new Map;for(const{name:n,at:i}of e)!F.has(n)||t.has(i)||r.has(i)||r.set(i,n);return[...r.values()]}(o);c.length>0&&i.push({code:"LP0711",level:"warning",title:`${String(c.length)} element(s) carry ${c[0]??""} or the like without a field`,detail:"The runtime reads these attributes only on a bound element, so they do nothing here, and it says nothing about them either.",remedy:"Add data-payload-field to the element, or remove them."}),void 0!==n.threshold&&a>n.threshold&&i.push({code:"LP0705",level:"warning",title:`${String(a)} bindings exceed the ${n.configured?"configured":"default"} visibility gate`,detail:`Above visibilityGateThreshold (${String(n.threshold)}) 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, or set disableVisibilityGate, on the adapter, LivePreviewClient or generateInlineScript() if the whole page must stay live, or confirm that deferring below the fold is acceptable here."}),r&&0===a&&0===l&&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 and not one data-payload-fragment boundary for a server to render, so no edit can ever be visible on this page.",remedy:"Add binding attributes or fragment boundaries to the markup. pll-codegen --inventory lists every field the schema makes addressable."});const u=function(e){const t=E(e,"data-payload-owner")[0];return void 0===t?0:E(e,"data-payload-field").filter(e=>e.at<t.at).length}(o);return d>0&&u>0&&i.push({code:"LP0706",level:"warning",title:`${String(u)} binding(s) outside every owner marker`,detail:`The page declares ${String(d)} 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}(C(i.body),C(n.body),a,function(e){const t=P(e);if(!0===t?.value("disableVisibilityGate"))return{threshold:void 0,configured:!0};const r=t?.value("visibilityGateThreshold");return"number"==typeof r?{threshold:r,configured:!0}:{threshold:50,configured:!1}}(i.body))),n.body.includes(O)){const t=n.body.includes(`var ${O}=`);l.push({code:e.RuntimeOnPublicPage,level:"info",title:t?"Runtime is also served to anonymous visitors":"Preview configuration is also served to anonymous visitors",detail:t?"A request with no preview intent received the inline runtime. That is correct for inject: 'always' and unintended for 'preview-only'.":"A request with no preview intent received the configuration element a page writes for a client it starts itself. That is correct if the page starts the client for every request, and unintended if it writes the element only for a preview.",remedy:""})}return!0===r.v2&&l.push(...R(t)),l.sort((e,t)=>A[e.level]-A[t.level]),{url:r.url,findings:l,errors:l.filter(e=>"error"===e.level).length,warnings:l.filter(e=>"warning"===e.level).length}}var z={error:"ERROR",warning:"WARN ",info:"INFO "};function N(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(`${z[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 M=["preview","draft","livePreview"];function q(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 B(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 G="previewToken";function D(e,t=G){const{base:r,parts:n,hash:i}=q(e),o=n.map(e=>{const r=e.indexOf("=");return-1===r||B(e)[0]!==t?e:`${e.slice(0,r)}=…`});return o.some((e,t)=>e!==n[t])?`${r}?${o.join("&")}${i}`:e}function V(e,t){return q(e).parts.some(e=>{const[r,n]=B(e);return r===t&&""!==n})}function W(e){const t={};return e.forEach((e,r)=>{t[r.toLowerCase()]=e}),t}async function H(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 J(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:W(a.headers),body:await H(a,r)}}}(),r=void 0===e.previewQueryParams||0===e.previewQueryParams.length?M:e.previewQueryParams,n=e.tokenQueryParam??G,i=await t(function(e,t,r){const{base:n,parts:i,hash:o}=q(e),a=/* @__PURE__ */new Set([...t,r]),s=i.filter(e=>!a.has(B(e)[0]));return s.length===i.length?e:`${n}${0===s.length?"":`?${s.join("&")}`}${o}`}(e.url,r,n),{headers:{Accept:"text/html","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate"}}),o={Accept:"text/html","Sec-Fetch-Dest":"iframe","Sec-Fetch-Mode":"navigate",...void 0===e.adminOrigin?{}:{Referer:J(e.adminOrigin)}},a=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,o),s=await t(function(e,t){const{base:r,parts:n,hash:i}=q(e);if(n.some(e=>{const[r,n]=B(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:{...a,...o}});return _({publicResponse:i,previewResponse:s},{url:D(e.url,n),adminOrigin:e.adminOrigin,...!0===e.v2?{v2:!0}:{},...Object.keys(a).length>0||V(e.url,n)?{credentials:!0}:{}})}export{e as DIAGNOSTIC_CODES,_ as analyzeProbe,R as analyzeV2Readiness,N as formatReport,W as lowercaseHeaders,Q as runDoctor};
2
2
  //# sourceMappingURL=doctor.js.map