payload-live-preview 1.8.0 → 2.0.0-beta.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 (98) hide show
  1. package/README.md +301 -93
  2. package/dist/adapters/astro/PreviewBoundary.astro +66 -0
  3. package/dist/adapters/astro/RichText.astro +6 -0
  4. package/dist/adapters/astro/index.d.ts +165 -134
  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 +4 -9
  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 +13 -80
  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 +46 -106
  14. package/dist/adapters/nuxt/index.js +1 -1
  15. package/dist/adapters/nuxt/index.js.map +1 -1
  16. package/dist/adapters/sveltekit/index.d.ts +10 -75
  17. package/dist/adapters/sveltekit/index.js +1 -1
  18. package/dist/adapters/sveltekit/index.js.map +1 -1
  19. package/dist/client.cjs +2 -0
  20. package/dist/client.cjs.map +1 -0
  21. package/dist/client.d.cts +747 -0
  22. package/dist/client.d.ts +747 -0
  23. package/dist/client.js +2 -0
  24. package/dist/client.js.map +1 -0
  25. package/dist/codegen-astro.d.ts +5 -27
  26. package/dist/codegen-astro.js +1 -1
  27. package/dist/codegen-astro.js.map +1 -1
  28. package/dist/codegen-cli.d.ts +3 -16
  29. package/dist/codegen-cli.js +1 -1
  30. package/dist/codegen-cli.js.map +1 -1
  31. package/dist/codegen.cjs +1 -1
  32. package/dist/codegen.cjs.map +1 -1
  33. package/dist/codegen.d.cts +37 -124
  34. package/dist/codegen.d.ts +37 -124
  35. package/dist/codegen.js +1 -1
  36. package/dist/codegen.js.map +1 -1
  37. package/dist/core.cjs +1 -1
  38. package/dist/core.cjs.map +1 -1
  39. package/dist/core.d.cts +647 -1100
  40. package/dist/core.d.ts +647 -1100
  41. package/dist/core.js +1 -1
  42. package/dist/core.js.map +1 -1
  43. package/dist/doctor-cli.d.ts +3 -28
  44. package/dist/doctor-cli.js +1 -1
  45. package/dist/doctor-cli.js.map +1 -1
  46. package/dist/doctor.d.ts +16 -32
  47. package/dist/doctor.js +1 -1
  48. package/dist/doctor.js.map +1 -1
  49. package/dist/fragment.cjs +2 -0
  50. package/dist/fragment.cjs.map +1 -0
  51. package/dist/fragment.d.cts +333 -0
  52. package/dist/fragment.d.ts +333 -0
  53. package/dist/fragment.js +2 -0
  54. package/dist/fragment.js.map +1 -0
  55. package/dist/index.cjs +1 -1
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.d.cts +885 -1531
  58. package/dist/index.d.ts +885 -1531
  59. package/dist/index.js +1 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/lexical.cjs +2 -0
  62. package/dist/lexical.cjs.map +1 -0
  63. package/dist/lexical.d.cts +90 -0
  64. package/dist/lexical.d.ts +90 -0
  65. package/dist/lexical.js +2 -0
  66. package/dist/lexical.js.map +1 -0
  67. package/dist/middleware-CEPsY1P7.d.ts +40 -0
  68. package/dist/migrate.d.ts +73 -0
  69. package/dist/migrate.js +2 -0
  70. package/dist/migrate.js.map +1 -0
  71. package/dist/options-CEzzfHJu.d.ts +197 -0
  72. package/dist/payload.cjs +1 -1
  73. package/dist/payload.cjs.map +1 -1
  74. package/dist/payload.d.cts +11 -80
  75. package/dist/payload.d.ts +11 -80
  76. package/dist/payload.js +1 -1
  77. package/dist/payload.js.map +1 -1
  78. package/dist/plugins.cjs +2 -0
  79. package/dist/plugins.cjs.map +1 -0
  80. package/dist/plugins.d.cts +509 -0
  81. package/dist/plugins.d.ts +509 -0
  82. package/dist/plugins.js +2 -0
  83. package/dist/plugins.js.map +1 -0
  84. package/dist/{probe-tMHdim5y.d.ts → probe-CYtvj_Lm.d.ts} +43 -77
  85. package/dist/server.cjs +2 -0
  86. package/dist/server.cjs.map +1 -0
  87. package/dist/server.d.cts +435 -0
  88. package/dist/server.d.ts +435 -0
  89. package/dist/server.js +2 -0
  90. package/dist/server.js.map +1 -0
  91. package/dist/structural.cjs +3 -0
  92. package/dist/structural.cjs.map +1 -0
  93. package/dist/structural.d.cts +243 -0
  94. package/dist/structural.d.ts +243 -0
  95. package/dist/structural.js +3 -0
  96. package/dist/structural.js.map +1 -0
  97. package/package.json +86 -7
  98. package/dist/middleware-DD4Hwomt.d.ts +0 -189
@@ -1,34 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { D as DoctorFetch } from './probe-tMHdim5y.js';
2
+ import { D as DoctorFetch } from './probe-CYtvj_Lm.js';
3
3
 
4
- /**
5
- * `pll` CLI — currently one subcommand, `doctor`.
6
- *
7
- * npx pll doctor https://example.com/some-page --admin https://cms.example.com
8
- *
9
- * Exit codes:
10
- * 0 — no error-level findings (warnings are reported and do not fail)
11
- * 1 — usage error, or the URL could not be fetched
12
- * 2 — at least one error-level finding
13
- *
14
- * `pll-codegen` stays its own binary; it predates this one and consumers have
15
- * it in their scripts.
16
- *
17
- * @module @doctor/cli
18
- */
19
-
20
- /**
21
- * @param fetchImpl Seam for tests, so the argument and output paths can be
22
- * exercised without a server. The bin shim never passes it.
23
- */
4
+ /** @param fetchImpl Seam for tests; the bin shim never passes it. */
24
5
  declare function run(argv: readonly string[], fetchImpl?: DoctorFetch): Promise<number>;
25
- /**
26
- * Whether this module was run as a program rather than imported.
27
- *
28
- * Matches the entry's basename, not the whole path: `includes('pll')` against
29
- * the full path would auto-run the CLI on import for any project whose
30
- * directory happens to contain those three letters.
31
- */
6
+ /** Matches the entry's basename only, so importing this module from a path containing `pll` never runs it. */
32
7
  declare function isCliInvocation(argv?: readonly (string | undefined)[]): boolean;
33
8
 
34
9
  export { isCliInvocation, run };
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- var e=Object.defineProperty,t=(t,n)=>e(t,"name",{value:n,configurable:!0}),n="__LIVE_PREVIEW_CONFIG__",r=/\bdata-payload-field\s*=/gu,i=/\bdata-payload-owner\s*=/gu,o={error:0,warning:1,info:2};function s(e,t){return e.match(t)?.length??0}function a(e){let t=e.search(/\bdata-payload-owner\s*=/u);return-1===t?0:s(e.slice(0,t),r)}function d(e,t){if(void 0===t)return!1;try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function l(e){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, a redirect target, or empty.",remedy:"Check the URL. A redirect to a login usually means the route needs authentication that a plain probe cannot supply."};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."};let 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 about the deployment.",remedy:"Point the audit at a page route rather than an API or an asset."}}function c(e){if(void 0!==e)for(let t of e.split(";")){let e=t.trim();if(e.toLowerCase().startsWith("frame-ancestors"))return e}}function u(e,t){let u=[],{publicResponse:h,previewResponse:p}=e,m=d(t.url,t.adminOrigin),g=l(p);if(void 0!==g)return{url:t.url,findings:[g],errors:1,warnings:0};let f=p.body.includes(n),v=h.body.includes(n),y=s(p.body,r),w=s(h.body,r),b=s(p.body,i);f||u.push({code:"LP0701",level:"warning",title:"No inline runtime in the preview response",detail:`A request carrying Sec-Fetch-Dest: iframe returned ${String(p.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."});let O=c(p.headers["content-security-policy"]);f&&void 0===O?u.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."}):void 0!==O&&void 0!==t.adminOrigin&&!O.includes(t.adminOrigin)&&(!m||!O.includes("'self'"))&&u.push({code:"LP0702",level:"error",title:"frame-ancestors does not admit the admin origin",detail:`Served: "${O}". Expected it to allow ${t.adminOrigin}.`,remedy:"Add the admin origin to allowedOrigins so the adapter merges it into frame-ancestors, or add it to your own policy."});let S=p.headers["x-frame-options"];void 0!==S&&(/deny/iu.test(S)||/sameorigin/iu.test(S)&&!m)&&u.push({code:"LP0703",level:"error",title:`X-Frame-Options: ${S} 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."}),w>0&&u.push({code:"LP0704",level:"warning",title:`${String(w)} 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."}),y>50&&u.push({code:"LP0705",level:"warning",title:`${String(y)} 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."}),f&&0===y&&u.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."});let L=a(p.body);return b>0&&L>0&&u.push({code:"LP0706",level:"warning",title:`${String(L)} binding(s) outside every owner marker`,detail:`The page declares ${String(b)} 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."}),v&&u.push({code:"LP0701",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:""}),u.sort((e,t)=>o[e.level]-o[t.level]),{url:t.url,findings:u,errors:u.filter(e=>"error"===e.level).length,warnings:u.filter(e=>"warning"===e.level).length}}function h(e){let t={};return e.forEach((e,n)=>{t[n.toLowerCase()]=e}),t}t(s,"count"),t(a,"unownedBindingsBeforeFirstOwner"),t(d,"isSameOrigin"),t(l,"describeNonPage"),t(c,"frameAncestorsOf"),t(u,"analyzeProbe"),t(h,"lowercaseHeaders");var p=t(async(e,t)=>{let n=await fetch(e,{headers:t.headers,redirect:"follow"});return{status:n.status,headers:h(n.headers),body:await n.text()}},"defaultFetch");async function m(e){let t=e.fetchImpl??p;return u({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:`${e.adminOrigin}/`}:{}}})},{url:e.url,adminOrigin:e.adminOrigin})}t(m,"runDoctor");var g={error:"ERROR",warning:"WARN ",info:"INFO "};function f(e){let 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(let n of e.findings)t.push(`${g[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 v(e){let t={url:void 0,adminOrigin:void 0,json:!1,showHelp:!1,unknown:[]};for(let n=0;n<e.length;n+=1){let r=e[n];if(void 0!==r){if("-h"===r||"--help"===r){t.showHelp=!0;continue}if("--json"===r){t.json=!0;continue}if("--admin"===r||"-a"===r){t.adminOrigin=e[n+1],n+=1;continue}if(r.startsWith("--admin=")){t.adminOrigin=r.slice(8);continue}if(r.startsWith("-")){t.unknown.push(r);continue}t.url??=r}}return t}t(f,"formatReport"),t(v,"parseArgs");var y="pll doctor — audit what a live-preview deployment actually serves\n\nUsage:\n pll doctor <url> [--admin <origin>] [--json]\n\nThe URL is fetched twice: once as an ordinary visitor and once with the\nheaders the Payload admin's iframe sends. Most findings come from the\ndifference between the two responses.\n\nOptions:\n -a, --admin <origin> Admin origin the preview is embedded from. Enables the\n frame-ancestors check to verify the origin is admitted,\n not merely that a policy exists.\n --json Emit the report as JSON instead of text\n -h, --help Show this help\n\nExit codes:\n 0 no error-level findings\n 1 usage error, or the URL could not be fetched\n 2 at least one error-level finding\n\nExamples:\n pll doctor https://example.com/\n pll doctor https://example.com/blog/hello --admin https://cms.example.com\n";async function w(e,t){let[n,...r]=e;if(void 0===n||"-h"===n||"--help"===n)return process.stdout.write(y),void 0===n?1:0;if("doctor"!==n)return process.stderr.write(`pll: unknown command "${n}". Try \`pll --help\`.\n`),1;let i,o=v(r);if(o.showHelp)return process.stdout.write(y),0;if(o.unknown.length>0)return process.stderr.write(`pll doctor: unknown option ${o.unknown.join(", ")}\n`),1;if(void 0===o.url)return process.stderr.write("pll doctor: a URL is required. Try `pll doctor --help`.\n"),1;try{i=await m({url:o.url,...void 0!==o.adminOrigin?{adminOrigin:o.adminOrigin}:{},...void 0!==t?{fetchImpl:t}:{}})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`pll doctor: could not probe ${o.url}: ${t}\n`),1}return process.stdout.write(o.json?`${JSON.stringify(i,void 0,2)}\n`:f(i)),i.errors>0?2:0}function b(e=process.argv){if(typeof process>"u")return!1;let t=e[1];if(void 0===t||""===t)return!1;let n=t.split(/[\\/]/u).pop()??"";return"pll"===n||"pll.cmd"===n||n.startsWith("doctor-cli")}t(w,"run"),t(b,"isCliInvocation"),b()&&w(process.argv.slice(2)).then(e=>{process.exit(e)});export{b as isCliInvocation,w as run};
2
+ import{writeFile as e,readFile as t,stat as n,readdir as r}from"fs/promises";import{relative as i,join as o,extname as s}from"path";import{createRequire as a}from"module";var d,l,c="payload-live-preview";function u(){if(void 0===d)try{d=a(import.meta.url)("ts-morph")}catch(e){throw new Error("pll migrate needs ts-morph: npm install --save-dev ts-morph",{cause:e})}return d}function h(e,t){const n=u();return l??=new n.Project({useInMemoryFileSystem:!0,skipLoadingLibFiles:!0,skipFileDependencyResolution:!0,compilerOptions:{allowJs:!0,noLib:!0}}),l.createSourceFile(`/script.${t}`,e,{overwrite:!0})}function p(e){return e===c||e.startsWith(`${c}/`)}function f(e){const{Node:t,SyntaxKind:n}=u(),r=[];for(const t of e.getImportDeclarations()){const e=t.getModuleSpecifierValue();if(p(e))for(const n of t.getNamedImports()){const i=n.getAliasNode(),o=i??n.getNameNode();r.push({imported:n.getName(),local:o.getText(),specifier:e,kind:"import",importedNode:n.getNameNode(),aliased:void 0!==i,symbol:o.getSymbol(),declaration:t})}}for(const i of e.getDescendantsOfKind(n.VariableDeclaration)){const e=g(i.getInitializer()),n=i.getNameNode();if(void 0!==e&&p(e)&&t.isObjectBindingPattern(n))for(const o of n.getElements()){const n=o.getNameNode();if(!t.isIdentifier(n))continue;const s=o.getPropertyNameNode();r.push({imported:void 0===s?n.getText():m(s),local:n.getText(),specifier:e,kind:"require",importedNode:s??n,aliased:void 0!==s,symbol:n.getSymbol(),declaration:i})}}return r}function g(e){const{Node:t,SyntaxKind:n}=u(),r=void 0!==e&&t.isAwaitExpression(e)?e.getExpression():e;if(void 0===r||!t.isCallExpression(r))return;const i=r.getExpression();if((!t.isIdentifier(i)||"require"!==i.getText())&&i.getKind()!==n.ImportKeyword)return;const[o]=r.getArguments();return void 0!==o&&t.isStringLiteral(o)?o.getLiteralText():void 0}function m(e){const{Node:t}=u();return t.isStringLiteral(e)||t.isNoSubstitutionTemplateLiteral(e)?e.getLiteralText():e.getText()}function w(e){const{Node:t}=u();if(t.isSpreadAssignment(e))return;const n=e.getNameNode();return t.isComputedPropertyName(n)?void 0:m(n)}function v(e,t){const{Node:n,SyntaxKind:r}=u(),i=t.symbol?.compilerSymbol;if(void 0===i)return[];const o=e.getProject().getTypeChecker(),s=[];for(const a of e.getDescendantsOfKind(r.Identifier)){if(a.getText()!==t.local||y(a,t.declaration))continue;const e=a.getParent();n.isShorthandPropertyAssignment(e)?o.getShorthandAssignmentValueSymbol(e)?.compilerSymbol===i&&s.push({node:a,kind:"shorthand"}):n.isExportSpecifier(e)?e.getNameNode()===a&&e.getLocalTargetSymbol()?.compilerSymbol===i&&s.push({node:a,kind:"export"}):a.getSymbol()?.compilerSymbol===i&&s.push({node:a,kind:"reference"})}return s}function y(e,t){return void 0!==e.getFirstAncestor(e=>e===t)}function b(e,t,n=[]){const{SyntaxKind:r}=u();for(const i of e.getDescendantsOfKind(r.Identifier)){if(i.getText()!==t)continue;const e=i.getParent();if(P(e,i)&&!n.some(t=>t===e||y(e,t)))return e}}function P(e,t){const{Node:n}=u();return n.isVariableDeclaration(e)||n.isBindingElement(e)||n.isFunctionDeclaration(e)||n.isClassDeclaration(e)||n.isParameterDeclaration(e)||n.isEnumDeclaration(e)?e.getNameNode()===t:n.isImportSpecifier(e)?(e.getAliasNode()??e.getNameNode())===t:n.isImportClause(e)?e.getDefaultImport()===t:!!n.isNamespaceImport(e)&&e.getNameNode()===t}function S(e,t){return{start:e.getStart(),end:e.getEnd(),text:t}}function $(e,t){const n=[...t].sort((e,t)=>t.start-e.start);let r=e,i=Number.POSITIVE_INFINITY;for(const e of n){if(e.end>i)throw new Error("overlapping codemod edits");r=`${r.slice(0,e.start)}${e.text}${r.slice(e.end)}`,i=e.start}return r}var x="move-fetch-preview-helpers",L=`${c}/server`,N={fetchPreviewDocument:"fetchDocument",fetchPreviewGlobal:"fetchGlobal"},T=/* @__PURE__ */new Set(["serverURL","apiRoute","depth"]),O=/* @__PURE__ */new Set(["draft","fetchFn"]);function R(e,t){return{codemod:x,reason:t,line:e}}function k(e,t,n){const r=[],i=[],o=[],s=/* @__PURE__ */new Set;for(const e of t.getProperties()){const t=w(e);void 0!==t&&s.add(t),void 0!==t&&T.has(t)?r.push(e.getText()):void 0!==t&&O.has(t)?o.push(t):i.push(e.getText())}s.has("depth")||r.push("depth: 1 /* TODO(pll migrate): the 1.x default; match the runtime mergeDepth */"),s.has("authorization")||i.push("authorization: null /* TODO(pll migrate): pass the verdict from authorizePreviewRequest(); null reads the published document */");const a=e.getTypeArguments().map(e=>e.getText()),d=0===a.length?"":`<${a.join(", ")}>`,l=N[n]??n,c=`definePreview({ ${r.join(", ")} }).${l}${d}({ ${i.join(", ")} })`,u=0===o.length?"":`, and ${o.join("/")} was dropped`;return{edit:{start:e.getStart(),end:e.getEnd(),text:c},note:R(e.getStartLineNumber(),`${n}() was rewritten onto definePreview().${l}(); it now returns a PreviewFetchResult (check ok/data) and needs a real authorization for drafts${u}`)}}function E(e,t,n){return`import { ${t.join(", ")} } from ${e}${n}${e};`}var A={id:x,summary:"`fetchPreviewDocument`/`fetchPreviewGlobal` from the root → `definePreview().fetchDocument`/`.fetchGlobal` (payload-live-preview/server)",ledgerEntry:9,apply(e){const t=f(e).filter(e=>e.specifier===c&&e.imported in N);return 0===t.length?{edits:[],conflicts:[]}:function(e,t){const{Node:n}=u(),r=[],i=[],o=[];for(const s of t)if("require"!==s.kind)for(const t of v(e,s)){const e=t.node.getStartLineNumber(),a=t.node.getParent();if("reference"!==t.kind||!n.isCallExpression(a)||a.getExpression()!==t.node){r.push(R(e,`${s.local} is used other than as a direct call; rewrite it onto definePreview() by hand`));continue}const[d]=a.getArguments();if(void 0===d||!n.isObjectLiteralExpression(d)||d.getProperties().some(e=>n.isSpreadAssignment(e))){r.push(R(e,`${s.imported}() options are not a plain object literal, so serverURL/depth cannot be split out; rewrite it onto definePreview() by hand`));continue}const l=k(a,d,s.imported);o.push(l.edit),i.push(l.note)}else r.push(R(s.declaration.getStartLineNumber(),`${s.imported} is bound with require(); rewrite it onto definePreview() from ${L} by hand`));const s=e.getImportDeclarations().filter(e=>e.getModuleSpecifierValue()===L),a=b(e,"definePreview",s);return void 0!==a&&r.push(R(a.getStartLineNumber(),`this module already binds a definePreview that is not ${L}'s; rename it, then move the fetch helpers by hand`)),r.length>0?{edits:[],conflicts:[...r,...i]}:(o.push(...function(e,t){const{Node:n}=u(),r=[],i=/* @__PURE__ */new Set;for(const e of t)n.isImportDeclaration(e.declaration)&&i.add(e.declaration);const o=e.getImportDeclarations().find(e=>e.getModuleSpecifierValue()===L&&!e.isTypeOnly()),s=!0===o?.getNamedImports().some(e=>"definePreview"===e.getName());let a;if(void 0!==o&&!s){const e=o.getModuleSpecifier().getQuoteKind(),t=[...o.getNamedImports().map(e=>e.getText()),"definePreview"],n=o.getDefaultImport()?.getText(),i=void 0===n?t.join(", "):`${n}, { ${t.join(", ")} }`,s=void 0===n?E(e,t,L):`import ${i} from ${e}${L}${e};`;r.push({start:o.getStart(),end:o.getEnd(),text:s})}for(const t of i){const n=t.getModuleSpecifier().getQuoteKind(),i=t.getNamedImports().filter(e=>!(e.getName()in N)).map(e=>e.getText()),s=t.getDefaultImport()?.getText(),d=[s,0===i.length?void 0:`{ ${i.join(", ")} }`].filter(e=>void 0!==e),l=t.isTypeOnly()?"type ":"",u=0===d.length?"":`import ${l}${d.join(", ")} from ${n}${c}${n};`;if(void 0===o&&void 0===a)a=E(n,["definePreview"],L),r.push({start:t.getStart(),end:t.getEnd(),text:""===u?a:`${u}\n${a}`});else{const n=t.getEnd(),i=""===u&&"\n"===e.getFullText()[n]?1:0;r.push({start:t.getStart(),end:n+i,text:u})}}return r}(e,t)),{edits:o,conflicts:i})}(e,t)}},I="rename-bindings-authorized-option",j="authorized",D="authorization",F=`if they carry \`${j}\`, rename it to \`${D}\` and pass the verdict from authorizePreviewRequest()`,C={id:I,summary:"`createPreviewBindings({ authorized })` → `{ authorization }`",ledgerEntry:7,apply(e){const{Node:t}=u(),n=[],r=[],i=f(e).filter(e=>"createPreviewBindings"===e.imported);for(const o of i)for(const i of v(e,o)){const e=i.node.getParent();if("reference"!==i.kind||!t.isCallExpression(e))continue;if(e.getExpression()!==i.node)continue;const[o]=e.getArguments();if(void 0===o)continue;const s=o.getStartLineNumber();if(t.isObjectLiteralExpression(o))for(const e of o.getProperties())if(t.isSpreadAssignment(e))r.push({codemod:I,line:e.getStartLineNumber(),reason:`createPreviewBindings() spreads its options; ${F}`});else{if(w(e)!==j)continue;t.isShorthandPropertyAssignment(e)?n.push(S(e,`${D}: ${j}`)):t.isPropertyAssignment(e)&&n.push(S(e.getNameNode(),D))}else r.push({codemod:I,line:s,reason:`createPreviewBindings() options are not a literal; ${F}`})}return{edits:n,conflicts:r}}},U="rename-is-preview-request",z="isPreviewRequest",q="hasPreviewIntent";function M(e,t){return{codemod:U,reason:t,line:e}}function _(e){return e.aliased&&e.local===q?S(e.importedNode.getParentOrThrow(),q):S(e.importedNode,q)}var B={id:U,summary:"`isPreviewRequest()` → `hasPreviewIntent()` (same signature)",ledgerEntry:1,apply(e){const t=f(e).filter(e=>e.imported===z);return 0===t.length?{edits:[],conflicts:[]}:function(e,t){const n=[],r=[],i=b(t,q,e.map(e=>e.declaration));void 0!==i&&n.push(M(i.getStartLineNumber(),`this module already binds ${q}; rename it (or drop the wrapper and import the package's ${q} directly), then rename ${z} by hand`));for(const i of e)if(r.push(_(i)),!i.aliased)for(const e of v(t,i))"reference"===e.kind?r.push(S(e.node,q)):"shorthand"===e.kind?n.push(M(e.node.getStartLineNumber(),`{ ${z} } keeps the old key for whoever reads it; write { ${z}: ${q} } or rename the key and its readers by hand`)):n.push(M(e.node.getStartLineNumber(),`${z} is re-exported under its old name; rename the export and its importers, or write export { ${q} as ${z} }`));const o=function(e,t){const{Node:n,SyntaxKind:r}=u();return e.getDescendantsOfKind(r.Identifier).filter(e=>{const r=e.getParent();return e.getText()===t&&n.isPropertyAccessExpression(r)&&r.getNameNode()===e})}(t,z).map(e=>M(e.getStartLineNumber(),`.${z} is a member of this module's own object, not the package import, and was left alone; check that it does not wrap the removed API`));return n.length>0?{edits:[],conflicts:[...n,...o]}:{edits:r,conflicts:o}}(t,e)}},W={".ts":"ts",".mts":"ts",".cts":"ts",".tsx":"tsx",".js":"js",".mjs":"js",".cjs":"js",".jsx":"jsx"},K=/* @__PURE__ */new Set([".astro",".vue",".svelte"]),V=/^---\r?\n([\s\S]*?)\r?\n---/u,G=/<script(?:\s[^>]*)?>([\s\S]*?)<\/script\b[^>]*>/giu,H=/(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)['"]payload-live-preview(?:\/[^'"]*)?['"]/u,J=[B,C,A];function Q(e,t){let n=1;for(let r=0;r<t;r+=1)"\n"===e[r]&&(n+=1);return n}function X(e,t,n){const r=e.split("\n"),i=t.split("\n");let o=0;for(;o<r.length&&o<i.length&&r[o]===i[o];)o+=1;let s=0;for(;s<r.length-o&&s<i.length-o&&r[r.length-1-s]===i[i.length-1-s];)s+=1;const a=r.slice(o,r.length-s),d=i.slice(o,i.length-s),l=[];for(let e=0;e<Math.max(a.length,d.length);e+=1){const t=a[e]??"",r=d[e]??"";t!==r&&l.push({line:n+o+e,before:t,after:r})}return l}function Y(e,t={}){const n=void 0===t.only?J:J.filter(e=>t.only?.includes(e.id));if(0===n.length||!function(e){return H.test(e)}(e))return{output:e,edits:[],conflicts:[]};const r=/* @__PURE__ */new Map,i=[];let o="",a=0;for(const d of function(e,t){const n=s(t).toLowerCase();if(!K.has(n))return[{start:0,end:e.length,kind:W[n]??"ts"}];const r=[],i=".astro"===n?V.exec(e):null;if(void 0!==i?.[1]){const e=i.index+i[0].indexOf("\n")+1;r.push({start:e,end:e+i[1].length,kind:"ts"})}for(const t of e.matchAll(G)){const e=t[1]??"";if(""===e.trim())continue;const n=t.index+t[0].lastIndexOf(e);r.push({start:n,end:n+e.length,kind:"ts"})}return r}(e,t.fileName??"source.ts")){o+=e.slice(a,d.start);const t=Q(e,d.start);let s=e.slice(d.start,d.end);for(const e of n){const n=e.apply(h(s,d.kind));for(const e of n.conflicts)i.push(void 0===e.line?e:{...e,line:e.line+t-1});if(0===n.edits.length)continue;const o=$(s,n.edits),a=r.get(e.id)??[];a.push(...X(s,o,t)),r.set(e.id,a),s=o}o+=s,a=d.end}return o+=e.slice(a),{output:o,edits:[...r].map(([e,t])=>({codemod:e,count:t.length,lines:t})),conflicts:i}}var Z=/* @__PURE__ */new Set([".ts",".tsx",".mts",".cts",".js",".jsx",".mjs",".cjs",".astro",".svelte",".vue"]),ee=/\.d\.[cm]?ts$/u,te=/* @__PURE__ */new Set(["node_modules","dist",".git",".astro",".svelte-kit",".next",".nuxt","coverage"]);function ne(e){return Z.has(s(e))&&!ee.test(e)}async function re(e){const t=[];return(await n(e)).isDirectory()?await async function e(n){for(const i of await r(n,{withFileTypes:!0})){const r=o(n,i.name);i.isDirectory()?te.has(i.name)||await e(r):ne(i.name)&&t.push(r)}}(e):ne(e)&&t.push(e),t.sort()}function ie(e,t){const n=i(e,t);return 0===n.length?t:n}function oe(e){return e.edits.map(e=>`${e.codemod}: ${String(e.count)} line(s)`).join(", ")}var se={error:"ERROR",warning:"WARN ",info:"INFO "},ae=Object.freeze({NoTrustedOrigin:"LP0101",ReferrerOnlyTrust:"LP0102",PluginIncompatible:"LP0103",OrphanField:"LP0201",UnattributableUpdate:"LP0202",VisibilityGateDeferred:"LP0301",UnsafeAttributeWrite:"LP0401",TextTargetHasChildren:"LP0402",MissingArrayTemplate:"LP0403",StructuralItemUnkeyed:"LP0404",StructuralDuplicateKey:"LP0405",StructuralUnstableKeys:"LP0406",UnsupportedStrategy:"LP0407",MessageRejected:"LP0501",TokenRejected:"LP0502",HandlerThrew:"LP0601",TransformThrew:"LP0602",RendererThrew:"LP0603",StartupFailed:"LP0605",ReadyFailed:"LP0606",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",V2ReadinessGap:"LP0709",RuntimeOnPublicPage:"LP0710"}),de={http:"80",https:"443",ws:"80",wss:"443"},le=/^([a-z][a-z0-9+.-]*):$/u,ce=/^(?:([a-z][a-z0-9+.-]*):\/\/)?(\*|(?:\*\.)?[a-z0-9-]+(?:\.[a-z0-9-]+)*\.?)(?::(\*|\d+))?(?:\/.*)?$/u;function ue(e){return e.protocol.slice(0,-1).toLowerCase()}function he(e){return""===e.port?de[ue(e)]??"":e.port}function pe(e,t){return e===t||"http"===e&&"https"===t||"ws"===e&&"wss"===t}function fe(e,t){if(e.hostname!==t.hostname)return!1;const n=ue(e),r=ue(t);return(n===r||"http"===r&&"https"===n)&&(he(e)===he(t)||""===e.port&&""===t.port)}function ge(e,t,n){const r=le.exec(e);if(void 0!==r?.[1])return pe(r[1],ue(t));const i=ce.exec(e);if(null===i)return!1;const o=i[1],s=i[2]??"",a=i[3];return!!pe(o??ue(n),ue(t))&&!!function(e,t){if("*"===e)return!0;const n=e.replace(/\.$/u,""),r=t.replace(/\.$/u,"");return n.startsWith("*.")?r.endsWith(n.slice(1))&&r.length>n.length-1:n===r}(s,t.hostname)&&(void 0===a?""===t.port:"*"===a||a===he(t))}var me=Object.freeze({disableReferrerDetection:11,skipUnchanged:14,eventSourcePolicy:15,sanitizerPolicy:16});function we(e,t,n){return{code:"LP0709",level:"warning",title:e,detail:t,remedy:n}}var ve="__LIVE_PREVIEW_CONFIG__",ye=/\bdata-payload-field\s*=/gu,be=/\bdata-payload-owner\s*=/gu,Pe={error:0,warning:1,info:2},Se=/<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>|<style\b[^>]*>[\s\S]*?<\/style\b[^>]*>|<!--[\s\S]*?-->/giu;function $e(e){let t=e;for(;;){const e=t.replace(Se,"");if(e===t)return t;t=e}}function xe(e,t){return e.match(t)?.length??0}function Le(e){if(void 0!==e)try{return new URL(e)}catch{return}}function Ne(e,t){const{publicResponse:n,previewResponse:r}=e,i=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!==i)return{url:t.url,findings:[i],errors:1,warnings:0};const o=r.body.includes(ve),s=[];return o||s.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."}),s.push(...function(e,t,n){const r=[],i=Le(n.url),o=Le(n.adminOrigin),s=void 0!==o&&i?.origin===o.origin,a=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===a)r.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!==a&&void 0!==n.adminOrigin){const e=void 0!==o&&void 0!==i&&function(e,t,n){const r=e.split(/\s+/u).slice(1).filter(e=>e.length>0).map(e=>e.toLowerCase());if(0===r.length||1===r.length&&"'none'"===r[0])return!1;for(const e of r)if("'self'"===e){if(fe(t,n))return!0}else{if("*"===e)return!0;if(!e.startsWith("'")&&ge(e,t,n))return!0}return!1}(a,o,i);e||r.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: "${a}". 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 d=e.headers["x-frame-options"];return void 0!==d&&(/deny/iu.test(d)||/sameorigin/iu.test(d)&&!s)&&r.push({code:"LP0703",level:"error",title:`X-Frame-Options: ${d} 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."}),r}(r,o,t)),s.push(...function(e,t,n){const r=[],i=xe(e,ye),o=xe(t,ye),s=xe(e,be);o>0&&r.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&&r.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."}),n&&0===i&&r.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 a=function(e){const t=e.search(/\bdata-payload-owner\s*=/u);return-1===t?0:xe(e.slice(0,t),ye)}(e);return s>0&&a>0&&r.push({code:"LP0706",level:"warning",title:`${String(a)} binding(s) outside every owner marker`,detail:`The page declares ${String(s)} 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."}),r}($e(r.body),$e(n.body),o)),n.body.includes(ve)&&s.push({code:ae.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===t.v2&&s.push(...function(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 r=0,i=!1,o="",s="";for(let t=n;t<e.length;t+=1){const n=e[t]??"";if(i)s+=n,"\\"===n?(s+=e[t+1]??"",t+=1):'"'===n&&(i=!1,o='"');else if('"'!==n){if(!/\s/u.test(n)&&((","===n&&(","===o||"["===o)||"]"===n&&","===o)&&(s+="null"),s+=n,o=n,"["===n&&(r+=1),"]"===n&&(r-=1,0===r)))break}else i=!0,s+=n}if(0===r)try{const e=JSON.parse(s);return Array.isArray(e)?e:void 0}catch{return}}(e.previewResponse.body);if(void 0===t)return[{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)."}];const n=[];return!0!==t[me.disableReferrerDetection]&&n.push(we("Referrer trust is still on","The runtime accepts the admin referer as a preview signal.","Set `defaults: 'v2'` (or `disableReferrerDetection: true`) so referrer trust is off outside local dev.")),"parent-or-opener"!==t[me.eventSourcePolicy]&&n.push(we("Messages are accepted from any window",`eventSourcePolicy is ${JSON.stringify(t[me.eventSourcePolicy]??"any")}.`,"Set `defaults: 'v2'` (or `eventSourcePolicy: 'parent-or-opener'`).")),"strict"!==t[me.sanitizerPolicy]&&n.push(we("Sanitizer is in compat mode",`sanitizerPolicy is ${JSON.stringify(t[me.sanitizerPolicy]??"compat")}; id and every data-* pass.`,"Set `defaults: 'v2'` (or `sanitizerPolicy: 'strict'`) once rich text no longer relies on id/data-*.")),!0!==t[me.skipUnchanged]&&n.push(we("Unchanged bindings are re-applied every message","skipUnchanged is off.","Set `defaults: 'v2'` (or `skipUnchanged: true`) to skip bindings whose value did not change.")),n}(e)),s.sort((e,t)=>Pe[e.level]-Pe[t.level]),{url:t.url,findings:s,errors:s.filter(e=>"error"===e.level).length,warnings:s.filter(e=>"warning"===e.level).length}}function Te(e){const t={};return e.forEach((e,n)=>{t[n.toLowerCase()]=e}),t}async function Oe(e,t){const n=e.body?.getReader();if(void 0===n)return e.text();const r=new TextDecoder;let i=0,o="";for(;;){const{done:e,value:s}=await n.read();if(e)break;if(i+=s.byteLength,i>t)throw await n.cancel(),new Error(`the response body exceeds ${String(t)} bytes, which is not a page the audit can judge`);o+=r.decode(s,{stream:!0})}return o+r.decode()}function Re(e){try{return new URL(e).href}catch{return`${e.replace(/\/+$/u,"")}/`}}var ke="pll doctor — audit what a live-preview deployment actually serves\n\nUsage:\n pll doctor <url> [--admin <origin>] [--json] [--v2]\n pll migrate <path> [--write] [--only <id,id>]\n\nThe URL is fetched twice: once as an ordinary visitor and once with the\nheaders the Payload admin's iframe sends. Most findings come from the\ndifference between the two responses. Redirects are reported, not followed.\n\nOptions:\n -a, --admin <origin> Admin origin the preview is embedded from. Enables the\n frame-ancestors check to verify the origin is admitted,\n not merely that a policy exists.\n --json Emit the report as JSON instead of text\n --v2 Also check the page against the 2.0 readiness table\n -h, --help Show this help\n\nExit codes:\n 0 no error-level findings\n 1 usage error, or the URL could not be fetched\n 2 at least one error-level finding\n\nExamples:\n pll doctor https://example.com/\n pll doctor https://example.com/blog/hello --admin https://cms.example.com\n";async function Ee(n,r){const[i,...o]=n;if(void 0===i||"-h"===i||"--help"===i)return process.stdout.write(ke),void 0===i?1:0;if("migrate"===i)return async function(n){const r=function(e){let t,n,r,i=!1,o=!1;const s=e=>e.split(",").filter(e=>e.length>0);for(let a=0;a<e.length;a+=1){const d=e[a];void 0!==d&&("-h"===d||"--help"===d?o=!0:"--write"===d?i=!0:"--only"===d?(n=s(e[a+1]??""),a+=1):d.startsWith("--only=")?n=s(d.slice(7)):d.startsWith("-")?r??=d:t??=d)}return{target:t,write:i,only:n,help:o,unknown:r}}(n);if(r.help)return process.stdout.write("pll migrate — rewrite 1.x APIs to their 2.0 names and homes (ADR 0007)\n\nUsage:\n pll migrate <path> [--write] [--only <id,id>]\n\nWithout --write the run only reports what it would change. Codemods touch\nonly names bound from payload-live-preview; anything they cannot rewrite\nsafely is listed as needing manual attention. Requires ts-morph.\n\nOptions:\n --write Apply the changes (otherwise dry-run)\n --only <ids> Run only these codemods (comma-separated)\n -h, --help Show this help\n\nExit codes:\n 0 nothing needs a human (changes applied, or would be with --write)\n 1 usage error, unreadable path, or ts-morph is not installed\n 3 at least one file needs manual attention\n"),0;if(void 0!==r.unknown)return process.stderr.write(`pll migrate: unknown option ${r.unknown}\n`),1;if(void 0===r.target)return process.stderr.write("pll migrate: a path is required. Try `pll migrate --help`.\n"),1;let i;try{i=await async function(n,r={}){const i=r.io??{read:e=>t(e,"utf8"),write:(t,n)=>e(t,n,"utf8"),list:re},o=[],s={};for(const e of await i.list(n)){const t=await i.read(e),{output:a,edits:d,conflicts:l}=Y(t,{fileName:e,...void 0===r.only?{}:{only:r.only}}),c=a!==t;if(c&&!0===r.write&&await i.write(e,a),c)for(const e of d)s[e.codemod]=(s[e.codemod]??0)+1;o.push({file:ie(n,e),edits:d,changed:c,conflicts:l})}return{files:o,changedCount:o.filter(e=>e.changed).length,byCodemod:s,conflictCount:o.filter(e=>e.conflicts.length>0).length}}(r.target,{write:r.write,...void 0===r.only?{}:{only:r.only}})}catch(e){return process.stderr.write(`pll migrate: ${e instanceof Error?e.message:String(e)}\n`),1}for(const e of i.files)e.changed&&process.stdout.write(`${r.write?"migrated":"would migrate"} ${e.file} (${oe(e)})\n`);process.stdout.write(`\n${r.write?"Migrated":"Would migrate"} ${String(i.changedCount)} file(s).${r.write?"":" Re-run with --write to apply."}\n`);const o=i.files.filter(e=>e.conflicts.length>0);if(0===o.length)return 0;process.stdout.write(`\n${String(o.length)} file(s) need manual attention:\n`);for(const e of o)for(const t of e.conflicts){const n=void 0===t.line?e.file:`${e.file}:${String(t.line)}`;process.stdout.write(` ${n}: ${t.reason}\n`)}return 3}(o);if("doctor"!==i)return process.stderr.write(`pll: unknown command "${i}". Try \`pll --help\`.\n`),1;const s=function(e){const t={url:void 0,adminOrigin:void 0,json:!1,v2:!1,showHelp:!1,unknown:[]};for(let n=0;n<e.length;n+=1){const r=e[n];void 0!==r&&("-h"===r||"--help"===r?t.showHelp=!0:"--json"===r?t.json=!0:"--v2"===r?t.v2=!0:"--admin"===r||"-a"===r?(t.adminOrigin=e[n+1],n+=1):r.startsWith("--admin=")?t.adminOrigin=r.slice(8):r.startsWith("-")?t.unknown.push(r):t.url??=r)}return t}(o);if(s.showHelp)return process.stdout.write(ke),0;if(s.unknown.length>0)return process.stderr.write(`pll doctor: unknown option ${s.unknown.join(", ")}\n`),1;if(void 0===s.url)return process.stderr.write("pll doctor: a URL is required. Try `pll doctor --help`.\n"),1;if(void 0!==s.adminOrigin&&!function(e){try{return"null"!==new URL(e).origin}catch{return!1}}(s.adminOrigin))return process.stderr.write(`pll doctor: --admin must be an absolute URL such as https://cms.example.com (got "${s.adminOrigin}")\n`),1;let a;try{a=await async function(e){const t=e.fetchImpl??function(e={}){const t=e.timeoutMs??15e3,n=e.maxBodyBytes??10485760;return async(r,i)=>{const o=e.fetchFn??globalThis.fetch;let s;try{s=await o(r,{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:s.status,headers:Te(s.headers),body:await Oe(s,n)}}}();return Ne({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:Re(e.adminOrigin)}}})},{url:e.url,adminOrigin:e.adminOrigin,...!0===e.v2?{v2:!0}:{}})}({url:s.url,...void 0!==s.adminOrigin?{adminOrigin:s.adminOrigin}:{},...s.v2?{v2:!0}:{},...void 0!==r?{fetchImpl:r}:{}})}catch(e){const t=function(e){if(!(e instanceof Error))return String(e);const t=e.cause;return t instanceof Error&&""!==t.message&&!e.message.includes(t.message)?`${e.message} (${t.message})`:e.message}(e);return s.json?process.stdout.write(`${JSON.stringify({url:s.url,error:t},void 0,2)}\n`):process.stderr.write(`pll doctor: could not probe ${s.url}: ${t}\n`),1}return process.stdout.write(s.json?`${JSON.stringify(a,void 0,2)}\n`:function(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(`${se[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")}(a)),a.errors>0?2:0}function Ae(e=process.argv){if("undefined"==typeof process)return!1;const t=e[1];if(void 0===t||""===t)return!1;const n=t.split(/[\\/]/u).pop()??"";return"pll"===n||"pll.cmd"===n||n.startsWith("doctor-cli")}Ae()&&Ee(process.argv.slice(2)).then(e=>{process.exit(e)});export{Ae as isCliInvocation,Ee as run};
3
3
  //# sourceMappingURL=doctor-cli.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor-cli.js","names":["S","Object","defineProperty","o","n","e","value","configurable","RUNTIME_MARKER","BINDING_ATTRIBUTE","OWNER_ATTRIBUTE","LEVEL_ORDER","error","warning","info","count","haystack","pattern","match","length","unownedBindingsBeforeFirstOwner","body","firstOwner","search","slice","isSameOrigin","pageUrl","adminOrigin","URL","origin","describeNonPage","preview","status","code","level","title","String","detail","remedy","trim","contentType","headers","toLowerCase","includes","frameAncestorsOf","csp","directive","split","trimmed","startsWith","analyzeProbe","probe","context","findings","publicResponse","pub","previewResponse","sameOrigin","url","notAPage","errors","warnings","runtimeInPreview","runtimeInPublic","previewBindings","publicBindings","owners","push","frameAncestors","frameOptions","test","unowned","sort","a","b","filter","f","lowercaseHeaders","out","forEach","key","__name","defaultFetch","async","init","response","fetch","redirect","text","runDoctor","options","fetchImpl","Accept","Referer","LEVEL_LABEL","formatReport","report","lines","join","finding","parseArgs","argv","parsed","json","showHelp","unknown","i","token","HELP_TEXT","run","subcommand","rest","process","stdout","write","stderr","args","message","Error","JSON","stringify","isCliInvocation","entry","name","pop","then","exit","H","C"],"sources":["../src/doctor/analyze.ts","../src/doctor/probe.ts","../src/doctor/cli.ts"],"sourcesContent":["/**\n * The `pll doctor` audit: what a deployment actually serves.\n *\n * Every check here reads a response, not a configuration file. That is the\n * whole point. A config can say `allowedOrigins: [...]` while a proxy strips\n * the header, an adapter runs in a mode nobody remembers choosing, or a build\n * emits binding attributes on public pages. The gap between what a project\n * believes it is configured to do and what it puts on the wire is where this\n * project's most expensive findings have lived.\n *\n * The audit compares two fetches of the same URL: one as an ordinary visitor,\n * one shaped the way the admin's iframe requests it. Most of what matters is\n * in the difference between them.\n *\n * HTML is inspected with regular expressions rather than a parser. This is a\n * deliberate limit: the audit counts attributes and looks for the runtime\n * marker, which regexes do reliably, and it never needs a tree. A malformed\n * page can therefore be miscounted — findings say what was observed so the\n * reader can check.\n *\n * Nothing here is sent anywhere. The audit makes exactly the two requests it\n * is asked to make, to the URL it is given.\n *\n * @module @doctor/analyze\n */\nimport type {\n DoctorContext,\n DoctorFinding,\n DoctorProbe,\n DoctorReport,\n DoctorResponse,\n} from './types';\n\n/**\n * Marker the inline runtime always carries.\n *\n * The generator emits `var __LIVE_PREVIEW_CONFIG__=...` ahead of the runtime\n * body, and `generateInlineScript()` keeps that identifier verbatim so the\n * bundle can read it. The banner comment is not usable here — minification\n * strips it.\n */\nconst RUNTIME_MARKER = '__LIVE_PREVIEW_CONFIG__';\n\n/** Any binding attribute from the public vocabulary. */\nconst BINDING_ATTRIBUTE = /\\bdata-payload-field\\s*=/gu;\n\n/** Owner markers, which only exist once a consumer has adopted owner scoping. */\nconst OWNER_ATTRIBUTE = /\\bdata-payload-owner\\s*=/gu;\n\n/** The runtime's own default, mirrored here so the audit can warn before it bites. */\nconst DEFAULT_VISIBILITY_GATE_THRESHOLD = 50;\n\nconst LEVEL_ORDER = { error: 0, warning: 1, info: 2 } as const;\n\nfunction count(haystack: string, pattern: RegExp): number {\n return haystack.match(pattern)?.length ?? 0;\n}\n\n/**\n * Bindings that sit outside every element carrying an owner marker.\n *\n * Approximated by segmenting on owner markers: everything before the first\n * one is certainly unowned. Nesting and sibling scopes are not modelled, so\n * this under-reports rather than over-reports — a finding here is real, an\n * absence is not proof.\n */\nfunction unownedBindingsBeforeFirstOwner(body: string): number {\n const firstOwner = body.search(/\\bdata-payload-owner\\s*=/u);\n if (firstOwner === -1) return 0;\n return count(body.slice(0, firstOwner), BINDING_ATTRIBUTE);\n}\n\n/**\n * Whether the admin is served from the same origin as the page being probed.\n *\n * This changes three verdicts, because a same-origin admin is admitted by\n * `'self'` and by `X-Frame-Options: SAMEORIGIN` without either naming it.\n * Getting this wrong turns a correct deployment into three red findings —\n * which is exactly what the first run against a real same-origin consumer did.\n */\nfunction isSameOrigin(pageUrl: string, adminOrigin: string | undefined): boolean {\n if (adminOrigin === undefined) return false;\n try {\n return new URL(pageUrl).origin === new URL(adminOrigin).origin;\n } catch {\n return false;\n }\n}\n\n/**\n * Why this response cannot be audited as a page, or `undefined` if it can.\n *\n * Deliberately only the preview probe: the public response being a redirect or\n * a 404 is a legitimate shape for a gated site, and judging it would be the\n * same mistake in the other direction.\n */\nfunction describeNonPage(preview: DoctorResponse): DoctorFinding | undefined {\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 ' +\n 'body is an error page, a redirect target, or empty.',\n remedy:\n 'Check the URL. A redirect to a login usually means the route needs ' +\n 'authentication that a plain probe cannot supply.',\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 — ' +\n 'is not a page, 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 ' +\n 'and no bindings by definition, so reporting their absence would say ' +\n 'nothing about the deployment.',\n remedy: 'Point the audit at a page route rather than an API or an asset.',\n };\n }\n return undefined;\n}\n\nfunction 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 (trimmed.toLowerCase().startsWith('frame-ancestors')) return trimmed;\n }\n return undefined;\n}\n\n/**\n * Audit a probed deployment.\n *\n * Pure: the caller performs the two fetches and hands the results in.\n */\nexport function analyzeProbe(probe: DoctorProbe, context: DoctorContext): DoctorReport {\n const findings: DoctorFinding[] = [];\n const { publicResponse: pub, previewResponse: preview } = probe;\n const sameOrigin = isSameOrigin(context.url, context.adminOrigin);\n\n // Everything below reads a rendered page. A 404, a redirect to a login, a\n // 500 or a JSON endpoint would each be diagnosed as \"no inline runtime,\n // perhaps you start the client yourself\" — a plausible sentence about the\n // wrong problem, which is worse than saying nothing. Establish that there is\n // a page before judging its contents.\n const notAPage = describeNonPage(preview);\n if (notAPage !== undefined) {\n return {\n url: context.url,\n findings: [notAPage],\n errors: 1,\n warnings: 0,\n };\n }\n\n const runtimeInPreview = preview.body.includes(RUNTIME_MARKER);\n const runtimeInPublic = pub.body.includes(RUNTIME_MARKER);\n const previewBindings = count(preview.body, BINDING_ATTRIBUTE);\n const publicBindings = count(pub.body, BINDING_ATTRIBUTE);\n const owners = count(preview.body, OWNER_ATTRIBUTE);\n\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)} ` +\n 'without the inline runtime. Two readings, and this audit cannot tell them ' +\n 'apart from the response alone: an adapter that did not recognise the ' +\n 'request as a preview, or a consumer that starts LivePreviewClient itself ' +\n '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\n const previewCsp = preview.headers['content-security-policy'];\n const frameAncestors = frameAncestorsOf(previewCsp);\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 ' +\n 'directive, 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 ' +\n 'origin. Without it you are relying on the absence of a policy.',\n });\n } else if (\n frameAncestors !== undefined &&\n context.adminOrigin !== undefined &&\n !frameAncestors.includes(context.adminOrigin) &&\n // `'self'` names the admin without spelling it out when they share an origin.\n !(sameOrigin && frameAncestors.includes(\"'self'\"))\n ) {\n findings.push({\n code: 'LP0702',\n level: 'error',\n title: 'frame-ancestors does not admit the admin origin',\n detail: `Served: \"${frameAncestors}\". Expected it to allow ${context.adminOrigin}.`,\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 frameOptionsBlocks =\n frameOptions !== undefined &&\n (/deny/iu.test(frameOptions) || (/sameorigin/iu.test(frameOptions) && !sameOrigin));\n if (frameOptionsBlocks) {\n findings.push({\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. ' +\n 'No frame-ancestors directive can override it.',\n remedy:\n 'Remove X-Frame-Options for preview responses. It is usually set by a ' +\n 'proxy or a security middleware rather than by the app.',\n });\n }\n\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. ' +\n 'These disclose the CMS field taxonomy, and any CSS keyed on them changes ' +\n 'public layout 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\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)}) ` +\n 'the scheduler stops writing offscreen elements and buffers them until they ' +\n 'scroll into view. On a page nobody scrolls, that is never.',\n remedy:\n 'Raise visibilityGateThreshold if the whole page must stay live, or confirm ' +\n 'that deferring below the fold is acceptable here.',\n });\n }\n\n if (runtimeInPreview && 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 ' +\n 'for it to 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 ' +\n 'field the schema makes addressable.',\n });\n }\n\n const unowned = unownedBindingsBeforeFirstOwner(preview.body);\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 ` +\n 'use. Bindings that belong to no owner receive nothing once ' +\n 'scopeBindingsByOwner is on — they fail closed.',\n remedy:\n 'Give every binding an owning ancestor, or leave scopeBindingsByOwner off ' +\n 'until they all have one. Counted before the first owner marker only, so ' +\n 'the real number may be higher.',\n });\n }\n\n if (runtimeInPublic) {\n findings.push({\n code: 'LP0701',\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 ' +\n \"correct for inject: 'always' and unintended for 'preview-only'.\",\n remedy: '',\n });\n }\n\n findings.sort((a, b) => LEVEL_ORDER[a.level] - LEVEL_ORDER[b.level]);\n\n return {\n url: context.url,\n findings,\n errors: findings.filter((f) => f.level === 'error').length,\n warnings: findings.filter((f) => f.level === 'warning').length,\n };\n}\n","/**\n * Fetching and rendering for `pll doctor`.\n *\n * Deliberately not the barrel: index files are excluded from the coverage\n * report, and this file holds the only code in the audit that touches the\n * network and the only place response headers are normalised — precisely the\n * parts whose silent failure would make every header check report a false\n * finding. Living here, they are measured like everything else.\n *\n * @module @doctor/probe\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 `globalThis.fetch`. */\n readonly fetchImpl?: DoctorFetch | undefined;\n}\n\n/**\n * Normalise header names to lowercase.\n *\n * Exported because every header check downstream reads a lowercase key: if\n * this were wrong, `content-security-policy` and `x-frame-options` would both\n * read as absent and the audit would invent findings rather than report them.\n * A silent failure of exactly that shape is worth a test of its own.\n */\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\nconst defaultFetch: DoctorFetch = async (url, init) => {\n const response = await fetch(url, { headers: init.headers, redirect: 'follow' });\n return {\n status: response.status,\n headers: lowercaseHeaders(response.headers),\n body: await response.text(),\n };\n};\n\n/**\n * Fetch the URL twice — as a visitor and as the admin's iframe — and audit the\n * difference.\n */\nexport async function runDoctor(options: RunDoctorOptions): Promise<DoctorReport> {\n const fetchImpl = options.fetchImpl ?? defaultFetch;\n\n const publicResponse = await fetchImpl(options.url, {\n headers: {\n // A plain top-level navigation. Deliberately no referer: an admin\n // referer is itself a preview signal, which would defeat the comparison.\n Accept: 'text/html',\n 'Sec-Fetch-Dest': 'document',\n 'Sec-Fetch-Mode': 'navigate',\n },\n });\n\n const previewResponse = await fetchImpl(options.url, {\n headers: {\n Accept: 'text/html',\n // What the admin's iframe sends. This is the signal `'preview-only'`\n // injection keys on, so it is the honest way to ask for the preview\n // variant of the page.\n 'Sec-Fetch-Dest': 'iframe',\n 'Sec-Fetch-Mode': 'navigate',\n ...(options.adminOrigin !== undefined ? { Referer: `${options.adminOrigin}/` } : {}),\n },\n });\n\n return analyzeProbe(\n { publicResponse, previewResponse },\n { url: options.url, adminOrigin: options.adminOrigin },\n );\n}\n\nconst LEVEL_LABEL = { error: 'ERROR', warning: 'WARN ', info: 'INFO ' } as const;\n\n/** Render a report for a terminal. */\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","#!/usr/bin/env node\n/**\n * `pll` CLI — currently one subcommand, `doctor`.\n *\n * npx pll doctor https://example.com/some-page --admin https://cms.example.com\n *\n * Exit codes:\n * 0 — no error-level findings (warnings are reported and do not fail)\n * 1 — usage error, or the URL could not be fetched\n * 2 — at least one error-level finding\n *\n * `pll-codegen` stays its own binary; it predates this one and consumers have\n * it in their scripts.\n *\n * @module @doctor/cli\n */\nimport { formatReport, runDoctor, type DoctorFetch } from './index';\n\ninterface ParsedArgs {\n url: string | undefined;\n adminOrigin: string | undefined;\n json: boolean;\n showHelp: boolean;\n unknown: string[];\n}\n\nfunction parseArgs(argv: readonly string[]): ParsedArgs {\n const parsed: ParsedArgs = {\n url: undefined,\n adminOrigin: undefined,\n json: false,\n showHelp: false,\n unknown: [],\n };\n for (let i = 0; i < argv.length; i += 1) {\n const token = argv[i];\n if (token === undefined) continue;\n if (token === '-h' || token === '--help') {\n parsed.showHelp = true;\n continue;\n }\n if (token === '--json') {\n parsed.json = true;\n continue;\n }\n if (token === '--admin' || token === '-a') {\n parsed.adminOrigin = argv[i + 1];\n i += 1;\n continue;\n }\n if (token.startsWith('--admin=')) {\n parsed.adminOrigin = token.slice('--admin='.length);\n continue;\n }\n if (token.startsWith('-')) {\n parsed.unknown.push(token);\n continue;\n }\n parsed.url ??= token;\n }\n return parsed;\n}\n\nconst HELP_TEXT = `pll doctor — audit what a live-preview deployment actually serves\n\nUsage:\n pll doctor <url> [--admin <origin>] [--json]\n\nThe URL is fetched twice: once as an ordinary visitor and once with the\nheaders the Payload admin's iframe sends. Most findings come from the\ndifference between the two responses.\n\nOptions:\n -a, --admin <origin> Admin origin the preview is embedded from. Enables the\n frame-ancestors check to verify the origin is admitted,\n not merely that a policy exists.\n --json Emit the report as JSON instead of text\n -h, --help Show this help\n\nExit codes:\n 0 no error-level findings\n 1 usage error, or the URL could not be fetched\n 2 at least one error-level finding\n\nExamples:\n pll doctor https://example.com/\n pll doctor https://example.com/blog/hello --admin https://cms.example.com\n`;\n\n/**\n * @param fetchImpl Seam for tests, so the argument and output paths can be\n * exercised without a server. The bin shim never passes it.\n */\nexport async function run(argv: readonly string[], fetchImpl?: DoctorFetch): Promise<number> {\n const [subcommand, ...rest] = argv;\n\n if (subcommand === undefined || subcommand === '-h' || subcommand === '--help') {\n process.stdout.write(HELP_TEXT);\n return subcommand === undefined ? 1 : 0;\n }\n if (subcommand !== 'doctor') {\n process.stderr.write(`pll: unknown command \"${subcommand}\". Try \\`pll --help\\`.\\n`);\n return 1;\n }\n\n const args = parseArgs(rest);\n if (args.showHelp) {\n process.stdout.write(HELP_TEXT);\n return 0;\n }\n if (args.unknown.length > 0) {\n process.stderr.write(`pll doctor: unknown option ${args.unknown.join(', ')}\\n`);\n return 1;\n }\n if (args.url === undefined) {\n process.stderr.write('pll doctor: a URL is required. Try `pll doctor --help`.\\n');\n return 1;\n }\n\n let report;\n try {\n report = await runDoctor({\n url: args.url,\n ...(args.adminOrigin !== undefined ? { adminOrigin: args.adminOrigin } : {}),\n ...(fetchImpl !== undefined ? { fetchImpl } : {}),\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`pll doctor: could not probe ${args.url}: ${message}\\n`);\n return 1;\n }\n\n process.stdout.write(\n args.json ? `${JSON.stringify(report, undefined, 2)}\\n` : formatReport(report),\n );\n return report.errors > 0 ? 2 : 0;\n}\n\n/**\n * Whether this module was run as a program rather than imported.\n *\n * Matches the entry's basename, not the whole path: `includes('pll')` against\n * the full path would auto-run the CLI on import for any project whose\n * directory happens to contain those three letters.\n */\nexport function isCliInvocation(argv: readonly (string | undefined)[] = process.argv): boolean {\n if (typeof process === 'undefined') return false;\n const entry = argv[1];\n if (entry === undefined || entry === '') return false;\n const name = entry.split(/[\\\\/]/u).pop() ?? '';\n return name === 'pll' || name === 'pll.cmd' || name.startsWith('doctor-cli');\n}\n\nif (isCliInvocation()) {\n // Direct invocation via the bin shim — run and exit with the code.\n void run(process.argv.slice(2)).then((code) => {\n process.exit(code);\n });\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"doctor-cli.js","names":["tsMorph","project","PACKAGE_NAME","loadTsMorph","createRequire","url","error","Error","cause","parseScript","text","kind","morph","Project","useInMemoryFileSystem","skipLoadingLibFiles","skipFileDependencyResolution","compilerOptions","allowJs","noLib","createSourceFile","overwrite","isPackageSpecifier","specifier","startsWith","packageBindings","script","Node","SyntaxKind","out","declaration","getImportDeclarations","getModuleSpecifierValue","spec","getNamedImports","alias","getAliasNode","localNode","getNameNode","push","imported","getName","local","getText","importedNode","aliased","symbol","getSymbol","getDescendantsOfKind","VariableDeclaration","requiredSpecifier","getInitializer","pattern","isObjectBindingPattern","element","getElements","nameNode","isIdentifier","propertyName","getPropertyNameNode","literalName","initializer","call","isAwaitExpression","getExpression","isCallExpression","callee","getKind","ImportKeyword","argument","getArguments","isStringLiteral","getLiteralText","node","isNoSubstitutionTemplateLiteral","propertyKey","property","isSpreadAssignment","isComputedPropertyName","referencesTo","binding","target","compilerSymbol","checker","getProject","getTypeChecker","Identifier","isInside","parent","getParent","isShorthandPropertyAssignment","getShorthandAssignmentValueSymbol","isExportSpecifier","getLocalTargetSymbol","ancestor","getFirstAncestor","candidate","declaresName","name","except","bindsIdentifier","some","skip","isVariableDeclaration","isBindingElement","isFunctionDeclaration","isClassDeclaration","isParameterDeclaration","isEnumDeclaration","isImportSpecifier","isImportClause","getDefaultImport","isNamespaceImport","replaceNode","start","getStart","end","getEnd","applyTextEdits","edits","ordered","sort","left","right","output","floor","Number","POSITIVE_INFINITY","edit","slice","ID","SERVER_SPECIFIER","METHOD","fetchPreviewDocument","fetchPreviewGlobal","SERVER_OPTIONS","Set","DROPPED_OPTIONS","conflict","line","reason","codemod","rewriteCall","options","server","read","dropped","keys","getProperties","key","add","has","typeArguments","getTypeArguments","map","generic","length","join","method","droppedNote","note","getStartLineNumber","importLine","quote","names","moveFetchPreviewHelpers","id","summary","ledgerEntry","apply","bindings","filter","conflicts","blocking","notes","reference","isObjectLiteralExpression","rewritten","serverDeclaration","foreign","declarations","isImportDeclaration","serverImport","find","isTypeOnly","hasDefinePreview","serverLine","getModuleSpecifier","getQuoteKind","defaultImport","clauses","remaining","clause","typeOnly","kept","trailingNewline","getFullText","rewriteImports","plan","OLD_KEY","NEW_KEY","BY_HAND","renameBindingsAuthorizedOption","isPropertyAssignment","OLD_NAME","NEW_NAME","renameBinding","getParentOrThrow","renameIsPreviewRequest","existing","isPropertyAccessExpression","memberAccesses","WHOLE_FILE","COMPONENT_EXTENSIONS","FRONTMATTER","SCRIPT_TAG","PACKAGE_REFERENCE","IMPLEMENTATIONS","lineNumberAt","source","offset","index","lineEdits","before","after","firstLine","oldLines","split","newLines","prefix","suffix","oldMiddle","newMiddle","changed","Math","max","migrateSource","chosen","only","includes","test","importsThisPackage","byCodemod","Map","cursor","block","fileName","extension","extname","toLowerCase","blocks","frontmatter","exec","indexOf","match","matchAll","body","trim","lastIndexOf","scriptBlocks","item","next","lines","get","set","count","SOURCE_EXTENSIONS","DECLARATION_FILE","SKIP_DIRECTORIES","isSourceFile","async","collectFiles","root","found","stat","isDirectory","walk","directory","entry","readdir","withFileTypes","path","relativeTo","file","rel","relative","describe","String","LEVEL_LABEL","warning","info","DIAGNOSTIC_CODES","Object","freeze","NoTrustedOrigin","ReferrerOnlyTrust","PluginIncompatible","OrphanField","UnattributableUpdate","VisibilityGateDeferred","UnsafeAttributeWrite","TextTargetHasChildren","MissingArrayTemplate","StructuralItemUnkeyed","StructuralDuplicateKey","StructuralUnstableKeys","UnsupportedStrategy","MessageRejected","TokenRejected","HandlerThrew","TransformThrew","RendererThrew","StartupFailed","ReadyFailed","AuditRuntimeMissing","AuditNoFrameAncestors","AuditFrameOptionsBlocks","AuditBindingsExposed","AuditGateThresholdExceeded","AuditUnownedBindings","AuditNoBindings","AuditNotAPage","FragmentRequestFailed","FragmentResponseInvalid","FragmentUnauthorized","FragmentSuperseded","RouteRefreshLoop","FragmentStrategyUnavailable","V2ReadinessGap","RuntimeOnPublicPage","DEFAULT_PORT","http","https","ws","wss","SCHEME_SOURCE","HOST_SOURCE","schemeOf","protocol","portOf","port","schemeMatches","expression","actual","isSelf","admin","page","hostname","adminScheme","pageScheme","sourceMatches","scheme","host","expressionScheme","expressionHost","expressionPort","wanted","replace","endsWith","hostMatches","RUNTIME_SLOT","disableReferrerDetection","skipUnchanged","eventSourcePolicy","sanitizerPolicy","gap","title","detail","remedy","code","level","RUNTIME_MARKER","BINDING_ATTRIBUTE","OWNER_ATTRIBUTE","LEVEL_ORDER","NON_MARKUP","visibleMarkup","markup","stripped","haystack","tryUrl","value","URL","analyzeProbe","probe","context","publicResponse","pub","previewResponse","preview","notAPage","status","location","headers","contentType","describeNonPage","findings","errors","warnings","runtimeInPreview","adminOrigin","sameOrigin","origin","frameAncestors","csp","directive","trimmed","frameAncestorsOf","admitted","sources","frameAncestorsAdmits","frameOptions","framingFindings","previewMarkup","publicMarkup","runtime","previewBindings","publicBindings","owners","unowned","firstOwner","search","unownedBindingsBeforeFirstOwner","bindingFindings","v2","config","from","CONFIG_MARKER","depth","inString","previous","json","char","JSON","parse","Array","isArray","readInlineConfig","stringify","analyzeV2Readiness","a","b","finding","lowercaseHeaders","forEach","readBody","response","limit","reader","getReader","decoder","TextDecoder","received","done","byteLength","cancel","decode","stream","previewReferer","href","HELP_TEXT","run","argv","fetchImpl","subcommand","rest","process","stdout","write","args","unknown","help","ids","token","parseArgs","stderr","result","io","readFile","content","writeFile","list","results","files","changedCount","conflictCount","runMigrate","message","conflicted","where","runMigrateCommand","parsed","showHelp","i","isAbsoluteUrl","report","timeoutMs","maxBodyBytes","init","fetchFn","globalThis","fetch","redirect","signal","AbortSignal","timeout","createDefaultFetch","Accept","Referer","runDoctor","describeFailure","formatReport","isCliInvocation","pop","then","exit"],"sources":["../src/migrate/ast.ts","../src/migrate/codemods/move-fetch-preview-helpers.ts","../src/migrate/codemods/rename-bindings-authorized-option.ts","../src/migrate/codemods/rename-is-preview-request.ts","../src/migrate/script-blocks.ts","../src/migrate/index.ts","../src/migrate/runner.ts","../src/migrate/cli.ts","../src/doctor/format.ts","../src/core/diagnostic-codes.ts","../src/doctor/csp.ts","../src/doctor/readiness.ts","../src/doctor/analyze.ts","../src/doctor/probe.ts","../src/doctor/cli.ts"],"sourcesContent":["/**\n * ts-morph access for the codemods: parsing, the package's bindings in a\n * script and the references those bindings have. ts-morph is loaded on first\n * use so the entry stays importable without the optional peer.\n */\nimport { createRequire } from 'node:module';\nimport type * as TsMorphModule from 'ts-morph';\nimport type {\n Identifier,\n Node,\n ObjectLiteralElementLike,\n Project,\n SourceFile,\n Symbol as TsSymbol,\n} from 'ts-morph';\nimport type { TextEdit } from './types';\n\ntype TsMorph = typeof TsMorphModule;\n\nexport type ScriptKind = 'ts' | 'tsx' | 'js' | 'jsx';\n\nexport const PACKAGE_NAME = 'payload-live-preview';\n\nlet tsMorph: TsMorph | undefined;\nlet project: Project | undefined;\n\nexport function loadTsMorph(): TsMorph {\n if (tsMorph === undefined) {\n try {\n tsMorph = createRequire(import.meta.url)('ts-morph') as TsMorph;\n } catch (error) {\n throw new Error('pll migrate needs ts-morph: npm install --save-dev ts-morph', {\n cause: error,\n });\n }\n }\n return tsMorph;\n}\n\n/** Parse one script into a throwaway in-memory project; the previous script is discarded. */\nexport function parseScript(text: string, kind: ScriptKind): SourceFile {\n const morph = loadTsMorph();\n project ??= new morph.Project({\n useInMemoryFileSystem: true,\n skipLoadingLibFiles: true,\n skipFileDependencyResolution: true,\n compilerOptions: { allowJs: true, noLib: true },\n });\n return project.createSourceFile(`/script.${kind}`, text, { overwrite: true });\n}\n\nexport function isPackageSpecifier(specifier: string): boolean {\n return specifier === PACKAGE_NAME || specifier.startsWith(`${PACKAGE_NAME}/`);\n}\n\n/** A name the script binds from the package, by `import` or by destructuring `require()`/`import()`. */\nexport interface PackageBinding {\n /** The exported name, as the package spells it. */\n readonly imported: string;\n /** The name the script uses; equals `imported` unless aliased. */\n readonly local: string;\n readonly specifier: string;\n readonly kind: 'import' | 'require';\n /** The node spelling the exported name; rewritten when the export is renamed. */\n readonly importedNode: Node;\n readonly aliased: boolean;\n readonly symbol: TsSymbol | undefined;\n /** The import declaration or variable declaration that binds it. */\n readonly declaration: Node;\n}\n\nexport function packageBindings(script: SourceFile): readonly PackageBinding[] {\n const { Node, SyntaxKind } = loadTsMorph();\n const out: PackageBinding[] = [];\n for (const declaration of script.getImportDeclarations()) {\n const specifier = declaration.getModuleSpecifierValue();\n if (!isPackageSpecifier(specifier)) continue;\n for (const spec of declaration.getNamedImports()) {\n const alias = spec.getAliasNode();\n const localNode = alias ?? spec.getNameNode();\n out.push({\n imported: spec.getName(),\n local: localNode.getText(),\n specifier,\n kind: 'import',\n importedNode: spec.getNameNode(),\n aliased: alias !== undefined,\n symbol: localNode.getSymbol(),\n declaration,\n });\n }\n }\n for (const declaration of script.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {\n const specifier = requiredSpecifier(declaration.getInitializer());\n const pattern = declaration.getNameNode();\n if (specifier === undefined || !isPackageSpecifier(specifier)) continue;\n if (!Node.isObjectBindingPattern(pattern)) continue;\n for (const element of pattern.getElements()) {\n const nameNode = element.getNameNode();\n if (!Node.isIdentifier(nameNode)) continue;\n const propertyName = element.getPropertyNameNode();\n out.push({\n imported: propertyName === undefined ? nameNode.getText() : literalName(propertyName),\n local: nameNode.getText(),\n specifier,\n kind: 'require',\n importedNode: propertyName ?? nameNode,\n aliased: propertyName !== undefined,\n symbol: nameNode.getSymbol(),\n declaration,\n });\n }\n }\n return out;\n}\n\nfunction requiredSpecifier(initializer: Node | undefined): string | undefined {\n const { Node, SyntaxKind } = loadTsMorph();\n const call =\n initializer !== undefined && Node.isAwaitExpression(initializer)\n ? initializer.getExpression()\n : initializer;\n if (call === undefined || !Node.isCallExpression(call)) return undefined;\n const callee = call.getExpression();\n const isRequire = Node.isIdentifier(callee) && callee.getText() === 'require';\n if (!isRequire && callee.getKind() !== SyntaxKind.ImportKeyword) return undefined;\n const [argument] = call.getArguments();\n return argument !== undefined && Node.isStringLiteral(argument)\n ? argument.getLiteralText()\n : undefined;\n}\n\nfunction literalName(node: Node): string {\n const { Node } = loadTsMorph();\n if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) {\n return node.getLiteralText();\n }\n return node.getText();\n}\n\n/** The key of an object-literal member, or `undefined` when it is computed. */\nexport function propertyKey(property: ObjectLiteralElementLike): string | undefined {\n const { Node } = loadTsMorph();\n if (Node.isSpreadAssignment(property)) return undefined;\n const nameNode = property.getNameNode();\n if (Node.isComputedPropertyName(nameNode)) return undefined;\n return literalName(nameNode);\n}\n\nexport type ReferenceKind = 'reference' | 'shorthand' | 'export';\n\nexport interface BindingReference {\n readonly node: Identifier;\n /** `shorthand` is `{ name }`, `export` is `export { name }`; both keep the old key visible. */\n readonly kind: ReferenceKind;\n}\n\n/** Every use of the binding outside its own declaration, resolved by symbol so shadowing is respected. */\nexport function referencesTo(\n script: SourceFile,\n binding: PackageBinding,\n): readonly BindingReference[] {\n const { Node, SyntaxKind } = loadTsMorph();\n const target = binding.symbol?.compilerSymbol;\n if (target === undefined) return [];\n const checker = script.getProject().getTypeChecker();\n const out: BindingReference[] = [];\n for (const node of script.getDescendantsOfKind(SyntaxKind.Identifier)) {\n if (node.getText() !== binding.local || isInside(node, binding.declaration)) continue;\n const parent = node.getParent();\n if (Node.isShorthandPropertyAssignment(parent)) {\n if (checker.getShorthandAssignmentValueSymbol(parent)?.compilerSymbol === target) {\n out.push({ node, kind: 'shorthand' });\n }\n continue;\n }\n if (Node.isExportSpecifier(parent)) {\n if (\n parent.getNameNode() === node &&\n parent.getLocalTargetSymbol()?.compilerSymbol === target\n ) {\n out.push({ node, kind: 'export' });\n }\n continue;\n }\n if (node.getSymbol()?.compilerSymbol === target) out.push({ node, kind: 'reference' });\n }\n return out;\n}\n\nfunction isInside(node: Node, ancestor: Node): boolean {\n return node.getFirstAncestor((candidate) => candidate === ancestor) !== undefined;\n}\n\n/** The declaration that binds `name` anywhere in the script, ignoring those in `except`. */\nexport function declaresName(\n script: SourceFile,\n name: string,\n except: readonly Node[] = [],\n): Node | undefined {\n const { SyntaxKind } = loadTsMorph();\n for (const node of script.getDescendantsOfKind(SyntaxKind.Identifier)) {\n if (node.getText() !== name) continue;\n const parent = node.getParent();\n if (!bindsIdentifier(parent, node)) continue;\n if (except.some((skip) => skip === parent || isInside(parent, skip))) continue;\n return parent;\n }\n return undefined;\n}\n\nfunction bindsIdentifier(parent: Node, node: Identifier): boolean {\n const { Node } = loadTsMorph();\n if (\n Node.isVariableDeclaration(parent) ||\n Node.isBindingElement(parent) ||\n Node.isFunctionDeclaration(parent) ||\n Node.isClassDeclaration(parent) ||\n Node.isParameterDeclaration(parent) ||\n Node.isEnumDeclaration(parent)\n ) {\n return parent.getNameNode() === node;\n }\n if (Node.isImportSpecifier(parent)) {\n return (parent.getAliasNode() ?? parent.getNameNode()) === node;\n }\n if (Node.isImportClause(parent)) return parent.getDefaultImport() === node;\n if (Node.isNamespaceImport(parent)) return parent.getNameNode() === node;\n return false;\n}\n\n/** Identifiers spelled `name` that are member names (`x.name`), never the package's binding. */\nexport function memberAccesses(script: SourceFile, name: string): readonly Identifier[] {\n const { Node, SyntaxKind } = loadTsMorph();\n return script.getDescendantsOfKind(SyntaxKind.Identifier).filter((node) => {\n const parent = node.getParent();\n return (\n node.getText() === name &&\n Node.isPropertyAccessExpression(parent) &&\n parent.getNameNode() === node\n );\n });\n}\n\nexport function replaceNode(node: Node, text: string): TextEdit {\n return { start: node.getStart(), end: node.getEnd(), text };\n}\n\n/** Splice non-overlapping edits into `text`. */\nexport function applyTextEdits(text: string, edits: readonly TextEdit[]): string {\n const ordered = [...edits].sort((left, right) => right.start - left.start);\n let output = text;\n let floor = Number.POSITIVE_INFINITY;\n for (const edit of ordered) {\n if (edit.end > floor) throw new Error('overlapping codemod edits');\n output = `${output.slice(0, edit.start)}${edit.text}${output.slice(edit.end)}`;\n floor = edit.start;\n }\n return output;\n}\n","/**\n * Ledger entry 9: `fetchPreviewDocument()`/`fetchPreviewGlobal()` on the root\n * entry → `definePreview({ serverURL, depth }).fetchDocument()`/`.fetchGlobal()`\n * on `payload-live-preview/server`.\n */\nimport {\n PACKAGE_NAME,\n declaresName,\n loadTsMorph,\n packageBindings,\n propertyKey,\n referencesTo,\n type PackageBinding,\n} from '../ast';\nimport type { CodemodImplementation, CodemodConflict, CodemodPlan, TextEdit } from '../types';\nimport type {\n CallExpression,\n ImportDeclaration,\n ObjectLiteralExpression,\n SourceFile,\n} from 'ts-morph';\n\nconst ID = 'move-fetch-preview-helpers';\nconst SERVER_SPECIFIER = `${PACKAGE_NAME}/server`;\nconst METHOD: Readonly<Record<string, string>> = {\n fetchPreviewDocument: 'fetchDocument',\n fetchPreviewGlobal: 'fetchGlobal',\n};\nconst SERVER_OPTIONS = new Set(['serverURL', 'apiRoute', 'depth']);\nconst DROPPED_OPTIONS = new Set(['draft', 'fetchFn']);\nconst DEPTH_TODO =\n 'depth: 1 /* TODO(pll migrate): the 1.x default; match the runtime mergeDepth */';\nconst AUTHORIZATION_TODO =\n 'authorization: null /* TODO(pll migrate): pass the verdict from authorizePreviewRequest(); ' +\n 'null reads the published document */';\n\nfunction conflict(line: number, reason: string): CodemodConflict {\n return { codemod: ID, reason, line };\n}\n\ninterface RewrittenCall {\n readonly edit: TextEdit;\n readonly note: CodemodConflict;\n}\n\nfunction rewriteCall(\n call: CallExpression,\n options: ObjectLiteralExpression,\n imported: string,\n): RewrittenCall {\n const server: string[] = [];\n const read: string[] = [];\n const dropped: string[] = [];\n const keys = new Set<string>();\n for (const property of options.getProperties()) {\n const key = propertyKey(property);\n if (key !== undefined) keys.add(key);\n if (key !== undefined && SERVER_OPTIONS.has(key)) server.push(property.getText());\n else if (key !== undefined && DROPPED_OPTIONS.has(key)) dropped.push(key);\n else read.push(property.getText());\n }\n if (!keys.has('depth')) server.push(DEPTH_TODO);\n if (!keys.has('authorization')) read.push(AUTHORIZATION_TODO);\n const typeArguments = call.getTypeArguments().map((argument) => argument.getText());\n const generic = typeArguments.length === 0 ? '' : `<${typeArguments.join(', ')}>`;\n const method = METHOD[imported] ?? imported;\n const text = `definePreview({ ${server.join(', ')} }).${method}${generic}({ ${read.join(', ')} })`;\n const droppedNote = dropped.length === 0 ? '' : `, and ${dropped.join('/')} was dropped`;\n return {\n edit: { start: call.getStart(), end: call.getEnd(), text },\n note: conflict(\n call.getStartLineNumber(),\n `${imported}() was rewritten onto definePreview().${method}(); it now returns a ` +\n `PreviewFetchResult (check ok/data) and needs a real authorization for drafts${droppedNote}`,\n ),\n };\n}\n\nfunction importLine(quote: string, names: readonly string[], specifier: string): string {\n return `import { ${names.join(', ')} } from ${quote}${specifier}${quote};`;\n}\n\n/** Drop the moved names from their root imports and make sure `definePreview` is imported from the server entry. */\nfunction rewriteImports(script: SourceFile, bindings: readonly PackageBinding[]): TextEdit[] {\n const { Node } = loadTsMorph();\n const edits: TextEdit[] = [];\n const declarations = new Set<ImportDeclaration>();\n for (const binding of bindings) {\n if (Node.isImportDeclaration(binding.declaration)) declarations.add(binding.declaration);\n }\n const serverImport = script\n .getImportDeclarations()\n .find(\n (declaration) =>\n declaration.getModuleSpecifierValue() === SERVER_SPECIFIER && !declaration.isTypeOnly(),\n );\n const hasDefinePreview =\n serverImport?.getNamedImports().some((spec) => spec.getName() === 'definePreview') === true;\n let serverLine: string | undefined;\n if (serverImport !== undefined && !hasDefinePreview) {\n const quote = serverImport.getModuleSpecifier().getQuoteKind();\n const names = [\n ...serverImport.getNamedImports().map((spec) => spec.getText()),\n 'definePreview',\n ];\n const defaultImport = serverImport.getDefaultImport()?.getText();\n const clauses =\n defaultImport === undefined ? names.join(', ') : `${defaultImport}, { ${names.join(', ')} }`;\n const text =\n defaultImport === undefined\n ? importLine(quote, names, SERVER_SPECIFIER)\n : `import ${clauses} from ${quote}${SERVER_SPECIFIER}${quote};`;\n edits.push({ start: serverImport.getStart(), end: serverImport.getEnd(), text });\n }\n for (const declaration of declarations) {\n const quote = declaration.getModuleSpecifier().getQuoteKind();\n const remaining = declaration\n .getNamedImports()\n .filter((spec) => !(spec.getName() in METHOD))\n .map((spec) => spec.getText());\n const defaultImport = declaration.getDefaultImport()?.getText();\n const clauses = [\n defaultImport,\n remaining.length === 0 ? undefined : `{ ${remaining.join(', ')} }`,\n ].filter((clause): clause is string => clause !== undefined);\n const typeOnly = declaration.isTypeOnly() ? 'type ' : '';\n const kept =\n clauses.length === 0\n ? ''\n : `import ${typeOnly}${clauses.join(', ')} from ${quote}${PACKAGE_NAME}${quote};`;\n if (serverImport === undefined && serverLine === undefined) {\n serverLine = importLine(quote, ['definePreview'], SERVER_SPECIFIER);\n edits.push({\n start: declaration.getStart(),\n end: declaration.getEnd(),\n text: kept === '' ? serverLine : `${kept}\\n${serverLine}`,\n });\n } else {\n // A removed declaration takes its line break with it.\n const end = declaration.getEnd();\n const trailingNewline = kept === '' && script.getFullText()[end] === '\\n' ? 1 : 0;\n edits.push({ start: declaration.getStart(), end: end + trailingNewline, text: kept });\n }\n }\n return edits;\n}\n\nfunction plan(script: SourceFile, bindings: readonly PackageBinding[]): CodemodPlan {\n const { Node } = loadTsMorph();\n const blocking: CodemodConflict[] = [];\n const notes: CodemodConflict[] = [];\n const edits: TextEdit[] = [];\n for (const binding of bindings) {\n if (binding.kind === 'require') {\n blocking.push(\n conflict(\n binding.declaration.getStartLineNumber(),\n `${binding.imported} is bound with require(); rewrite it onto definePreview() from ` +\n `${SERVER_SPECIFIER} by hand`,\n ),\n );\n continue;\n }\n for (const reference of referencesTo(script, binding)) {\n const line = reference.node.getStartLineNumber();\n const call = reference.node.getParent();\n const isCall =\n reference.kind === 'reference' &&\n Node.isCallExpression(call) &&\n call.getExpression() === reference.node;\n if (!isCall) {\n blocking.push(\n conflict(\n line,\n `${binding.local} is used other than as a direct call; rewrite it onto definePreview() by hand`,\n ),\n );\n continue;\n }\n const [options] = call.getArguments();\n if (\n options === undefined ||\n !Node.isObjectLiteralExpression(options) ||\n options.getProperties().some((property) => Node.isSpreadAssignment(property))\n ) {\n blocking.push(\n conflict(\n line,\n `${binding.imported}() options are not a plain object literal, so serverURL/depth ` +\n 'cannot be split out; rewrite it onto definePreview() by hand',\n ),\n );\n continue;\n }\n const rewritten = rewriteCall(call, options, binding.imported);\n edits.push(rewritten.edit);\n notes.push(rewritten.note);\n }\n }\n const serverDeclaration = script\n .getImportDeclarations()\n .filter((declaration) => declaration.getModuleSpecifierValue() === SERVER_SPECIFIER);\n const foreign = declaresName(script, 'definePreview', serverDeclaration);\n if (foreign !== undefined) {\n blocking.push(\n conflict(\n foreign.getStartLineNumber(),\n `this module already binds a definePreview that is not ${SERVER_SPECIFIER}'s; rename it, ` +\n 'then move the fetch helpers by hand',\n ),\n );\n }\n if (blocking.length > 0) return { edits: [], conflicts: [...blocking, ...notes] };\n edits.push(...rewriteImports(script, bindings));\n return { edits, conflicts: notes };\n}\n\nexport const moveFetchPreviewHelpers: CodemodImplementation = {\n id: ID,\n summary:\n '`fetchPreviewDocument`/`fetchPreviewGlobal` from the root → `definePreview().fetchDocument`/`.fetchGlobal` (payload-live-preview/server)',\n ledgerEntry: 9,\n apply(script) {\n const bindings = packageBindings(script).filter(\n (binding) => binding.specifier === PACKAGE_NAME && binding.imported in METHOD,\n );\n if (bindings.length === 0) return { edits: [], conflicts: [] };\n return plan(script, bindings);\n },\n};\n","/** Ledger entry 7: `createPreviewBindings({ authorized })` → `{ authorization }`. */\nimport { loadTsMorph, packageBindings, propertyKey, referencesTo, replaceNode } from '../ast';\nimport type { CodemodImplementation, CodemodConflict, TextEdit } from '../types';\n\nconst ID = 'rename-bindings-authorized-option';\nconst OLD_KEY = 'authorized';\nconst NEW_KEY = 'authorization';\nconst BY_HAND =\n `if they carry \\`${OLD_KEY}\\`, rename it to \\`${NEW_KEY}\\` and pass the verdict from ` +\n 'authorizePreviewRequest()';\n\nexport const renameBindingsAuthorizedOption: CodemodImplementation = {\n id: ID,\n summary: '`createPreviewBindings({ authorized })` → `{ authorization }`',\n ledgerEntry: 7,\n apply(script) {\n const { Node } = loadTsMorph();\n const edits: TextEdit[] = [];\n const conflicts: CodemodConflict[] = [];\n const bindings = packageBindings(script).filter(\n (binding) => binding.imported === 'createPreviewBindings',\n );\n for (const binding of bindings) {\n for (const reference of referencesTo(script, binding)) {\n const call = reference.node.getParent();\n if (reference.kind !== 'reference' || !Node.isCallExpression(call)) continue;\n if (call.getExpression() !== reference.node) continue;\n const [options] = call.getArguments();\n if (options === undefined) continue;\n const line = options.getStartLineNumber();\n if (!Node.isObjectLiteralExpression(options)) {\n conflicts.push({\n codemod: ID,\n line,\n reason: `createPreviewBindings() options are not a literal; ${BY_HAND}`,\n });\n continue;\n }\n for (const property of options.getProperties()) {\n if (Node.isSpreadAssignment(property)) {\n conflicts.push({\n codemod: ID,\n line: property.getStartLineNumber(),\n reason: `createPreviewBindings() spreads its options; ${BY_HAND}`,\n });\n } else if (propertyKey(property) !== OLD_KEY) {\n continue;\n } else if (Node.isShorthandPropertyAssignment(property)) {\n edits.push(replaceNode(property, `${NEW_KEY}: ${OLD_KEY}`));\n } else if (Node.isPropertyAssignment(property)) {\n edits.push(replaceNode(property.getNameNode(), NEW_KEY));\n }\n }\n }\n }\n return { edits, conflicts };\n },\n};\n","/** Ledger entry 1: `isPreviewRequest()` → `hasPreviewIntent()`, same signature. */\nimport {\n declaresName,\n memberAccesses,\n packageBindings,\n referencesTo,\n replaceNode,\n type PackageBinding,\n} from '../ast';\nimport type { CodemodImplementation, CodemodConflict, CodemodPlan, TextEdit } from '../types';\n\nconst ID = 'rename-is-preview-request';\nconst OLD_NAME = 'isPreviewRequest';\nconst NEW_NAME = 'hasPreviewIntent';\n\nfunction conflict(line: number, reason: string): CodemodConflict {\n return { codemod: ID, reason, line };\n}\n\nfunction renameBinding(binding: PackageBinding): TextEdit {\n // `import { isPreviewRequest as hasPreviewIntent }` collapses to the bare name.\n if (binding.aliased && binding.local === NEW_NAME) {\n return replaceNode(binding.importedNode.getParentOrThrow(), NEW_NAME);\n }\n return replaceNode(binding.importedNode, NEW_NAME);\n}\n\nfunction plan(\n bindings: readonly PackageBinding[],\n script: Parameters<CodemodImplementation['apply']>[0],\n): CodemodPlan {\n const blocking: CodemodConflict[] = [];\n const edits: TextEdit[] = [];\n const existing = declaresName(\n script,\n NEW_NAME,\n bindings.map((binding) => binding.declaration),\n );\n if (existing !== undefined) {\n blocking.push(\n conflict(\n existing.getStartLineNumber(),\n `this module already binds ${NEW_NAME}; rename it (or drop the wrapper and import the ` +\n `package's ${NEW_NAME} directly), then rename ${OLD_NAME} by hand`,\n ),\n );\n }\n for (const binding of bindings) {\n edits.push(renameBinding(binding));\n if (binding.aliased) continue;\n for (const reference of referencesTo(script, binding)) {\n if (reference.kind === 'reference') {\n edits.push(replaceNode(reference.node, NEW_NAME));\n } else if (reference.kind === 'shorthand') {\n blocking.push(\n conflict(\n reference.node.getStartLineNumber(),\n `{ ${OLD_NAME} } keeps the old key for whoever reads it; write ` +\n `{ ${OLD_NAME}: ${NEW_NAME} } or rename the key and its readers by hand`,\n ),\n );\n } else {\n blocking.push(\n conflict(\n reference.node.getStartLineNumber(),\n `${OLD_NAME} is re-exported under its old name; rename the export and its ` +\n `importers, or write export { ${NEW_NAME} as ${OLD_NAME} }`,\n ),\n );\n }\n }\n }\n const notes = memberAccesses(script, OLD_NAME).map((node) =>\n conflict(\n node.getStartLineNumber(),\n `.${OLD_NAME} is a member of this module's own object, not the package import, and was ` +\n 'left alone; check that it does not wrap the removed API',\n ),\n );\n if (blocking.length > 0) return { edits: [], conflicts: [...blocking, ...notes] };\n return { edits, conflicts: notes };\n}\n\nexport const renameIsPreviewRequest: CodemodImplementation = {\n id: ID,\n summary: '`isPreviewRequest()` → `hasPreviewIntent()` (same signature)',\n ledgerEntry: 1,\n apply(script) {\n const bindings = packageBindings(script).filter((binding) => binding.imported === OLD_NAME);\n if (bindings.length === 0) return { edits: [], conflicts: [] };\n return plan(bindings, script);\n },\n};\n","/** Where the script lives in a file: the whole file, or the script blocks of a component. */\nimport { extname } from 'node:path';\nimport type { ScriptKind } from './ast';\n\nexport interface ScriptBlock {\n readonly start: number;\n readonly end: number;\n readonly kind: ScriptKind;\n}\n\nconst WHOLE_FILE: Readonly<Record<string, ScriptKind>> = {\n '.ts': 'ts',\n '.mts': 'ts',\n '.cts': 'ts',\n '.tsx': 'tsx',\n '.js': 'js',\n '.mjs': 'js',\n '.cjs': 'js',\n '.jsx': 'jsx',\n};\nconst COMPONENT_EXTENSIONS = new Set(['.astro', '.vue', '.svelte']);\nconst FRONTMATTER = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/u;\n// Browsers close a script on `</script` followed by anything up to `>`, so the\n// end tag must accept that too, or a stray `</script foo>` would swallow the\n// rest of the file into one block.\nconst SCRIPT_TAG = /<script(?:\\s[^>]*)?>([\\s\\S]*?)<\\/script\\b[^>]*>/giu;\n\nexport function scriptBlocks(source: string, fileName: string): readonly ScriptBlock[] {\n const extension = extname(fileName).toLowerCase();\n if (!COMPONENT_EXTENSIONS.has(extension)) {\n return [{ start: 0, end: source.length, kind: WHOLE_FILE[extension] ?? 'ts' }];\n }\n const blocks: ScriptBlock[] = [];\n const frontmatter = extension === '.astro' ? FRONTMATTER.exec(source) : null;\n if (frontmatter?.[1] !== undefined) {\n const start = frontmatter.index + frontmatter[0].indexOf('\\n') + 1;\n blocks.push({ start, end: start + frontmatter[1].length, kind: 'ts' });\n }\n for (const match of source.matchAll(SCRIPT_TAG)) {\n const body = match[1] ?? '';\n if (body.trim() === '') continue;\n const start = match.index + match[0].lastIndexOf(body);\n blocks.push({ start, end: start + body.length, kind: 'ts' });\n }\n return blocks;\n}\n","/**\n * `pll migrate`: the 1.x → 2.0 codemods and the driver that applies them to one\n * source string. Rewrites are planned on the AST (ts-morph) so only names the\n * package binds are touched; see ADR 0007.\n */\nimport { applyTextEdits, parseScript } from './ast';\nimport { moveFetchPreviewHelpers } from './codemods/move-fetch-preview-helpers';\nimport { renameBindingsAuthorizedOption } from './codemods/rename-bindings-authorized-option';\nimport { renameIsPreviewRequest } from './codemods/rename-is-preview-request';\nimport { scriptBlocks } from './script-blocks';\nimport type {\n Codemod,\n CodemodConflict,\n CodemodEdit,\n CodemodImplementation,\n CodemodLineEdit,\n} from './types';\n\nexport type {\n Codemod,\n CodemodConflict,\n CodemodEdit,\n CodemodLineEdit,\n CodemodPlan,\n TextEdit,\n} from './types';\n\nconst PACKAGE_REFERENCE =\n /(?:\\bfrom\\s*|\\bimport\\s*\\(?\\s*|\\brequire\\s*\\(\\s*)['\"]payload-live-preview(?:\\/[^'\"]*)?['\"]/u;\n\n/** Whether the source names the package in an `import`, `import()` or `require()`. */\nexport function importsThisPackage(source: string): boolean {\n return PACKAGE_REFERENCE.test(source);\n}\n\nconst IMPLEMENTATIONS: readonly CodemodImplementation[] = [\n renameIsPreviewRequest,\n renameBindingsAuthorizedOption,\n moveFetchPreviewHelpers,\n];\n\n/**\n * The codemods in the order they run. Each is idempotent. Metadata only: the\n * rewrite is internal, so importing this entry's types does not require\n * `ts-morph`, which is needed only to run `pll migrate`.\n */\nexport const CODEMODS: readonly Codemod[] = IMPLEMENTATIONS;\n\nexport interface MigrateSourceOptions {\n /** Restrict to these codemod ids. */\n readonly only?: readonly string[];\n /**\n * Decides how the source is parsed: `.astro`, `.vue` and `.svelte` by script\n * block, JSX by extension. Defaults to a `.ts` module.\n */\n readonly fileName?: string;\n}\n\nexport interface MigrateSourceResult {\n readonly output: string;\n readonly edits: readonly CodemodEdit[];\n readonly conflicts: readonly CodemodConflict[];\n}\n\nfunction lineNumberAt(source: string, offset: number): number {\n let line = 1;\n for (let index = 0; index < offset; index += 1) if (source[index] === '\\n') line += 1;\n return line;\n}\n\n/** The changed lines between two versions, trimmed to the differing middle. */\nfunction lineEdits(before: string, after: string, firstLine: number): CodemodLineEdit[] {\n const oldLines = before.split('\\n');\n const newLines = after.split('\\n');\n let prefix = 0;\n while (\n prefix < oldLines.length &&\n prefix < newLines.length &&\n oldLines[prefix] === newLines[prefix]\n ) {\n prefix += 1;\n }\n let suffix = 0;\n while (\n suffix < oldLines.length - prefix &&\n suffix < newLines.length - prefix &&\n oldLines[oldLines.length - 1 - suffix] === newLines[newLines.length - 1 - suffix]\n ) {\n suffix += 1;\n }\n const oldMiddle = oldLines.slice(prefix, oldLines.length - suffix);\n const newMiddle = newLines.slice(prefix, newLines.length - suffix);\n const changed: CodemodLineEdit[] = [];\n for (let index = 0; index < Math.max(oldMiddle.length, newMiddle.length); index += 1) {\n const before = oldMiddle[index] ?? '';\n const after = newMiddle[index] ?? '';\n if (before !== after) changed.push({ line: firstLine + prefix + index, before, after });\n }\n return changed;\n}\n\n/** Apply every codemod (or the chosen subset) to one source string. */\nexport function migrateSource(\n source: string,\n options: MigrateSourceOptions = {},\n): MigrateSourceResult {\n const chosen =\n options.only === undefined\n ? IMPLEMENTATIONS\n : IMPLEMENTATIONS.filter((codemod) => options.only?.includes(codemod.id));\n if (chosen.length === 0 || !importsThisPackage(source)) {\n return { output: source, edits: [], conflicts: [] };\n }\n const byCodemod = new Map<string, CodemodLineEdit[]>();\n const conflicts: CodemodConflict[] = [];\n let output = '';\n let cursor = 0;\n for (const block of scriptBlocks(source, options.fileName ?? 'source.ts')) {\n output += source.slice(cursor, block.start);\n const firstLine = lineNumberAt(source, block.start);\n let text = source.slice(block.start, block.end);\n for (const codemod of chosen) {\n const plan = codemod.apply(parseScript(text, block.kind));\n for (const item of plan.conflicts) {\n conflicts.push(\n item.line === undefined ? item : { ...item, line: item.line + firstLine - 1 },\n );\n }\n if (plan.edits.length === 0) continue;\n const next = applyTextEdits(text, plan.edits);\n const lines = byCodemod.get(codemod.id) ?? [];\n lines.push(...lineEdits(text, next, firstLine));\n byCodemod.set(codemod.id, lines);\n text = next;\n }\n output += text;\n cursor = block.end;\n }\n output += source.slice(cursor);\n const edits = [...byCodemod].map(([codemod, lines]) => ({ codemod, count: lines.length, lines }));\n return { output, edits, conflicts };\n}\n","/** The file walker behind `pll migrate`: find source files, apply the codemods, report or write. */\nimport { readFile, readdir, stat, writeFile } from 'node:fs/promises';\nimport { extname, join, relative } from 'node:path';\nimport { migrateSource } from './index';\nimport type { CodemodConflict, CodemodEdit } from './types';\n\nconst SOURCE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.mts',\n '.cts',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.astro',\n '.svelte',\n '.vue',\n]);\nconst DECLARATION_FILE = /\\.d\\.[cm]?ts$/u;\nconst SKIP_DIRECTORIES = new Set([\n 'node_modules',\n 'dist',\n '.git',\n '.astro',\n '.svelte-kit',\n '.next',\n '.nuxt',\n 'coverage',\n]);\n\nexport interface MigrateFileResult {\n readonly file: string;\n readonly edits: readonly CodemodEdit[];\n readonly changed: boolean;\n /** What a codemod could not rewrite safely in this file. */\n readonly conflicts: readonly CodemodConflict[];\n}\n\nexport interface MigrateRunResult {\n readonly files: readonly MigrateFileResult[];\n /** Files the run changed (or would change, in dry-run). */\n readonly changedCount: number;\n /** Codemod id → number of files it touched. */\n readonly byCodemod: Readonly<Record<string, number>>;\n /** Files needing manual attention. */\n readonly conflictCount: number;\n}\n\nexport interface MigrateOptions {\n /** Apply and save; without it the run only reports. */\n readonly write?: boolean;\n /** Restrict to these codemod ids. */\n readonly only?: readonly string[];\n /** Reader/writer injection for tests. */\n readonly io?: {\n readonly read: (path: string) => Promise<string>;\n readonly write: (path: string, content: string) => Promise<void>;\n readonly list: (root: string) => Promise<readonly string[]>;\n };\n}\n\nfunction isSourceFile(name: string): boolean {\n return SOURCE_EXTENSIONS.has(extname(name)) && !DECLARATION_FILE.test(name);\n}\n\nasync function collectFiles(root: string): Promise<string[]> {\n const found: string[] = [];\n async function walk(directory: string): Promise<void> {\n for (const entry of await readdir(directory, { withFileTypes: true })) {\n const path = join(directory, entry.name);\n if (entry.isDirectory()) {\n if (!SKIP_DIRECTORIES.has(entry.name)) await walk(path);\n } else if (isSourceFile(entry.name)) {\n found.push(path);\n }\n }\n }\n const info = await stat(root);\n if (info.isDirectory()) await walk(root);\n else if (isSourceFile(root)) found.push(root);\n return found.sort();\n}\n\nfunction relativeTo(root: string, file: string): string {\n const rel = relative(root, file);\n return rel.length === 0 ? file : rel;\n}\n\n/** Migrate every source file under `root`. */\nexport async function runMigrate(\n root: string,\n options: MigrateOptions = {},\n): Promise<MigrateRunResult> {\n const io = options.io ?? {\n read: (path) => readFile(path, 'utf8'),\n write: (path, content) => writeFile(path, content, 'utf8'),\n list: collectFiles,\n };\n const results: MigrateFileResult[] = [];\n const byCodemod: Record<string, number> = {};\n for (const file of await io.list(root)) {\n const source = await io.read(file);\n const { output, edits, conflicts } = migrateSource(source, {\n fileName: file,\n ...(options.only === undefined ? {} : { only: options.only }),\n });\n const changed = output !== source;\n if (changed && options.write === true) await io.write(file, output);\n if (changed) {\n for (const edit of edits) byCodemod[edit.codemod] = (byCodemod[edit.codemod] ?? 0) + 1;\n }\n results.push({ file: relativeTo(root, file), edits, changed, conflicts });\n }\n return {\n files: results,\n changedCount: results.filter((result) => result.changed).length,\n byCodemod,\n conflictCount: results.filter((result) => result.conflicts.length > 0).length,\n };\n}\n","/**\n * `pll migrate <path> [--write] [--only <id,id>]`, hosted by the `pll` binary.\n * Exit codes: 0 clean, 1 usage or I/O error, 3 when conflicts need a human.\n */\nimport { runMigrate, type MigrateFileResult } from './runner';\n\nexport const MIGRATE_HELP = `pll migrate — rewrite 1.x APIs to their 2.0 names and homes (ADR 0007)\n\nUsage:\n pll migrate <path> [--write] [--only <id,id>]\n\nWithout --write the run only reports what it would change. Codemods touch\nonly names bound from payload-live-preview; anything they cannot rewrite\nsafely is listed as needing manual attention. Requires ts-morph.\n\nOptions:\n --write Apply the changes (otherwise dry-run)\n --only <ids> Run only these codemods (comma-separated)\n -h, --help Show this help\n\nExit codes:\n 0 nothing needs a human (changes applied, or would be with --write)\n 1 usage error, unreadable path, or ts-morph is not installed\n 3 at least one file needs manual attention\n`;\n\ninterface MigrateArgs {\n readonly target: string | undefined;\n readonly write: boolean;\n readonly only: readonly string[] | undefined;\n readonly help: boolean;\n readonly unknown: string | undefined;\n}\n\nfunction parseArgs(argv: readonly string[]): MigrateArgs {\n let target: string | undefined;\n let write = false;\n let only: string[] | undefined;\n let help = false;\n let unknown: string | undefined;\n const ids = (value: string): string[] => value.split(',').filter((id) => id.length > 0);\n for (let index = 0; index < argv.length; index += 1) {\n const token = argv[index];\n if (token === undefined) continue;\n if (token === '-h' || token === '--help') help = true;\n else if (token === '--write') write = true;\n else if (token === '--only') {\n only = ids(argv[index + 1] ?? '');\n index += 1;\n } else if (token.startsWith('--only=')) only = ids(token.slice('--only='.length));\n else if (token.startsWith('-')) unknown ??= token;\n else target ??= token;\n }\n return { target, write, only, help, unknown };\n}\n\nfunction describe(file: MigrateFileResult): string {\n return file.edits.map((edit) => `${edit.codemod}: ${String(edit.count)} line(s)`).join(', ');\n}\n\nexport async function runMigrateCommand(argv: readonly string[]): Promise<number> {\n const args = parseArgs(argv);\n if (args.help) {\n process.stdout.write(MIGRATE_HELP);\n return 0;\n }\n if (args.unknown !== undefined) {\n process.stderr.write(`pll migrate: unknown option ${args.unknown}\\n`);\n return 1;\n }\n if (args.target === undefined) {\n process.stderr.write('pll migrate: a path is required. Try `pll migrate --help`.\\n');\n return 1;\n }\n let result;\n try {\n result = await runMigrate(args.target, {\n write: args.write,\n ...(args.only === undefined ? {} : { only: args.only }),\n });\n } catch (error) {\n process.stderr.write(\n `pll migrate: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n return 1;\n }\n for (const file of result.files) {\n if (!file.changed) continue;\n process.stdout.write(\n `${args.write ? 'migrated' : 'would migrate'} ${file.file} (${describe(file)})\\n`,\n );\n }\n process.stdout.write(\n `\\n${args.write ? 'Migrated' : 'Would migrate'} ${String(result.changedCount)} file(s).` +\n `${args.write ? '' : ' Re-run with --write to apply.'}\\n`,\n );\n const conflicted = result.files.filter((file) => file.conflicts.length > 0);\n if (conflicted.length === 0) return 0;\n process.stdout.write(`\\n${String(conflicted.length)} file(s) need manual attention:\\n`);\n for (const file of conflicted) {\n for (const item of file.conflicts) {\n const where = item.line === undefined ? file.file : `${file.file}:${String(item.line)}`;\n process.stdout.write(` ${where}: ${item.reason}\\n`);\n }\n }\n return 3;\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\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 * 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. 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\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\n /** A message was rejected before it reached the update pipeline. */\n MessageRejected: 'LP0501',\n /** A preview token was rejected. */\n TokenRejected: 'LP0502',\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\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. */\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 requested again for the same revision; the loop guard stopped it. */\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 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 * `pll doctor --v2`: read the served inline configuration and report each\n * runtime row still at its 1.x value (ADR 0007).\n */\nimport type { DoctorFinding, DoctorProbe } from './types';\n\nconst CONFIG_MARKER = 'var __LIVE_PREVIEW_CONFIG__=';\n/** Positions in the inline tuple; keep aligned with `RuntimeBuildConfig` in core/runtime.ts. */\nconst RUNTIME_SLOT = Object.freeze({\n disableReferrerDetection: 11,\n skipUnchanged: 14,\n eventSourcePolicy: 15,\n sanitizerPolicy: 16,\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\nfunction gap(title: string, detail: string, remedy: string): DoctorFinding {\n return { code: 'LP0709', level: 'warning', title, detail, remedy };\n}\n\n/** One `LP0709` finding per runtime row not yet at its 2.0 value; `null` slots count as unset. */\nexport function analyzeV2Readiness(probe: DoctorProbe): readonly DoctorFinding[] {\n const config = readInlineConfig(probe.previewResponse.body);\n if (config === undefined) {\n return [\n {\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 ];\n }\n const findings: DoctorFinding[] = [];\n if (config[RUNTIME_SLOT.disableReferrerDetection] !== true) {\n findings.push(\n gap(\n 'Referrer trust is still on',\n 'The runtime accepts the admin referer as a preview signal.',\n \"Set `defaults: 'v2'` (or `disableReferrerDetection: true`) so referrer trust is off outside local dev.\",\n ),\n );\n }\n if (config[RUNTIME_SLOT.eventSourcePolicy] !== 'parent-or-opener') {\n findings.push(\n gap(\n 'Messages are accepted from any window',\n `eventSourcePolicy is ${JSON.stringify(config[RUNTIME_SLOT.eventSourcePolicy] ?? 'any')}.`,\n \"Set `defaults: 'v2'` (or `eventSourcePolicy: 'parent-or-opener'`).\",\n ),\n );\n }\n if (config[RUNTIME_SLOT.sanitizerPolicy] !== 'strict') {\n findings.push(\n gap(\n 'Sanitizer is in compat mode',\n `sanitizerPolicy is ${JSON.stringify(config[RUNTIME_SLOT.sanitizerPolicy] ?? 'compat')}; id and every data-* pass.`,\n \"Set `defaults: 'v2'` (or `sanitizerPolicy: 'strict'`) once rich text no longer relies on id/data-*.\",\n ),\n );\n }\n if (config[RUNTIME_SLOT.skipUnchanged] !== true) {\n findings.push(\n gap(\n 'Unchanged bindings are re-applied every message',\n 'skipUnchanged is off.',\n \"Set `defaults: 'v2'` (or `skipUnchanged: true`) to skip bindings whose value did not change.\",\n ),\n );\n }\n return 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 findings.push({\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 or a ' +\n 'security middleware rather than by the app.',\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","/**\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. */\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","#!/usr/bin/env node\n/**\n * The `pll` binary: `pll doctor <url>` here, `pll migrate <path>` in\n * migrate/cli. Doctor exit codes: 0 no error-level findings, 1 usage error or\n * the URL could not be fetched, 2 at least one error-level finding.\n */\nimport { runMigrateCommand } from '../migrate/cli';\nimport { formatReport } from './format';\nimport { describeFailure, runDoctor, type DoctorFetch } from './probe';\n\ninterface ParsedArgs {\n url: string | undefined;\n adminOrigin: string | undefined;\n json: boolean;\n v2: boolean;\n showHelp: boolean;\n unknown: string[];\n}\n\nfunction parseArgs(argv: readonly string[]): ParsedArgs {\n const parsed: ParsedArgs = {\n url: undefined,\n adminOrigin: undefined,\n json: false,\n v2: false,\n showHelp: false,\n unknown: [],\n };\n for (let i = 0; i < argv.length; i += 1) {\n const token = argv[i];\n if (token === undefined) continue;\n if (token === '-h' || token === '--help') parsed.showHelp = true;\n else if (token === '--json') parsed.json = true;\n else if (token === '--v2') parsed.v2 = true;\n else if (token === '--admin' || token === '-a') {\n parsed.adminOrigin = argv[i + 1];\n i += 1;\n } else if (token.startsWith('--admin=')) parsed.adminOrigin = token.slice('--admin='.length);\n else if (token.startsWith('-')) parsed.unknown.push(token);\n else parsed.url ??= token;\n }\n return parsed;\n}\n\nconst HELP_TEXT = `pll doctor — audit what a live-preview deployment actually serves\n\nUsage:\n pll doctor <url> [--admin <origin>] [--json] [--v2]\n pll migrate <path> [--write] [--only <id,id>]\n\nThe URL is fetched twice: once as an ordinary visitor and once with the\nheaders the Payload admin's iframe sends. Most findings come from the\ndifference between the two responses. Redirects are reported, not followed.\n\nOptions:\n -a, --admin <origin> Admin origin the preview is embedded from. Enables the\n frame-ancestors check to verify the origin is admitted,\n not merely that a policy exists.\n --json Emit the report as JSON instead of text\n --v2 Also check the page against the 2.0 readiness table\n -h, --help Show this help\n\nExit codes:\n 0 no error-level findings\n 1 usage error, or the URL could not be fetched\n 2 at least one error-level finding\n\nExamples:\n pll doctor https://example.com/\n pll doctor https://example.com/blog/hello --admin https://cms.example.com\n`;\n\nfunction isAbsoluteUrl(value: string): boolean {\n try {\n return new URL(value).origin !== 'null';\n } catch {\n return false;\n }\n}\n\n/** @param fetchImpl Seam for tests; the bin shim never passes it. */\nexport async function run(argv: readonly string[], fetchImpl?: DoctorFetch): Promise<number> {\n const [subcommand, ...rest] = argv;\n if (subcommand === undefined || subcommand === '-h' || subcommand === '--help') {\n process.stdout.write(HELP_TEXT);\n return subcommand === undefined ? 1 : 0;\n }\n if (subcommand === 'migrate') return runMigrateCommand(rest);\n if (subcommand !== 'doctor') {\n process.stderr.write(`pll: unknown command \"${subcommand}\". Try \\`pll --help\\`.\\n`);\n return 1;\n }\n const args = parseArgs(rest);\n if (args.showHelp) {\n process.stdout.write(HELP_TEXT);\n return 0;\n }\n if (args.unknown.length > 0) {\n process.stderr.write(`pll doctor: unknown option ${args.unknown.join(', ')}\\n`);\n return 1;\n }\n if (args.url === undefined) {\n process.stderr.write('pll doctor: a URL is required. Try `pll doctor --help`.\\n');\n return 1;\n }\n if (args.adminOrigin !== undefined && !isAbsoluteUrl(args.adminOrigin)) {\n process.stderr.write(\n `pll doctor: --admin must be an absolute URL such as https://cms.example.com (got \"${args.adminOrigin}\")\\n`,\n );\n return 1;\n }\n let report;\n try {\n report = await runDoctor({\n url: args.url,\n ...(args.adminOrigin !== undefined ? { adminOrigin: args.adminOrigin } : {}),\n ...(args.v2 ? { v2: true } : {}),\n ...(fetchImpl !== undefined ? { fetchImpl } : {}),\n });\n } catch (error) {\n const message = describeFailure(error);\n if (args.json) {\n process.stdout.write(`${JSON.stringify({ url: args.url, error: message }, undefined, 2)}\\n`);\n } else {\n process.stderr.write(`pll doctor: could not probe ${args.url}: ${message}\\n`);\n }\n return 1;\n }\n process.stdout.write(\n args.json ? `${JSON.stringify(report, undefined, 2)}\\n` : formatReport(report),\n );\n return report.errors > 0 ? 2 : 0;\n}\n\n/** Matches the entry's basename only, so importing this module from a path containing `pll` never runs it. */\nexport function isCliInvocation(argv: readonly (string | undefined)[] = process.argv): boolean {\n if (typeof process === 'undefined') return false;\n const entry = argv[1];\n if (entry === undefined || entry === '') return false;\n const name = entry.split(/[\\\\/]/u).pop() ?? '';\n return name === 'pll' || name === 'pll.cmd' || name.startsWith('doctor-cli');\n}\n\nif (isCliInvocation()) {\n void run(process.argv.slice(2)).then((code) => {\n process.exit(code);\n });\n}\n"],"mappings":"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","ignoreList":[]}
package/dist/doctor.d.ts CHANGED
@@ -1,37 +1,21 @@
1
- import { a as DoctorProbe, b as DoctorContext, c as DoctorReport } from './probe-tMHdim5y.js';
2
- export { d as DIAGNOSTIC_CODES, e as DiagnosticCode, D as DoctorFetch, f as DoctorFinding, g as DoctorLevel, h as DoctorResponse, R as RunDoctorOptions, i as formatReport, l as lowercaseHeaders, r as runDoctor } from './probe-tMHdim5y.js';
1
+ import { a as DoctorProbe, b as DoctorContext, c as DoctorReport, d as DoctorFinding } from './probe-CYtvj_Lm.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-CYtvj_Lm.js';
3
3
 
4
- /**
5
- * The `pll doctor` audit: what a deployment actually serves.
6
- *
7
- * Every check here reads a response, not a configuration file. That is the
8
- * whole point. A config can say `allowedOrigins: [...]` while a proxy strips
9
- * the header, an adapter runs in a mode nobody remembers choosing, or a build
10
- * emits binding attributes on public pages. The gap between what a project
11
- * believes it is configured to do and what it puts on the wire is where this
12
- * project's most expensive findings have lived.
13
- *
14
- * The audit compares two fetches of the same URL: one as an ordinary visitor,
15
- * one shaped the way the admin's iframe requests it. Most of what matters is
16
- * in the difference between them.
17
- *
18
- * HTML is inspected with regular expressions rather than a parser. This is a
19
- * deliberate limit: the audit counts attributes and looks for the runtime
20
- * marker, which regexes do reliably, and it never needs a tree. A malformed
21
- * page can therefore be miscounted — findings say what was observed so the
22
- * reader can check.
23
- *
24
- * Nothing here is sent anywhere. The audit makes exactly the two requests it
25
- * is asked to make, to the URL it is given.
26
- *
27
- * @module @doctor/analyze
28
- */
4
+ /** Audit a probed deployment. */
5
+ declare function analyzeProbe(probe: DoctorProbe, context: DoctorContext & {
6
+ readonly v2?: boolean;
7
+ }): DoctorReport;
29
8
 
30
9
  /**
31
- * Audit a probed deployment.
32
- *
33
- * Pure: the caller performs the two fetches and hands the results in.
10
+ * `pll doctor --v2`: read the served inline configuration and report each
11
+ * runtime row still at its 1.x value (ADR 0007).
34
12
  */
35
- declare function analyzeProbe(probe: DoctorProbe, context: DoctorContext): DoctorReport;
36
13
 
37
- export { DoctorContext, DoctorProbe, DoctorReport, analyzeProbe };
14
+ /** One `LP0709` finding per runtime row not yet at its 2.0 value; `null` slots count as unset. */
15
+ declare function analyzeV2Readiness(probe: DoctorProbe): readonly DoctorFinding[];
16
+
17
+ /** Terminal rendering of a doctor report. */
18
+
19
+ declare function formatReport(report: DoctorReport): string;
20
+
21
+ export { DoctorContext, DoctorFinding, DoctorProbe, DoctorReport, analyzeProbe, analyzeV2Readiness, formatReport };