@moorage/plugin-seo 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @moorage/plugin-seo
2
2
 
3
+ ## 0.1.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 4586179: README: on Cloudflare's Workers free plan a save or publish can now and then fail with "Worker exceeded resource limits" (about 10 ms of CPU per request, which EmDash's own saving already comes close to); trying again works, and Workers Paid does not have this problem. No code change.
8
+
9
+ ## 0.1.4
10
+
11
+ ### Patch Changes
12
+
13
+ - 272f2f3: The SEO page shows real numbers on a new site that runs the plugin in its own process (`plugins: [seo]`, as on the Workers free plan). There the plugin is switched on when the server starts, before setup has added any pages, and its first check found nothing; it then showed "No published pages yet" until "Check all pages again" was pressed. A first check that finds nothing is no longer kept, so the first visit to the SEO page checks the pages. Sandboxed installs were not affected.
14
+
3
15
  ## 0.1.3
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -32,6 +32,8 @@ export default defineConfig({
32
32
 
33
33
  `sandboxed` needs a sandbox runner: on Cloudflare it is the Worker Loader binding from `@emdash-cms/cloudflare`; on Node add `@emdash-cms/sandbox-workerd` and set `sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox"`. Without a runner, list the plugin under `plugins: [seo]` instead, which runs it in-process. Requires EmDash 1.2.0 or newer.
34
34
 
35
+ **On Cloudflare's Workers free plan** each request gets about 10 ms of CPU. EmDash's own saving and publishing already comes close to that, and every plugin run in the site's process adds a little, so a save or publish can now and then fail with "Worker exceeded resource limits"; trying again works. Workers Paid (30 seconds per request) does not have this problem.
36
+
35
37
  It asks for four permissions: read content, read bylines, read the content schema, and manage redirects. The registry lists a fifth, read redirects, which managing them includes. It makes no requests to any outside service.
36
38
 
37
39
  ## The SEO page
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@
7
7
  /** @type {import("emdash").PluginDescriptor} */
8
8
  const descriptor = Object.freeze({
9
9
  "id": "seo",
10
- "version": "0.1.3",
10
+ "version": "0.1.5",
11
11
  "format": "standard",
12
12
  "entrypoint": "@moorage/plugin-seo/sandbox",
13
13
  "capabilities": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "seo",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "declaredAccess": {
5
5
  "content": {
6
6
  "read": {}
package/dist/plugin.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  function e(e,t){try{return new URL(e,t)}catch{return null}}const t=`config`,n=[`WebPage`,`AboutPage`,`ContactPage`,`CollectionPage`,`ProfilePage`],r=[`Article`,`BlogPosting`,`NewsArticle`],i=[`Service`];function a(e){let t=e.toLowerCase();return[`posts`,`post`,`blog`].includes(t)?`BlogPosting`:[`news`,`articles`,`article`].includes(t)?`Article`:[`services`,`service`].includes(t)?`Service`:[`team`,`people`,`staff`,`authors`].includes(t)?`ProfilePage`:`WebPage`}const o={site:{type:`Organization`,sameAs:[],openingHours:[],areaServed:[]},collections:{},robots:{maxSnippet:-1,maxImagePreview:`large`,maxVideoPreview:-1,nosnippet:!1},jsonLdField:`json_ld`,breadcrumbs:!0,steps:{identity:!1,types:!1}};function s(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function c(e){if(typeof e!=`string`)return;let t=e.trim();return t===``?void 0:t}function l(t){let n=c(t),r=n?e(n)?.protocol:void 0;return r===`https:`||r===`http:`?n:void 0}function u(e,t){return typeof e==`number`&&Number.isInteger(e)&&e>=-1?e:t}function d(e){let t=[...n,...r,...i];return typeof e==`string`&&t.includes(e)?e:void 0}function f(e){if(!s(e))return;let t={};for(let n of[`streetAddress`,`addressLocality`,`addressRegion`,`postalCode`,`addressCountry`]){let r=c(e[n]);r&&(t[n]=r)}return Object.keys(t).length>0?t:void 0}function p(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){if(!s(n)||!Array.isArray(n.days))continue;let e=n.days.filter(e=>typeof e==`string`&&e!==``),r=c(n.opens),i=c(n.closes);e.length>0&&r&&i&&t.push({days:e,opens:r,closes:i})}return t}function m(e){if(!s(e))return{...o.site};let t=s(e.geo)?e.geo:void 0,n=t?.latitude,r=t?.longitude;return{type:c(e.type)??`Organization`,name:c(e.name),alternateName:c(e.alternateName),legalName:c(e.legalName),description:c(e.description),logo:l(e.logo),sameAs:Array.isArray(e.sameAs)?e.sameAs.map(l).filter(e=>e!==void 0):[],email:c(e.email),telephone:c(e.telephone),address:f(e.address),geo:typeof n==`number`&&typeof r==`number`?{latitude:n,longitude:r}:void 0,openingHours:p(e.openingHours),priceRange:c(e.priceRange),areaServed:Array.isArray(e.areaServed)?e.areaServed.map(c).filter(e=>e!==void 0):[],foundingDate:c(e.foundingDate),vatID:c(e.vatID),taxID:c(e.taxID),numberOfEmployees:typeof e.numberOfEmployees==`number`&&e.numberOfEmployees>0?Math.round(e.numberOfEmployees):void 0}}function h(e){if(!s(e))return o;let t={};if(s(e.collections))for(let[n,r]of Object.entries(e.collections))s(r)&&(t[n]={type:d(r.type),noindex:r.noindex===!0,...r.hiddenOk===!0?{hiddenOk:!0}:{}});let n=s(e.robots)?e.robots:{},r=n.maxImagePreview;return{site:m(e.site),collections:t,robots:{maxSnippet:u(n.maxSnippet,o.robots.maxSnippet),maxImagePreview:r===`none`||r===`standard`||r===`large`?r:o.robots.maxImagePreview,maxVideoPreview:u(n.maxVideoPreview,o.robots.maxVideoPreview),nosnippet:n.nosnippet===!0},jsonLdField:c(e.jsonLdField)??o.jsonLdField,breadcrumbs:e.breadcrumbs!==!1,steps:{identity:s(e.steps)&&e.steps.identity===!0,types:s(e.steps)&&e.steps.types===!0}}}const g=[`Monday`,`Tuesday`,`Wednesday`,`Thursday`,`Friday`,`Saturday`,`Sunday`],_=/^(\d{1,2})(?:[:.](\d{2}))?\s*[-–—]\s*(\d{1,2})(?:[:.](\d{2}))?$/;function v(e,t){let n=Number(e),r=Number(t??`0`);return n>24||r>59||n===24&&r!==0?null:`${String(n).padStart(2,`0`)}:${String(r).padStart(2,`0`)}`}function y(e,t){let n=t.trim();if(n===``||n.toLowerCase()===`closed`)return[];let r=n.split(/\s*[,;]\s*|\s+and\s+/i).filter(e=>e!==``);if(r.length>2)return null;let i=[];for(let t of r){let n=_.exec(t);if(!n)return null;let r=v(n[1]??``,n[2]),a=v(n[3]??``,n[4]);if(!r||!a)return null;i.push({days:[e],opens:r,closes:a})}return i}function b(e,t){return t.filter(t=>t.days.includes(e)).map(e=>`${e.opens}-${e.closes}`).join(`, `)}const x=[{value:`WebPage`,label:`Web page`,short:`Web page`},{value:`AboutPage`,label:`About page`,short:`About page`},{value:`ContactPage`,label:`Contact page`,short:`Contact page`},{value:`BlogPosting`,label:`Blog post`,short:`Blog post`},{value:`Article`,label:`Article`,short:`Article`},{value:`Service`,label:`Service`,short:`Service`},{value:`ProfilePage`,label:`Person or team member`,short:`Person`}];function S(e){return x.find(t=>t.value===e)?.short??e}const C=[301,302,307,308,410,451],w=2e3,T={empty:`No old address`,query:`Addresses with a ? are not supported`,external:`Points to another website`,noTarget:`No new address`,same:`Old and new address are the same`,code:`Unknown redirect type`,pattern:`Uses a pattern. Add it by hand under Manage > Redirects`,inactive:`Switched off in the export`,repeated:`Listed twice`,exists:`Already exists`,conflict:`Conflicts with an existing redirect`,loop:`Would send visitors in a circle`,tooMany:`Only the first ${w} rows are imported at once`};function E(e,t){let n=[],r=``,i=!1;for(let a=0;a<e.length;a+=1){let o=e[a];i?o===`"`&&e[a+1]===`"`?(r+=`"`,a+=1):o===`"`?i=!1:r+=o:o===`"`?i=!0:o===t?(n.push(r.trim()),r=``):r+=o}n.push(r.trim());let a=n[0]??``;return n.length===1&&/\S\s+\S/.test(a)?a.split(/\s+/):n}const D={source:`source`,origin:`source`,from:`source`,"old url":`source`,old:`source`,sources:`source`,target:`target`,destination:`target`,to:`target`,"new url":`target`,new:`target`,url_to:`target`,regex:`regex`,code:`code`,"http code":`code`,type:`code`,header_code:`code`,format:`format`,matching:`matching`,status:`status`,ignore:`ignore`,id:`id`};function O(e,t){let n=e.map(e=>D[e.toLowerCase()]),r=n.includes(`source`)&&n.includes(`target`);if(r){let e={};return n.forEach((t,n)=>{t&&e[t]===void 0&&(e[t]=n)}),{format:e.matching===void 0?e.format===void 0?e.regex===void 0?`plain`:`redirection`:`yoast`:`rankmath`,hasHeader:r,index:e}}let i=t.slice(0,20),a=i.map(e=>e[2]??``),o=i.map(e=>(e[3]??``).toLowerCase());return o.length>0&&o.every(e=>e===`plain`||e===`regex`)?{format:`yoast`,hasHeader:r,index:{source:0,target:1,code:2,format:3}}:a.length>0&&a.every(e=>e===`0`||e===`1`)?{format:`redirection`,hasHeader:r,index:{source:0,target:1,regex:2,code:3}}:{format:`plain`,hasHeader:r,index:{source:0,target:1,code:2}}}function k(t){let n=t.trim();if(n===``)return{reason:T.empty};if(/^https?:\/\//i.test(n)){let t=e(n);if(!t)return{reason:T.empty};n=`${t.pathname}${t.search}`}return n.includes(`?`)?{reason:T.query}:(n.startsWith(`/`)||(n=`/${n}`),n.length>1&&(n=n.replace(/\/+$/,``)),{path:n===``?`/`:n})}const ee=/[\\^$.*+?()[\]{}|]/;function te(e,t){let n=e.trim();if(n.startsWith(`^`)&&(n=n.slice(1)),n.endsWith(`$`)&&(n=n.slice(0,-1)),n.endsWith(`/?`)&&(n=n.slice(0,-2)),!ee.test(n))return{source:n,target:t};let r=/^(\/[^\\^$.*+?()[\]{}|]*\/)\(\.\*\)$/.exec(n);return r&&!/\$[2-9]/.test(t)?{source:`${r[1]}[...rest]`,target:t.replace(/\$1/g,`[...rest]`)}:null}function A(e){if(e===void 0||e.trim()===``)return 301;let t=Number(e.trim());return C.includes(t)?t:null}function j(t,n){let r=t.split(/\r?\n/),i=r.some(e=>e.includes(` `))?` `:`,`,a=r.map((e,t)=>({line:t+1,cells:E(e,i)})).filter(e=>e.cells.some(e=>e!==``)),o=a[0];if(!o)return{format:`plain`,ready:[],skipped:[]};let s=O(o.cells,a.map(e=>e.cells)),c=s.hasHeader?a.slice(1):a,l=(e,t)=>{let n=s.index[t];return n===void 0?void 0:e[n]},u=[],d=[],f=new Map(n.map(e=>[e.source,e.destination])),p=new Set;for(let{line:t,cells:n}of c){let r=l(n,`source`)??``,i=e=>d.push({line:t,source:r,reason:e});if(u.length>=w){i(T.tooMany);continue}if([`inactive`,`disabled`].includes((l(n,`status`)??`active`).toLowerCase())){i(T.inactive);continue}let a=A(l(n,`code`));if(a===null){i(T.code);continue}let o=r,s=l(n,`target`)??``,c=(l(n,`matching`)??`exact`).toLowerCase();if(l(n,`regex`)===`1`||(l(n,`format`)??``).toLowerCase()===`regex`||c===`regex`){let e=te(o,s);if(!e){i(T.pattern);continue}o=e.source,s=e.target}else if(c===`start`&&o.trim().endsWith(`/`))o=`${o.trim()}[...rest]`;else if(c!==`exact`){i(T.pattern);continue}let m=k(o);if(`reason`in m){i(m.reason);continue}let h=a===410||a===451,g=``;if(!h){if(s.trim()===``){i(T.noTarget);continue}let t=/^https?:\/\//i.test(s.trim()),n=/^https?:\/\//i.test(r.trim())?e(r.trim())?.host:void 0;if(t&&n!==e(s.trim())?.host){i(T.external);continue}let a=k(s);if(`reason`in a){i(a.reason===T.empty?T.noTarget:a.reason);continue}if(g=a.path,g===m.path){i(T.same);continue}}if(p.has(m.path)){i(T.repeated);continue}let _=f.get(m.path);if(_!==void 0){i(_===g?T.exists:T.conflict);continue}p.add(m.path),u.push({line:t,source:m.path,destination:g,type:a})}let m=new Map(f);for(let e of u)m.set(e.source,e.destination);let h=[];for(let e of u){let t=e.destination,n=!1;for(let r=0;r<20&&t!==``&&m.has(t);r+=1)if(t=m.get(t)??``,t===e.source){n=!0;break}n?d.push({line:e.line,source:e.source,reason:T.loop}):h.push({source:e.source,destination:e.destination,type:e.type})}return{format:s.format,ready:h,skipped:d}}function ne(e){let t=e=>/[",\n]/.test(e)?`"${e.replace(/"/g,`""`)}"`:e;return[`source,target,code`,...e.map(e=>`${t(e.source)},${t(e.destination)},${e.type}`)].join(`
2
- `)}const M=`redirects`,N=`redirects-import`,re={redirection:`Redirection`,yoast:`Yoast SEO Premium`,rankmath:`Rank Math`,plain:`a plain list`};async function ie(e){let t=await e.kv.get(M);return t&&Array.isArray(t.rows)?t:null}async function ae(e){let t=[],n;for(let r=0;r<5;r+=1){let r=await e.redirects?.list({limit:100,cursor:n});if(!r||(t.push(...r.items),n=r.hasMore?r.cursor:void 0,!n))break}return t}async function oe(e,t){let n=j(t,await ae(e)),r={status:`checked`,format:n.format,rows:n.ready,skipped:n.skipped,cursor:0,added:0,group:`Imported ${new Date().toISOString().slice(0,16).replace(`T`,` `)}`};return await e.kv.set(M,r),r}function se(e){let t=e instanceof Error?e.message:String(e);return/already exists/i.test(t)?{kind:`exists`,message:t}:/invalid source pattern|must be different|destination|path traversal|loop/i.test(t)?{kind:`invalid`,message:t}:{kind:`stop`,message:t}}async function ce(e,t){let n={...t,status:`adding`,skipped:[...t.skipped]},r=async()=>{try{return await e.kv.set(M,n),!0}catch{return!1}};if(t.status===`checked`)try{await e.cron?.schedule(N,{schedule:`* * * * *`})}catch(t){e.log.warn(`could not schedule background import`,{error:String(t)})}let i=0,a=n.cursor;for(;n.cursor<n.rows.length;){let t=n.rows[n.cursor];if(!t)break;try{await e.redirects?.create?.({source:t.source,destination:t.destination,type:t.type,groupName:n.group}),n.added+=1}catch(e){let{kind:r,message:i}=se(e);if(r===`stop`){if(n.cursor!==a||(n.stalls=(n.stalls??0)+1,n.stalls<3))break;n.skipped.push({line:0,source:t.source,reason:`EmDash could not add it: ${i}`})}else r===`exists`?n.added+=1:n.skipped.push({line:0,source:t.source,reason:`EmDash refused it: ${i}`})}if(n.cursor+=1,n.stalls=0,i+=1,i>=4){if(!await r())return n;i=0}}if(n.cursor>=n.rows.length){n.status=`done`;try{await e.cron?.cancel(N)}catch{}}return await r(),n}async function le(e,t){let n=await e.redirects?.list({limit:100,group:t.group}),r=n?.items??[],i=r.length;for(let t of r)try{let n=await e.redirects?.get(t.id);n&&await e.redirects?.delete?.(t.id,{_rev:n._rev}),--i}catch{break}if(i===0&&!n?.hasMore){try{await e.cron?.cancel(N)}catch{}return await e.kv.delete(M),0}return i}async function ue(e){let t=[],n;for(let r=0;r<5;r+=1){let r=await e.redirects?.list({limit:100,cursor:n});if(!r||(t.push(...r.items),n=r.hasMore?r.cursor:void 0,!n))break}return{text:ne(t),count:t.length}}function P(e,t){return`${e} ${t}${e===1?``:`s`}`}function de(e){if(e.length===0)return[];let t=[{type:`header`,text:`Skipped (${e.length})`},{type:`context`,text:`Skipped rows are not added. Copy any you still need and add them by hand under Manage > Redirects, which also accepts patterns.`},{type:`table`,page_action_id:`skipped_table`,columns:[{key:`line`,label:`Row`},{key:`source`,label:`Old address`},{key:`reason`,label:`Why`}],rows:e.slice(0,50).map(e=>({line:e.line>0?String(e.line):``,source:e.source,reason:e.reason}))}];return e.length>50&&t.push({type:`context`,text:`And ${e.length-50} more.`}),t}const fe=[{value:`301`,label:`Permanent`},{value:`302`,label:`Temporary, the old address will come back`},{value:`410`,label:`Page removed on purpose, no new address`}];function pe(e){return e===301||e===308?`Permanent`:e===302||e===307?`Temporary`:e===410?`Deleted on purpose`:String(e)}async function me(e){let t=await e.redirects?.list({limit:20});return{rows:(t?.items??[]).map(({id:e,source:t,destination:n,type:r,hits:i})=>({id:e,source:t,destination:n,type:r,hits:i})),more:t?.hasMore??!1}}function he(e){let t=e instanceof Error?e.message:String(e);return/already exists/i.test(t)?`There is already a redirect from that old address. Use Change in the list below.`:/must be different/i.test(t)?`The old and new address are the same.`:/loop/i.test(t)?`That would send visitors in a circle.`:/changed since/i.test(t)?`That redirect was just changed elsewhere. Try again.`:`EmDash could not save that redirect. Check both addresses start with /.`}function ge(e,t,n){let r=j(ne([{source:e,destination:t,type:Number(n)||301}]),[]);return r.ready[0]??r.skipped[0]?.reason??`Type the old address first.`}async function _e(e,t,n,r){let i=ge(t,n,r);if(typeof i==`string`)return`${i}.`;try{return await e.redirects?.create?.(i),null}catch(e){return he(e)}}async function ve(e,t,n,r,i){let a=ge(n,r,i);if(typeof a==`string`)return`${a}.`;try{let n=await e.redirects?.get(t);return n?(await e.redirects?.update?.(t,{...a,_rev:n._rev}),null):`That redirect no longer exists.`}catch(e){return he(e)}}async function ye(e,t){try{let n=await e.redirects?.get(t);return n&&await e.redirects?.delete?.(t,{_rev:n._rev}),null}catch(e){return he(e)}}function be(e){if(!e)return[{type:`context`,text:`Paste a list exported from Redirection, Yoast SEO Premium or Rank Math, or any list of old address, new address. Nothing is added until you confirm.`},{type:`form`,block_id:`redirects`,fields:[{type:`text_input`,action_id:`list`,label:`Your list, one redirect per line`,multiline:!0,placeholder:`/old-page,/new-page`,initial_value:``}],submit:{label:`Check list`,action_id:`check_redirects`}}];if(e.status===`checked`){let t=e.rows.length;return[{type:`banner`,variant:t===0?`alert`:`default`,title:`${t} ready, ${e.skipped.length} skipped`,description:t===0?`Nothing in this list can be added. See why below.`:`Read as ${re[e.format]}. Nothing has been added yet. Existing redirects are never changed.`},{type:`actions`,elements:[...t>0?[{type:`button`,action_id:`add_redirects`,label:`Add ${P(t,`redirect`)}`,style:`primary`,confirm:{title:`Add ${P(t,`redirect`)}?`,text:`Visitors to these old addresses will be sent to the new ones. You can remove them all again afterwards.`,confirm:`Add`,deny:`Cancel`}}]:[],{type:`button`,action_id:`reset_redirects`,label:`Start again`}]},...de(e.skipped)]}return e.status===`adding`?[{type:`meter`,label:`${e.added} of ${e.rows.length} added`,value:e.cursor,max:e.rows.length},{type:`context`,text:`The rest are added in the background, a few each minute. You can leave this page.`},{type:`actions`,elements:[{type:`button`,action_id:`more_redirects`,label:`Add more now`,style:`primary`},{type:`button`,action_id:`stop_redirects`,label:`Stop here`}]}]:[{type:`banner`,title:`${P(e.added,`redirect`)} added from your list`,description:`They are labelled "${e.group}".`},{type:`actions`,elements:[{type:`button`,action_id:`reset_redirects`,label:`Import another list`},...e.added>0?[{type:`button`,action_id:`remove_redirects`,label:`Remove the ${e.added} just added`,confirm:{title:`Remove these redirects?`,text:`Only the redirects this import added are removed.`,confirm:`Remove`,deny:`Cancel`,style:`danger`}}]:[]]},...de(e.skipped)]}function xe(e,t){let n=e?String(e.type):`301`;return{type:`form`,block_id:e?`edit_redirect_${e.id}`:`add_one_${t}`,fields:[{type:`text_input`,action_id:`source`,label:`Redirect from`,placeholder:`/old-page`,initial_value:e?.source??``},{type:`text_input`,action_id:`destination`,label:`Redirect to`,placeholder:`/new-page`,initial_value:e?.destination??``,condition:{field:`code`,neq:`410`}},{type:`select`,action_id:`code`,label:`Kind`,options:fe.some(e=>e.value===n)?fe:[...fe,{value:n,label:`${pe(Number(n))} (${n})`}],initial_value:n}],submit:e?{label:`Save changes`,action_id:`save_redirect:${e.id}`}:{label:`Add redirect`,action_id:`add_redirect`}}}function Se(e){let{state:t,rows:n,editing:r}=e,i=r?void 0:e.panel??(t?`import`:void 0),a=n[0]?.id??`first`,o=[{type:`context`,text:`Send visitors from an old address to a new one. Pages you rename in EmDash are redirected for you.`},{type:`actions`,elements:[{type:`button`,action_id:`open_add`,label:`Add a redirect`,...i===void 0&&!r?{style:`primary`}:{}},{type:`button`,action_id:`open_import`,label:`Import from WordPress`},{type:`button`,action_id:`export_redirects`,label:`Export`},...e.managerUrl?[{type:`link`,label:`Full Redirects screen`,target:{kind:`external`,url:e.managerUrl}}]:[]]}],s={type:`actions`,elements:[{type:`button`,action_id:`close_panel`,label:`Close`}]},c=e.problem?[{type:`banner`,variant:`error`,title:e.problem}]:[];return r?o.push({type:`header`,text:`Change a redirect`},...c,xe(r,a),s,{type:`divider`}):i===`add`?o.push({type:`header`,text:`Add a redirect`},...c,xe(void 0,a),s,{type:`divider`}):i===`import`?(o.push({type:`header`,text:`Import from WordPress`},...be(t)),t||o.push(s),o.push({type:`divider`})):i===`export`&&o.push({type:`header`,text:`Export`},{type:`context`,text:`Copy this list to keep a backup or to paste into a spreadsheet. It can be imported again here.`},{type:`code`,code:e.exported??``,language:`bash`},s,{type:`divider`}),n.length===0?o.push({type:`empty`,title:`No redirects yet`,description:`Add one, or import a list from your old site.`,size:`sm`}):(o.push({type:`table`,page_action_id:`redirects_table`,columns:[{key:`source`,label:`Redirect from`},{key:`destination`,label:`Redirect to`},{key:`kind`,label:`Kind`},{key:`hits`,label:`Visits`,format:`number`},{key:`edit`,label:``,format:`element`},{key:`remove`,label:``,format:`element`}],rows:n.map(e=>({source:e.source,destination:e.destination||`(none)`,kind:pe(e.type),hits:e.hits,edit:{type:`button`,action_id:`edit_redirect`,label:`Change`,value:e.id},remove:{type:`button`,action_id:`remove_redirect`,label:`Remove`,value:e.id,confirm:{title:`Remove this redirect?`,text:`Visitors to ${e.source} will see a not-found page.`,confirm:`Remove`,deny:`Keep`,style:`danger`}}}))}),e.more&&o.push({type:`context`,text:`Showing the newest ${n.length}. The rest are in the full Redirects screen.`})),o}async function Ce(e,t){let n={...t,status:`done`};try{await e.cron?.cancel(N)}catch{}return await e.kv.set(M,n),n}const we=new Set("iIl.,:;|!'`jtfr ()[]".split(``)),Te=new Set(`mwMWQO@%`.split(``));function F(e){let t=0;for(let n of e)we.has(n)?t+=5:Te.has(n)?t+=15:n>=`A`&&n<=`Z`?t+=12:t+=9.5;return Math.round(t)}function Ee(e){if(F(e)<=580)return e;let t=e;for(;t.length>1&&F(`${t}…`)>580;)t=t.slice(0,-1);return`${t.trimEnd()}…`}function De(e){return e.toLowerCase().replace(/\s+/g,` `).trim()}const Oe=new Set([`home`,`homepage`,`home page`,`index`,`untitled`,`welcome`]),ke=new Set([`_type`,`_key`,`id`,`slug`,`url`,`href`,`src`,`style`,`icon`]);function Ae(e){let t={textLength:0,imagesWithoutAlt:0,hasImage:!1},n=(e,r,i)=>{if(i>12)return;if(typeof e==`string`){!ke.has(r)&&!/^(https?:)?\//.test(e)&&(t.textLength+=e.trim().length);return}if(Array.isArray(e)){for(let t of e)n(t,r,i+1);return}if(typeof e!=`object`||!e)return;let a=e;(a._type===`image`||typeof a.src==`string`&&`alt`in a)&&(t.hasImage=!0,(typeof a.alt!=`string`||a.alt.trim()===``)&&(t.imagesWithoutAlt+=1));for(let[e,t]of Object.entries(a))n(t,e,i+1)};for(let[t,r]of Object.entries(e))t!==`json_ld`&&n(r,t,0);return t}const I={id:`no-social-image`,level:`look`,message:`This page has no picture for shared links, unless your site has a default sharing image.`,fix:`Add an image in the SEO panel. If your theme adds a picture itself, this clears once the page has been viewed.`};function L(e){return typeof e.excerpt==`string`?e.excerpt.trim():``}function je(t){let n=[],{seo:r}=t,i=Ae(t.data),a=r?.title?.trim()||t.title.trim();if(a===``&&n.push({id:`no-title`,level:`fix`,message:`This page has no title.`,fix:`Give the page a title.`}),Oe.has(a.toLowerCase())&&n.push({id:`generic-title`,level:`fix`,message:`The title is just "${a}", which is what Google shows as the headline.`,fix:`Put your name and what you do in SEO Title, in the page's SEO panel.`}),r&&!r.description?.trim()&&L(t.data)===``&&n.push({id:`no-description`,level:`fix`,message:`No search description, so Google writes its own summary of this page.`,fix:`Fill in Meta Description in the page's SEO panel.`}),t.duplicateTitle&&a!==``&&n.push({id:`duplicate-title`,level:`fix`,message:`Another page has the same title, so Google cannot tell them apart.`,fix:`Give this page its own title in the SEO panel.`}),r?.canonical&&t.slug){let i=e(r.canonical,`${t.siteUrl.replace(/\/+$/,``)}/`),a=i?.pathname.replace(/\/+$/,``).split(`/`).pop(),o=[`home`,`index`].includes(t.slug)&&(a??``)===``;i&&a!==t.slug&&!o&&n.push({id:`canonical-elsewhere`,level:`fix`,message:`This page tells Google to show a different page instead (canonical).`,fix:`Clear the canonical address in the SEO panel unless that is intended.`})}if(a!==``&&F(a)>580&&n.push({id:`title-long`,level:`look`,message:`The end of this title may be cut off in Google.`,fix:`Shorten the title, or put the important words first.`}),r&&!r.image&&!i.hasImage&&n.push(I),t.isArticle&&t.authorCount===0&&n.push({id:`no-author`,level:`look`,message:`This article has no author.`,fix:`Add a byline to the article.`}),i.imagesWithoutAlt>0){let e=i.imagesWithoutAlt;n.push({id:`images-without-alt`,level:`look`,message:`${e} ${e===1?`picture has`:`pictures have`} no description for screen readers.`,fix:`Add alt text to each picture.`})}return i.textLength<80&&n.push({id:`nearly-empty`,level:`look`,message:`This page has almost no text.`,fix:`Add some content, or hide the page from search.`}),n}function Me(e,t){return t?`hidden`:e.some(e=>e.level===`fix`)?`fix`:e.length>0?`look`:`ready`}const Ne=/^[0-9A-HJKMNP-TV-Z]{26}(\.[A-Za-z0-9]+)?$/i;function R(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Pe(e){return typeof e==`number`&&Number.isFinite(e)&&e>=1?Math.round(e):void 0}function Fe(e){if(!e.startsWith(`/_emdash/api/media/file/`))return;let t=e.slice(24);try{t=decodeURIComponent(t)}catch{return}return t!==``&&!t.includes(`/`)?t:void 0}function Ie(t,n){let r=e(t,`${n.replace(/\/+$/,``)}/`);if(!r||!/^https?:$/.test(r.protocol)||r.search!==``)return[];let{pathname:i}=r;if(i===`/_image`||i.includes(`/cdn-cgi/image/`))return[];let a=Fe(i);if(a)return[`file:${a}`];let o=[`url:${r.origin}${i}`],s=i.slice(1);return Ne.test(s)&&o.push(`file:${s}`),o}function Le(e){if(typeof e!=`string`||e===``)return[];if(e.startsWith(`/`)){let t=e.includes(`?`)?void 0:Fe(e);return t?[`file:${t}`]:[]}return/^https?:\/\//i.test(e)?Ie(e,`http://localhost`).filter(e=>e.startsWith(`url:`)):[]}function Re(e){return typeof e==`string`&&e!==``&&!e.includes(`/`)?[`file:${e}`]:[]}function ze(e){let t=Pe(e.width),n=Pe(e.height);if(t===void 0||n===void 0||typeof e.mimeType==`string`&&!e.mimeType.startsWith(`image/`))return;let r=[];if(e._type===`image`&&R(e.asset))r.push(...Re(e.asset._ref),...Le(e.asset.url));else if(typeof e.id==`string`){if(e.provider===void 0||e.provider===`local`){let t=R(e.meta)?e.meta:{};r.push(...Re(t.storageKey),...Re(e.id))}r.push(...Le(e.src))}let i=[...new Set(r)];return i.length>0?{keys:i,width:t,height:n}:void 0}function Be(e){let t=[],n=new Set,r=(e,i)=>{if(t.length>=10||i>12)return;if(Array.isArray(e)){for(let t of e)r(t,i+1);return}if(!R(e))return;let a=ze(e);if(a&&!a.keys.some(e=>n.has(e))){for(let e of a.keys)n.add(e);t.push(a)}for(let t of Object.values(e))r(t,i+1)};for(let[t,n]of Object.entries(e))t!==`json_ld`&&r(n,0);return t.slice(0,10)}function Ve(e,t,n){if(!e||e.length===0)return;let r=Ie(t,n);if(r.length===0)return;let i=e.find(e=>e.keys.some(e=>r.includes(e)));return i?{width:i.width,height:i.height}:void 0}const He=16*1024;function z(e,t){return`${e}:${t}`}function B(e){return e.storage.entries}function Ue(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&`@type`in e}function We(e){let t=e;if(typeof e==`string`){if(e.trim()===``||e.length>He)return[];try{t=JSON.parse(e)}catch{return[]}}let n=(Array.isArray(t)?t:[t]).filter(Ue);return JSON.stringify(n).length>He?[]:n}function Ge(e){let{title:t,name:n}=e.data;return typeof t==`string`?t:typeof n==`string`?n:``}function Ke(e){let{collection:t,entry:n,authors:i,previous:o,config:s,siteUrl:c}=e,l=s.collections[t],u=o?.type??l?.type??a(t),d=n.seo?.title?.trim()||Ge(n),f=n.status===`published`,p=f&&e.hasPages,m=e.hasPages?je({slug:n.slug,data:n.data,seo:n.seo,title:Ge(n),siteUrl:c,isArticle:r.includes(u),authorCount:i.length,duplicateTitle:e.duplicateTitle}):[],h=m.some(e=>e.id===I.id),g=h&&o?.imageSeen===!0,_=g?m.filter(e=>e.id!==I.id):m,v=e.hasPages?Be(n.data):[];return{v:1,publishedAt:n.publishedAt,updatedAt:n.updatedAt,authors:i,...o?.type?{type:o.type}:{},custom:We(n.data[s.jsonLdField]),...v.length>0?{images:v}:{},needsImage:h,...g?{imageSeen:g}:{},collection:t,entryId:n.id,title:d,status:e.hasPages?f?Me(_,n.seo?.noIndex===!0||l?.noindex===!0):`draft`:`skip`,titleKey:p?De(d):``,issues:_,checkedAt:new Date().toISOString()}}function qe(e,t){return e.status===`hidden`||e.status===`draft`||e.status===`skip`?e.status:Me(t,!1)}function Je(e,t){let n=e.issues??[],r=n.some(e=>e.id===I.id);if(!(e.needsImage??r)||e.imageSeen===!0===t&&r!==t)return null;let i=n.filter(e=>e.id!==I.id),a=t?i:[...i,I],{imageSeen:o,...s}=e;return{...s,needsImage:!0,...t?{imageSeen:!0}:{},issues:a,status:qe(e,a)}}async function Ye(e,t,n){return t===``?[]:(await B(e).query({where:{titleKey:t},limit:5})).items.filter(e=>e.id!==n)}function V(e,t){let n=(e.issues??[]).filter(e=>e.id!==`duplicate-title`),r=t?[...n,{id:`duplicate-title`,level:`fix`,message:`Another page has the same title, so Google cannot tell them apart.`,fix:`Give this page its own title in the SEO panel.`}]:n;return{...e,issues:r,status:qe(e,r)}}async function Xe(e,t,n,r){return(await Ze(e,t,n,r))?.record??null}async function Ze(e,t,n,r){let i=await e.content?.get(t,n);if(!i)return null;let a=B(e),o=z(t,n),[s,c,l]=await Promise.all([e.bylines?.getEntriesBylines(t,[n])??[],a.get(o),e.schema?.getCollection(t)]),u=Ke({collection:t,entry:i,authors:(s[0]?.bylines??[]).map(({byline:e})=>({name:e.displayName,...e.websiteUrl?{url:e.websiteUrl}:{}})),previous:c,config:r,siteUrl:e.site.url,duplicateTitle:!1,hasPages:l?l.routable&&!l.hidden:!0}),d=await Ye(e,u.titleKey??``,o),f=V(u,d.length>0),p=[{id:o,data:f}];for(let e of d)e.data.issues?.some(e=>e.id===`duplicate-title`)||p.push({id:e.id,data:V(e.data,!0)});let m=c?.titleKey??``;if(m!==``&&m!==f.titleKey){let t=await Ye(e,m,o),n=t.length===1?t[0]:void 0;n&&p.push({id:n.id,data:V(n.data,!1)})}return await a.putMany(p),{record:f,entry:i}}async function Qe(e,t,n){let r=B(e),i=z(t,n),a=await r.get(i);await r.delete(i);let o=await Ye(e,a?.titleKey??``,i),s=o.length===1?o[0]:void 0;s&&await r.put(s.id,V(s.data,!1))}const $e=`scan`,et=`stats`,H=`seo-scan`;async function tt(e){return e.kv.get($e)}async function nt(e,t,n){let r={status:`running`,collections:t,phase:`entries`,checked:0,tally:{checked:0,fix:0,hidden:0,rows:[],more:!1},startedAt:new Date().toISOString()};try{await e.cron?.schedule(H,{schedule:`* * * * *`})}catch(t){e.log.warn(`could not schedule background check`,{error:String(t)})}return ot(e,r,n)}async function rt(e,t,n){let r=t.collections[0];if(r===void 0){t.phase=`titles`;return}let i=await e.content?.list(r,{limit:20,cursor:t.cursor,where:{status:`published`}}),a=i?.items??[];if(a.length>0){let o=B(e),s=a.map(e=>e.id),[c,l]=await Promise.all([e.bylines?.getEntriesBylines(r,s)??[],o.getMany(s.map(e=>z(r,e)))]),u=new Map(c.map(e=>[e.entryId,e.bylines])),d=a.map(t=>{let i=l.get(z(r,t.id))??null;return{id:z(r,t.id),data:Ke({hasPages:!0,collection:r,entry:t,authors:(u.get(t.id)??[]).map(({byline:e})=>({name:e.displayName,...e.websiteUrl?{url:e.websiteUrl}:{}})),previous:i,config:n,siteUrl:e.site.url,duplicateTitle:i?.issues?.some(e=>e.id===`duplicate-title`)??!1})}});if(t.cursor===void 0&&t.checked===0&&!i?.hasMore&&t.collections.length===1){let e=new Map;for(let{data:t}of d)t.titleKey&&e.set(t.titleKey,(e.get(t.titleKey)??0)+1);for(let t of d)t.data=V(t.data,(e.get(t.data.titleKey??``)??0)>1);t.status=`done`,t.finishedAt=new Date().toISOString()}await o.putMany(d);for(let{data:e}of d)at(t.tally,e,1);t.checked+=a.length}i?.hasMore&&i.cursor?t.cursor=i.cursor:(t.cursor=void 0,t.collections=t.collections.slice(1),t.collections.length===0&&t.status!==`done`&&(t.phase=`titles`))}async function it(e,t){let n=B(e),r=await n.query({orderBy:{titleKey:`asc`},limit:100,cursor:t.titleCursor}),i=r.items.filter(e=>(e.data.titleKey??``)!==``),a=new Map,o=(e,n,r)=>{if((n.issues?.some(e=>e.id===`duplicate-title`)??!1)===r)return;let i=V(n,r);a.set(e,i),at(t.tally,n,-1),at(t.tally,i,1)},s=t.lastTitle;for(let e=0;e<i.length;e+=1){let t=i[e];if(!t)continue;let r=t.data.titleKey??``,a=i[e+1],c=s?.key===r,l=a?.data.titleKey===r;if(c&&s&&!s.flagged&&e===0){let e=await n.get(s.id);e&&o(s.id,e,!0)}let u=c||l;o(t.id,t.data,u),s={key:r,id:t.id,flagged:u}}a.size>0&&await n.putMany([...a].map(([e,t])=>({id:e,data:t}))),t.lastTitle=s,r.hasMore&&r.cursor?t.titleCursor=r.cursor:(t.status=`done`,t.finishedAt=new Date().toISOString(),t.titleCursor=void 0,t.lastTitle=void 0)}function U(e,t=`fix`){return{collection:e.collection??``,id:e.entryId??``,title:e.title||`(no title)`,issues:(e.issues??[]).filter(e=>e.level===t).map(e=>e.message)}}function at(e,t,n){if(!(!t.status||t.status===`draft`||t.status===`skip`)){if(e.checked+=n,t.status===`hidden`){e.hidden+=n;let r=e.hiddenTitles??[],i=t.title||`(no title)`;e.hiddenTitles=n===1?[...r,i].slice(0,10):r.filter(e=>e!==i)}if(t.status===`look`){let r=e.look??[];e.look=n===1?[...r,U(t,`look`)].slice(0,10):r.filter(e=>e.id!==t.entryId)}t.status===`fix`&&(e.fix+=n,n===1?e.rows.length<10?e.rows.push(U(t)):e.more=!0:e.rows=e.rows.filter(e=>e.id!==t.entryId))}}async function ot(e,t,n){let r={...t,collections:[...t.collections],tally:{...t.tally,rows:[...t.tally.rows]}};if(r.status===`done`)return r;if(r.phase===`entries`){for(let t=0;t<5&&r.phase===`entries`;t+=1){let t=r.checked;if(await rt(e,r,n),r.checked!==t||r.status===`done`)break}r.phase===`titles`&&r.checked===0&&(r.status=`done`,r.finishedAt=new Date().toISOString())}else await it(e,r);if(r.status===`done`)try{await e.cron?.cancel(H)}catch{}return await e.kv.set($e,r),r}async function st(e){let t=B(e),[n,r,i,a,o]=await Promise.all([t.count({status:{in:[`ready`,`look`,`fix`,`hidden`]}}),t.count({status:`fix`}),t.query({where:{status:`fix`},limit:10}),t.query({where:{status:`look`},limit:10}),t.query({where:{status:`hidden`},limit:10})]);return{checked:n,fix:r,hidden:o.hasMore?11:o.items.length,rows:i.items.map(({data:e})=>U(e)),more:i.hasMore,look:a.items.map(({data:e})=>U(e,`look`)),hiddenTitles:o.items.map(({data:e})=>e.title||`(no title)`)}}const W={overview:0,redirects:1,identity:2,types:2},G=[{value:`FoodEstablishment`,label:`Restaurant, cafe or bakery`},{value:`Store`,label:`Shop`},{value:`MedicalBusiness`,label:`Health and medical`},{value:`Dentist`,label:`Dentist`},{value:`LegalService`,label:`Legal`},{value:`FinancialService`,label:`Financial`},{value:`HomeAndConstructionBusiness`,label:`Home services (plumber, electrician, builder)`},{value:`HealthAndBeautyBusiness`,label:`Beauty and hair`},{value:`SportsActivityLocation`,label:`Fitness`},{value:`LodgingBusiness`,label:`Hotel or lodging`},{value:`AutomotiveBusiness`,label:`Automotive`},{value:`ProfessionalService`,label:`Professional service`},{value:`LocalBusiness`,label:`Other`}],ct=[``,`$`,`$$`,`$$$`,`$$$$`],lt=[{id:`facebook`,label:`Facebook`,hosts:[`www.facebook.com`,`facebook.com`,`fb.com`]},{id:`instagram`,label:`Instagram`,hosts:[`www.instagram.com`,`instagram.com`]},{id:`linkedin`,label:`LinkedIn`,hosts:[`www.linkedin.com`,`linkedin.com`]},{id:`x`,label:`X (Twitter)`,hosts:[`x.com`,`www.x.com`,`twitter.com`,`www.twitter.com`]},{id:`youtube`,label:`YouTube`,hosts:[`www.youtube.com`,`youtube.com`]}];function ut(t){let n={},r=[];for(let i of t){let t=e(i)?.host??``,a=lt.find(e=>e.hosts.includes(t));a&&!(a.id in n)?n[a.id]=i:r.push(i)}return{named:n,other:r}}const dt={needed:`needed`,worth:`worth adding`,optional:`optional`};function K(e,t,n){let r=n.filter(e=>!!e);return r.length>0?`${e}: ${r.join(`, `)}`:`${e} (${dt[t]}, not added yet)`}function ft(e){return g.map(t=>{let n=b(t,e.openingHours).replace(`, `,` and `);return n===``?void 0:`${t.slice(0,3)} ${n}`}).filter(e=>e!==void 0)}function pt(e){let{site:t}=e.config,n=q(t),r=[];return n===`local`?((!t.address?.streetAddress||!t.address.addressLocality)&&r.push({what:`Address`,why:`Without it Google cannot treat you as a local business.`}),t.openingHours.length===0&&r.push({what:`Opening hours`,why:`Google cannot show when you are open.`}),t.telephone||r.push({what:`Phone number`,why:`Customers cannot call you from Google.`})):(n===`organization`&&!t.logo&&r.push({what:`Logo`,why:`Google shows it beside your name.`}),t.sameAs.length===0&&r.push({what:`Profiles elsewhere`,why:`They help Google connect your other pages to this site.`})),r}function q(e){return e.type===`Person`?`person`:e.type===`Organization`?`organization`:`local`}function mt(t,n){let r=e(t);if(!(!r||r.protocol!==`https:`&&r.protocol!==`http:`))return`${r.origin}${n}`}function ht(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function J(e,t,n=``){let r=e[t];return typeof r==`string`?r.trim():n}function gt(e,t,n){let r=e[t];return typeof r==`boolean`?r:n}function _t(e){return e.split(/\r?\n/).map(e=>e.trim()).filter(e=>e!==``)}async function vt(e){let[n,r,i]=await Promise.all([e.kv.list(),e.schema?.listCollections()??[],me(e)]),a=new Map(n.map(e=>[e.key,e.value])),o=a.get(M);return{config:h(a.get(t)),collections:r.filter(e=>e.routable&&!e.hidden),redirects:o&&Array.isArray(o.rows)?o:null,redirectRows:i.rows,moreRedirects:i.more,scan:a.get($e)??null,stats:a.get(et)??null}}async function yt(e,t){let n=await st(e);return JSON.stringify(n)!==JSON.stringify(t.stats)&&await e.kv.set(et,n),{...t,stats:n}}function bt(e){return e.stats??(e.scan?.status===`done`?e.scan.tally:null)}function xt(e){let{scan:t}=e,n=bt(e);if(!t||t.status===`running`)return[{type:`banner`,title:`Checking your pages`,description:t?`${t.checked} checked so far. The rest are checked in the background.`:`This starts by itself and takes about a minute.`}];if(!n)return[];let r=n.checked-n.fix-n.hidden;return[{type:`stats`,items:[{label:`Pages checked`,value:n.checked},{label:`Nothing to fix`,value:r},{label:`To fix`,value:n.fix}]}]}function St(e){let{scan:t}=e,n=bt(e);if(!n||!t||t.status===`running`)return[];let r=[];n.checked===0?r.push({type:`empty`,title:`No published pages yet`,size:`sm`}):n.fix===0?r.push({type:`empty`,title:`No pages to fix. Nice work.`,size:`sm`}):(r.push({type:`header`,text:`To fix`},{type:`table`,page_action_id:`fix_table`,columns:[{key:`page`,label:`Page`},{key:`missing`,label:`What's missing`},{key:`open`,label:``,format:`element`}],rows:n.rows.map(e=>({page:e.title,missing:e.issues.join(` `),open:{type:`link`,label:`Open page`,target:{kind:`content`,collection:e.collection,id:e.id}}}))}),n.more&&r.push({type:`context`,text:`Showing the first 10. Fix these and the next ones appear.`}));let i=n.look??[];if(i.length>0&&r.push({type:`accordion`,label:`Worth a look (${i.length}${i.length===10?`+`:``})`,blocks:[{type:`context`,text:`Nothing is wrong with these pages. These are improvements for when you have time.`},{type:`table`,page_action_id:`look_table`,columns:[{key:`page`,label:`Page`},{key:`advice`,label:`Could be better`},{key:`open`,label:``,format:`element`}],rows:i.map(e=>({page:e.title,advice:e.issues.join(` `),open:{type:`link`,label:`Open page`,target:{kind:`content`,collection:e.collection,id:e.id}}}))}]}),n.hidden>0){let e=(n.hiddenTitles??[]).join(`, `);r.push({type:`context`,text:`${n.hidden>10?`More than 10`:n.hidden} ${n.hidden===1?`page is`:`pages are`} hidden from Google and not counted${e?`: ${e}`:``}. To show one again, open it and switch off "Hide from search engines" in its SEO panel.`})}if(t.finishedAt){let e=new Date(t.finishedAt).toLocaleDateString(`en-GB`,{day:`numeric`,month:`short`});r.push({type:`context`,text:`Each page is checked again when it is saved. Last full check: ${e}.`})}return r}function Ct(e){if(!e.scan)return[];let t=e.scan.status===`running`;return[{type:`actions`,elements:[{type:`button`,action_id:t?`check_more`:`check_pages`,label:t?`Update`:`Check all pages again`}]}]}function wt(e){return e.collections.filter(t=>{let n=e.config.collections[t.slug];return n?.noindex===!0&&n.hiddenOk!==!0})}function Tt(e,t){let n=wt(e);if(n.length===0)return[];let r=[{type:`banner`,variant:`alert`,title:`Some pages are hidden from Google`,description:`Every page in ${n.map(e=>e.label).join(`, `)} is hidden from search engines. If that is a leftover from building the site, switch it back on in the Settings tab, under Content types.`}];return t&&r.push({type:`actions`,elements:[{type:`button`,action_id:`hidden_ok`,label:`This is intended`}]}),r}function Et(e,t){let{steps:n,site:r}=e.config;return n.identity?[]:[{type:`banner`,title:`Tell Google who this site is for`,description:`Google can then show your name and details beside your pages. It takes a minute.`},{type:`accordion`,label:`Set this up (1 minute)`,default_open:!0,blocks:[{type:`form`,block_id:`setup_identity`,fields:[{type:`radio`,action_id:`kind`,label:`This site is for`,options:[{label:`A local business customers visit (shop, cafe, clinic)`,value:`local`},{label:`A company or organisation`,value:`organization`},{label:`A person`,value:`person`}]},{type:`select`,action_id:`business`,label:`Kind of business`,options:[{label:`Choose the closest`,value:``},...G],initial_value:``,condition:{field:`kind`,eq:`local`}},{type:`text_input`,action_id:`name`,label:`Name`,placeholder:`Your name or your business's name`,initial_value:r.name??t},{type:`text_input`,action_id:`street`,label:`Street address`,initial_value:r.address?.streetAddress??``,condition:{field:`kind`,eq:`local`}},{type:`text_input`,action_id:`town`,label:`Town or city`,initial_value:r.address?.addressLocality??``,condition:{field:`kind`,eq:`local`}}],submit:{label:`Save`,action_id:`setup_identity`}}]}]}function Dt(e,t){let{site:n}=e.config,r=q(n),i=G.find(e=>e.value===n.type)?.label,a=r===`person`?`a person`:r===`local`?(i??`a local business`).toLowerCase():`a company`,o=[n.address?.streetAddress,n.address?.addressLocality].filter(Boolean).join(`, `),s=ft(n);return[`Google is told: ${n.name??t}, ${a}`,o?`, ${o}`:``,s.length>0?`, open ${s.join(`, `)}`:``,`. Change this in the Settings tab.`].join(``)}function Ot(e,t){if(!e.config.steps.identity)return[];let n=pt(e);return n.length===0?[{type:`context`,text:Dt(e,t)}]:[{type:`header`,text:`Your details`},{type:`context`,text:`${n.length} ${n.length===1?`thing`:`things`} Google needs from you. Add ${n.length===1?`it`:`them`} in the Settings tab.`},{type:`table`,page_action_id:`details_table`,columns:[{key:`what`,label:`Missing`},{key:`why`,label:`Why it matters`}],rows:n}]}function kt(e,t){return[...xt(e),...Tt(e,!0),...Et(e,t),...Ot(e,t),...St(e),...Ct(e)]}function At(e){let t=bt(e),n={type:`actions`,elements:[{type:`link`,label:`Open SEO`,target:{kind:`plugin-page`,path:`/overview`}}]};if(!t||t.checked===0){let{steps:t}=e.config;return{blocks:[{type:`section`,text:t.identity?`Your pages are being checked.`:`One thing left: tell Google who this site is for.`},n]}}return{blocks:[{type:`stats`,items:[t.fix===0?{label:`SEO`,value:`All pages ready`}:{label:t.fix===1?`Page to fix`:`Pages to fix`,value:t.fix}]},n]}}function jt(t,n){let{site:r}=t.config,i=q(r),a=i===`local`,o=t.config.steps.identity,s=ut(r.sameAs),c=[{type:`context`,text:`Google reads these details from your site and can show them beside your pages. Sections marked needed are followed up on the Overview; the rest are up to you.`},{type:`form`,block_id:`identity`,fields:[{type:`radio`,action_id:`kind`,label:`This site is for`,options:[{label:`A local business customers visit (shop, cafe, clinic)`,value:`local`},{label:`A company or organisation`,value:`organization`},{label:`A person`,value:`person`}],...o?{initial_value:i}:{}},{type:`select`,action_id:`business`,label:`Kind of business`,options:[{label:`Choose the closest`,value:``},...G],initial_value:a?r.type:``,condition:{field:`kind`,eq:`local`}},{type:`text_input`,action_id:`name`,label:`Name`,placeholder:`Your name or your business's name`,initial_value:r.name??n},{type:`text_input`,action_id:`description`,label:`What you do, in one or two sentences`,multiline:!0,initial_value:r.description??``}],submit:{label:`Save`,action_id:`save_identity`}},{type:`context`,text:`Each section below is saved with its own Save button.`}],l={type:`accordion`,label:K(`Address and phone`,a?`needed`:`optional`,[r.address?.streetAddress,r.address?.addressLocality,r.telephone,r.email]),default_open:a&&pt(t).some(e=>e.what!==`Opening hours`),blocks:[{type:`context`,text:a?`Google needs an address before it treats you as a local business. Use the same details as your Google Business Profile, which is what puts you on the map.`:`Leave the address empty if you have no public one.`},{type:`form`,block_id:`contact`,fields:[{type:`text_input`,action_id:`street`,label:`Street address`,initial_value:r.address?.streetAddress??``},{type:`text_input`,action_id:`town`,label:`Town or city`,initial_value:r.address?.addressLocality??``},{type:`text_input`,action_id:`region`,label:`State or region`,initial_value:r.address?.addressRegion??``},{type:`text_input`,action_id:`postcode`,label:`Postcode`,initial_value:r.address?.postalCode??``},{type:`text_input`,action_id:`country`,label:`Country code (GB, US, IN, AU)`,initial_value:r.address?.addressCountry??``},{type:`text_input`,action_id:`telephone`,label:`Phone`,initial_value:r.telephone??``},{type:`text_input`,action_id:`email`,label:`Email`,initial_value:r.email??``},{type:`select`,action_id:`price_range`,label:`Price range`,options:ct.map(e=>({label:e===``?`Not shown`:e,value:e})),initial_value:r.priceRange??``},{type:`text_input`,action_id:`areas`,label:`Areas you serve, one per line (if you travel to customers)`,multiline:!0,initial_value:r.areaServed.join(`
2
+ `)}const M=`redirects`,N=`redirects-import`,re={redirection:`Redirection`,yoast:`Yoast SEO Premium`,rankmath:`Rank Math`,plain:`a plain list`};async function ie(e){let t=await e.kv.get(M);return t&&Array.isArray(t.rows)?t:null}async function ae(e){let t=[],n;for(let r=0;r<5;r+=1){let r=await e.redirects?.list({limit:100,cursor:n});if(!r||(t.push(...r.items),n=r.hasMore?r.cursor:void 0,!n))break}return t}async function oe(e,t){let n=j(t,await ae(e)),r={status:`checked`,format:n.format,rows:n.ready,skipped:n.skipped,cursor:0,added:0,group:`Imported ${new Date().toISOString().slice(0,16).replace(`T`,` `)}`};return await e.kv.set(M,r),r}function se(e){let t=e instanceof Error?e.message:String(e);return/already exists/i.test(t)?{kind:`exists`,message:t}:/invalid source pattern|must be different|destination|path traversal|loop/i.test(t)?{kind:`invalid`,message:t}:{kind:`stop`,message:t}}async function ce(e,t){let n={...t,status:`adding`,skipped:[...t.skipped]},r=async()=>{try{return await e.kv.set(M,n),!0}catch{return!1}};if(t.status===`checked`)try{await e.cron?.schedule(N,{schedule:`* * * * *`})}catch(t){e.log.warn(`could not schedule background import`,{error:String(t)})}let i=0,a=n.cursor;for(;n.cursor<n.rows.length;){let t=n.rows[n.cursor];if(!t)break;try{await e.redirects?.create?.({source:t.source,destination:t.destination,type:t.type,groupName:n.group}),n.added+=1}catch(e){let{kind:r,message:i}=se(e);if(r===`stop`){if(n.cursor!==a||(n.stalls=(n.stalls??0)+1,n.stalls<3))break;n.skipped.push({line:0,source:t.source,reason:`EmDash could not add it: ${i}`})}else r===`exists`?n.added+=1:n.skipped.push({line:0,source:t.source,reason:`EmDash refused it: ${i}`})}if(n.cursor+=1,n.stalls=0,i+=1,i>=4){if(!await r())return n;i=0}}if(n.cursor>=n.rows.length){n.status=`done`;try{await e.cron?.cancel(N)}catch{}}return await r(),n}async function le(e,t){let n=await e.redirects?.list({limit:100,group:t.group}),r=n?.items??[],i=r.length;for(let t of r)try{let n=await e.redirects?.get(t.id);n&&await e.redirects?.delete?.(t.id,{_rev:n._rev}),--i}catch{break}if(i===0&&!n?.hasMore){try{await e.cron?.cancel(N)}catch{}return await e.kv.delete(M),0}return i}async function ue(e){let t=[],n;for(let r=0;r<5;r+=1){let r=await e.redirects?.list({limit:100,cursor:n});if(!r||(t.push(...r.items),n=r.hasMore?r.cursor:void 0,!n))break}return{text:ne(t),count:t.length}}function P(e,t){return`${e} ${t}${e===1?``:`s`}`}function de(e){if(e.length===0)return[];let t=[{type:`header`,text:`Skipped (${e.length})`},{type:`context`,text:`Skipped rows are not added. Copy any you still need and add them by hand under Manage > Redirects, which also accepts patterns.`},{type:`table`,page_action_id:`skipped_table`,columns:[{key:`line`,label:`Row`},{key:`source`,label:`Old address`},{key:`reason`,label:`Why`}],rows:e.slice(0,50).map(e=>({line:e.line>0?String(e.line):``,source:e.source,reason:e.reason}))}];return e.length>50&&t.push({type:`context`,text:`And ${e.length-50} more.`}),t}const fe=[{value:`301`,label:`Permanent`},{value:`302`,label:`Temporary, the old address will come back`},{value:`410`,label:`Page removed on purpose, no new address`}];function pe(e){return e===301||e===308?`Permanent`:e===302||e===307?`Temporary`:e===410?`Deleted on purpose`:String(e)}async function me(e){let t=await e.redirects?.list({limit:20});return{rows:(t?.items??[]).map(({id:e,source:t,destination:n,type:r,hits:i})=>({id:e,source:t,destination:n,type:r,hits:i})),more:t?.hasMore??!1}}function he(e){let t=e instanceof Error?e.message:String(e);return/already exists/i.test(t)?`There is already a redirect from that old address. Use Change in the list below.`:/must be different/i.test(t)?`The old and new address are the same.`:/loop/i.test(t)?`That would send visitors in a circle.`:/changed since/i.test(t)?`That redirect was just changed elsewhere. Try again.`:`EmDash could not save that redirect. Check both addresses start with /.`}function ge(e,t,n){let r=j(ne([{source:e,destination:t,type:Number(n)||301}]),[]);return r.ready[0]??r.skipped[0]?.reason??`Type the old address first.`}async function _e(e,t,n,r){let i=ge(t,n,r);if(typeof i==`string`)return`${i}.`;try{return await e.redirects?.create?.(i),null}catch(e){return he(e)}}async function ve(e,t,n,r,i){let a=ge(n,r,i);if(typeof a==`string`)return`${a}.`;try{let n=await e.redirects?.get(t);return n?(await e.redirects?.update?.(t,{...a,_rev:n._rev}),null):`That redirect no longer exists.`}catch(e){return he(e)}}async function ye(e,t){try{let n=await e.redirects?.get(t);return n&&await e.redirects?.delete?.(t,{_rev:n._rev}),null}catch(e){return he(e)}}function be(e){if(!e)return[{type:`context`,text:`Paste a list exported from Redirection, Yoast SEO Premium or Rank Math, or any list of old address, new address. Nothing is added until you confirm.`},{type:`form`,block_id:`redirects`,fields:[{type:`text_input`,action_id:`list`,label:`Your list, one redirect per line`,multiline:!0,placeholder:`/old-page,/new-page`,initial_value:``}],submit:{label:`Check list`,action_id:`check_redirects`}}];if(e.status===`checked`){let t=e.rows.length;return[{type:`banner`,variant:t===0?`alert`:`default`,title:`${t} ready, ${e.skipped.length} skipped`,description:t===0?`Nothing in this list can be added. See why below.`:`Read as ${re[e.format]}. Nothing has been added yet. Existing redirects are never changed.`},{type:`actions`,elements:[...t>0?[{type:`button`,action_id:`add_redirects`,label:`Add ${P(t,`redirect`)}`,style:`primary`,confirm:{title:`Add ${P(t,`redirect`)}?`,text:`Visitors to these old addresses will be sent to the new ones. You can remove them all again afterwards.`,confirm:`Add`,deny:`Cancel`}}]:[],{type:`button`,action_id:`reset_redirects`,label:`Start again`}]},...de(e.skipped)]}return e.status===`adding`?[{type:`meter`,label:`${e.added} of ${e.rows.length} added`,value:e.cursor,max:e.rows.length},{type:`context`,text:`The rest are added in the background, a few each minute. You can leave this page.`},{type:`actions`,elements:[{type:`button`,action_id:`more_redirects`,label:`Add more now`,style:`primary`},{type:`button`,action_id:`stop_redirects`,label:`Stop here`}]}]:[{type:`banner`,title:`${P(e.added,`redirect`)} added from your list`,description:`They are labelled "${e.group}".`},{type:`actions`,elements:[{type:`button`,action_id:`reset_redirects`,label:`Import another list`},...e.added>0?[{type:`button`,action_id:`remove_redirects`,label:`Remove the ${e.added} just added`,confirm:{title:`Remove these redirects?`,text:`Only the redirects this import added are removed.`,confirm:`Remove`,deny:`Cancel`,style:`danger`}}]:[]]},...de(e.skipped)]}function xe(e,t){let n=e?String(e.type):`301`;return{type:`form`,block_id:e?`edit_redirect_${e.id}`:`add_one_${t}`,fields:[{type:`text_input`,action_id:`source`,label:`Redirect from`,placeholder:`/old-page`,initial_value:e?.source??``},{type:`text_input`,action_id:`destination`,label:`Redirect to`,placeholder:`/new-page`,initial_value:e?.destination??``,condition:{field:`code`,neq:`410`}},{type:`select`,action_id:`code`,label:`Kind`,options:fe.some(e=>e.value===n)?fe:[...fe,{value:n,label:`${pe(Number(n))} (${n})`}],initial_value:n}],submit:e?{label:`Save changes`,action_id:`save_redirect:${e.id}`}:{label:`Add redirect`,action_id:`add_redirect`}}}function Se(e){let{state:t,rows:n,editing:r}=e,i=r?void 0:e.panel??(t?`import`:void 0),a=n[0]?.id??`first`,o=[{type:`context`,text:`Send visitors from an old address to a new one. Pages you rename in EmDash are redirected for you.`},{type:`actions`,elements:[{type:`button`,action_id:`open_add`,label:`Add a redirect`,...i===void 0&&!r?{style:`primary`}:{}},{type:`button`,action_id:`open_import`,label:`Import from WordPress`},{type:`button`,action_id:`export_redirects`,label:`Export`},...e.managerUrl?[{type:`link`,label:`Full Redirects screen`,target:{kind:`external`,url:e.managerUrl}}]:[]]}],s={type:`actions`,elements:[{type:`button`,action_id:`close_panel`,label:`Close`}]},c=e.problem?[{type:`banner`,variant:`error`,title:e.problem}]:[];return r?o.push({type:`header`,text:`Change a redirect`},...c,xe(r,a),s,{type:`divider`}):i===`add`?o.push({type:`header`,text:`Add a redirect`},...c,xe(void 0,a),s,{type:`divider`}):i===`import`?(o.push({type:`header`,text:`Import from WordPress`},...be(t)),t||o.push(s),o.push({type:`divider`})):i===`export`&&o.push({type:`header`,text:`Export`},{type:`context`,text:`Copy this list to keep a backup or to paste into a spreadsheet. It can be imported again here.`},{type:`code`,code:e.exported??``,language:`bash`},s,{type:`divider`}),n.length===0?o.push({type:`empty`,title:`No redirects yet`,description:`Add one, or import a list from your old site.`,size:`sm`}):(o.push({type:`table`,page_action_id:`redirects_table`,columns:[{key:`source`,label:`Redirect from`},{key:`destination`,label:`Redirect to`},{key:`kind`,label:`Kind`},{key:`hits`,label:`Visits`,format:`number`},{key:`edit`,label:``,format:`element`},{key:`remove`,label:``,format:`element`}],rows:n.map(e=>({source:e.source,destination:e.destination||`(none)`,kind:pe(e.type),hits:e.hits,edit:{type:`button`,action_id:`edit_redirect`,label:`Change`,value:e.id},remove:{type:`button`,action_id:`remove_redirect`,label:`Remove`,value:e.id,confirm:{title:`Remove this redirect?`,text:`Visitors to ${e.source} will see a not-found page.`,confirm:`Remove`,deny:`Keep`,style:`danger`}}}))}),e.more&&o.push({type:`context`,text:`Showing the newest ${n.length}. The rest are in the full Redirects screen.`})),o}async function Ce(e,t){let n={...t,status:`done`};try{await e.cron?.cancel(N)}catch{}return await e.kv.set(M,n),n}const we=new Set("iIl.,:;|!'`jtfr ()[]".split(``)),Te=new Set(`mwMWQO@%`.split(``));function F(e){let t=0;for(let n of e)we.has(n)?t+=5:Te.has(n)?t+=15:n>=`A`&&n<=`Z`?t+=12:t+=9.5;return Math.round(t)}function Ee(e){if(F(e)<=580)return e;let t=e;for(;t.length>1&&F(`${t}…`)>580;)t=t.slice(0,-1);return`${t.trimEnd()}…`}function De(e){return e.toLowerCase().replace(/\s+/g,` `).trim()}const Oe=new Set([`home`,`homepage`,`home page`,`index`,`untitled`,`welcome`]),ke=new Set([`_type`,`_key`,`id`,`slug`,`url`,`href`,`src`,`style`,`icon`]);function Ae(e){let t={textLength:0,imagesWithoutAlt:0,hasImage:!1},n=(e,r,i)=>{if(i>12)return;if(typeof e==`string`){!ke.has(r)&&!/^(https?:)?\//.test(e)&&(t.textLength+=e.trim().length);return}if(Array.isArray(e)){for(let t of e)n(t,r,i+1);return}if(typeof e!=`object`||!e)return;let a=e;(a._type===`image`||typeof a.src==`string`&&`alt`in a)&&(t.hasImage=!0,(typeof a.alt!=`string`||a.alt.trim()===``)&&(t.imagesWithoutAlt+=1));for(let[e,t]of Object.entries(a))n(t,e,i+1)};for(let[t,r]of Object.entries(e))t!==`json_ld`&&n(r,t,0);return t}const I={id:`no-social-image`,level:`look`,message:`This page has no picture for shared links, unless your site has a default sharing image.`,fix:`Add an image in the SEO panel. If your theme adds a picture itself, this clears once the page has been viewed.`};function L(e){return typeof e.excerpt==`string`?e.excerpt.trim():``}function je(t){let n=[],{seo:r}=t,i=Ae(t.data),a=r?.title?.trim()||t.title.trim();if(a===``&&n.push({id:`no-title`,level:`fix`,message:`This page has no title.`,fix:`Give the page a title.`}),Oe.has(a.toLowerCase())&&n.push({id:`generic-title`,level:`fix`,message:`The title is just "${a}", which is what Google shows as the headline.`,fix:`Put your name and what you do in SEO Title, in the page's SEO panel.`}),r&&!r.description?.trim()&&L(t.data)===``&&n.push({id:`no-description`,level:`fix`,message:`No search description, so Google writes its own summary of this page.`,fix:`Fill in Meta Description in the page's SEO panel.`}),t.duplicateTitle&&a!==``&&n.push({id:`duplicate-title`,level:`fix`,message:`Another page has the same title, so Google cannot tell them apart.`,fix:`Give this page its own title in the SEO panel.`}),r?.canonical&&t.slug){let i=e(r.canonical,`${t.siteUrl.replace(/\/+$/,``)}/`),a=i?.pathname.replace(/\/+$/,``).split(`/`).pop(),o=[`home`,`index`].includes(t.slug)&&(a??``)===``;i&&a!==t.slug&&!o&&n.push({id:`canonical-elsewhere`,level:`fix`,message:`This page tells Google to show a different page instead (canonical).`,fix:`Clear the canonical address in the SEO panel unless that is intended.`})}if(a!==``&&F(a)>580&&n.push({id:`title-long`,level:`look`,message:`The end of this title may be cut off in Google.`,fix:`Shorten the title, or put the important words first.`}),r&&!r.image&&!i.hasImage&&n.push(I),t.isArticle&&t.authorCount===0&&n.push({id:`no-author`,level:`look`,message:`This article has no author.`,fix:`Add a byline to the article.`}),i.imagesWithoutAlt>0){let e=i.imagesWithoutAlt;n.push({id:`images-without-alt`,level:`look`,message:`${e} ${e===1?`picture has`:`pictures have`} no description for screen readers.`,fix:`Add alt text to each picture.`})}return i.textLength<80&&n.push({id:`nearly-empty`,level:`look`,message:`This page has almost no text.`,fix:`Add some content, or hide the page from search.`}),n}function Me(e,t){return t?`hidden`:e.some(e=>e.level===`fix`)?`fix`:e.length>0?`look`:`ready`}const Ne=/^[0-9A-HJKMNP-TV-Z]{26}(\.[A-Za-z0-9]+)?$/i;function Pe(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Fe(e){return typeof e==`number`&&Number.isFinite(e)&&e>=1?Math.round(e):void 0}function Ie(e){if(!e.startsWith(`/_emdash/api/media/file/`))return;let t=e.slice(24);try{t=decodeURIComponent(t)}catch{return}return t!==``&&!t.includes(`/`)?t:void 0}function Le(t,n){let r=e(t,`${n.replace(/\/+$/,``)}/`);if(!r||!/^https?:$/.test(r.protocol)||r.search!==``)return[];let{pathname:i}=r;if(i===`/_image`||i.includes(`/cdn-cgi/image/`))return[];let a=Ie(i);if(a)return[`file:${a}`];let o=[`url:${r.origin}${i}`],s=i.slice(1);return Ne.test(s)&&o.push(`file:${s}`),o}function Re(e){if(typeof e!=`string`||e===``)return[];if(e.startsWith(`/`)){let t=e.includes(`?`)?void 0:Ie(e);return t?[`file:${t}`]:[]}return/^https?:\/\//i.test(e)?Le(e,`http://localhost`).filter(e=>e.startsWith(`url:`)):[]}function ze(e){return typeof e==`string`&&e!==``&&!e.includes(`/`)?[`file:${e}`]:[]}function Be(e){let t=Fe(e.width),n=Fe(e.height);if(t===void 0||n===void 0||typeof e.mimeType==`string`&&!e.mimeType.startsWith(`image/`))return;let r=[];if(e._type===`image`&&Pe(e.asset))r.push(...ze(e.asset._ref),...Re(e.asset.url));else if(typeof e.id==`string`){if(e.provider===void 0||e.provider===`local`){let t=Pe(e.meta)?e.meta:{};r.push(...ze(t.storageKey),...ze(e.id))}r.push(...Re(e.src))}let i=[...new Set(r)];return i.length>0?{keys:i,width:t,height:n}:void 0}function Ve(e){let t=[],n=new Set,r=(e,i)=>{if(t.length>=10||i>12)return;if(Array.isArray(e)){for(let t of e)r(t,i+1);return}if(!Pe(e))return;let a=Be(e);if(a&&!a.keys.some(e=>n.has(e))){for(let e of a.keys)n.add(e);t.push(a)}for(let t of Object.values(e))r(t,i+1)};for(let[t,n]of Object.entries(e))t!==`json_ld`&&r(n,0);return t.slice(0,10)}function He(e,t,n){if(!e||e.length===0)return;let r=Le(t,n);if(r.length===0)return;let i=e.find(e=>e.keys.some(e=>r.includes(e)));return i?{width:i.width,height:i.height}:void 0}const Ue=16*1024;function R(e,t){return`${e}:${t}`}function z(e){return e.storage.entries}function We(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&`@type`in e}function Ge(e){let t=e;if(typeof e==`string`){if(e.trim()===``||e.length>Ue)return[];try{t=JSON.parse(e)}catch{return[]}}let n=(Array.isArray(t)?t:[t]).filter(We);return JSON.stringify(n).length>Ue?[]:n}function Ke(e){let{title:t,name:n}=e.data;return typeof t==`string`?t:typeof n==`string`?n:``}function qe(e){let{collection:t,entry:n,authors:i,previous:o,config:s,siteUrl:c}=e,l=s.collections[t],u=o?.type??l?.type??a(t),d=n.seo?.title?.trim()||Ke(n),f=n.status===`published`,p=f&&e.hasPages,m=e.hasPages?je({slug:n.slug,data:n.data,seo:n.seo,title:Ke(n),siteUrl:c,isArticle:r.includes(u),authorCount:i.length,duplicateTitle:e.duplicateTitle}):[],h=m.some(e=>e.id===I.id),g=h&&o?.imageSeen===!0,_=g?m.filter(e=>e.id!==I.id):m,v=e.hasPages?Ve(n.data):[];return{v:1,publishedAt:n.publishedAt,updatedAt:n.updatedAt,authors:i,...o?.type?{type:o.type}:{},custom:Ge(n.data[s.jsonLdField]),...v.length>0?{images:v}:{},needsImage:h,...g?{imageSeen:g}:{},collection:t,entryId:n.id,title:d,status:e.hasPages?f?Me(_,n.seo?.noIndex===!0||l?.noindex===!0):`draft`:`skip`,titleKey:p?De(d):``,issues:_,checkedAt:new Date().toISOString()}}function Je(e,t){return e.status===`hidden`||e.status===`draft`||e.status===`skip`?e.status:Me(t,!1)}function Ye(e,t){let n=e.issues??[],r=n.some(e=>e.id===I.id);if(!(e.needsImage??r)||e.imageSeen===!0===t&&r!==t)return null;let i=n.filter(e=>e.id!==I.id),a=t?i:[...i,I],{imageSeen:o,...s}=e;return{...s,needsImage:!0,...t?{imageSeen:!0}:{},issues:a,status:Je(e,a)}}async function Xe(e,t,n){return t===``?[]:(await z(e).query({where:{titleKey:t},limit:5})).items.filter(e=>e.id!==n)}function B(e,t){let n=(e.issues??[]).filter(e=>e.id!==`duplicate-title`),r=t?[...n,{id:`duplicate-title`,level:`fix`,message:`Another page has the same title, so Google cannot tell them apart.`,fix:`Give this page its own title in the SEO panel.`}]:n;return{...e,issues:r,status:Je(e,r)}}async function Ze(e,t,n,r){return(await Qe(e,t,n,r))?.record??null}async function Qe(e,t,n,r){let i=await e.content?.get(t,n);if(!i)return null;let a=z(e),o=R(t,n),[s,c,l]=await Promise.all([e.bylines?.getEntriesBylines(t,[n])??[],a.get(o),e.schema?.getCollection(t)]),u=qe({collection:t,entry:i,authors:(s[0]?.bylines??[]).map(({byline:e})=>({name:e.displayName,...e.websiteUrl?{url:e.websiteUrl}:{}})),previous:c,config:r,siteUrl:e.site.url,duplicateTitle:!1,hasPages:l?l.routable&&!l.hidden:!0}),d=await Xe(e,u.titleKey??``,o),f=B(u,d.length>0),p=[{id:o,data:f}];for(let e of d)e.data.issues?.some(e=>e.id===`duplicate-title`)||p.push({id:e.id,data:B(e.data,!0)});let m=c?.titleKey??``;if(m!==``&&m!==f.titleKey){let t=await Xe(e,m,o),n=t.length===1?t[0]:void 0;n&&p.push({id:n.id,data:B(n.data,!1)})}return await a.putMany(p),{record:f,entry:i}}async function $e(e,t,n){let r=z(e),i=R(t,n),a=await r.get(i);await r.delete(i);let o=await Xe(e,a?.titleKey??``,i),s=o.length===1?o[0]:void 0;s&&await r.put(s.id,B(s.data,!1))}const V=`scan`,et=`stats`,H=`seo-scan`;async function tt(e){return e.kv.get(V)}async function nt(e,t,n){let r={status:`running`,collections:t,phase:`entries`,checked:0,tally:{checked:0,fix:0,hidden:0,rows:[],more:!1},startedAt:new Date().toISOString()};try{await e.cron?.schedule(H,{schedule:`* * * * *`})}catch(t){e.log.warn(`could not schedule background check`,{error:String(t)})}return ot(e,r,n)}async function rt(e,t,n){let r=t.collections[0];if(r===void 0){t.phase=`titles`;return}let i=await e.content?.list(r,{limit:20,cursor:t.cursor,where:{status:`published`}}),a=i?.items??[];if(a.length>0){let o=z(e),s=a.map(e=>e.id),[c,l]=await Promise.all([e.bylines?.getEntriesBylines(r,s)??[],o.getMany(s.map(e=>R(r,e)))]),u=new Map(c.map(e=>[e.entryId,e.bylines])),d=a.map(t=>{let i=l.get(R(r,t.id))??null;return{id:R(r,t.id),data:qe({hasPages:!0,collection:r,entry:t,authors:(u.get(t.id)??[]).map(({byline:e})=>({name:e.displayName,...e.websiteUrl?{url:e.websiteUrl}:{}})),previous:i,config:n,siteUrl:e.site.url,duplicateTitle:i?.issues?.some(e=>e.id===`duplicate-title`)??!1})}});if(t.cursor===void 0&&t.checked===0&&!i?.hasMore&&t.collections.length===1){let e=new Map;for(let{data:t}of d)t.titleKey&&e.set(t.titleKey,(e.get(t.titleKey)??0)+1);for(let t of d)t.data=B(t.data,(e.get(t.data.titleKey??``)??0)>1);t.status=`done`,t.finishedAt=new Date().toISOString()}await o.putMany(d);for(let{data:e}of d)at(t.tally,e,1);t.checked+=a.length}i?.hasMore&&i.cursor?t.cursor=i.cursor:(t.cursor=void 0,t.collections=t.collections.slice(1),t.collections.length===0&&t.status!==`done`&&(t.phase=`titles`))}async function it(e,t){let n=z(e),r=await n.query({orderBy:{titleKey:`asc`},limit:100,cursor:t.titleCursor}),i=r.items.filter(e=>(e.data.titleKey??``)!==``),a=new Map,o=(e,n,r)=>{if((n.issues?.some(e=>e.id===`duplicate-title`)??!1)===r)return;let i=B(n,r);a.set(e,i),at(t.tally,n,-1),at(t.tally,i,1)},s=t.lastTitle;for(let e=0;e<i.length;e+=1){let t=i[e];if(!t)continue;let r=t.data.titleKey??``,a=i[e+1],c=s?.key===r,l=a?.data.titleKey===r;if(c&&s&&!s.flagged&&e===0){let e=await n.get(s.id);e&&o(s.id,e,!0)}let u=c||l;o(t.id,t.data,u),s={key:r,id:t.id,flagged:u}}a.size>0&&await n.putMany([...a].map(([e,t])=>({id:e,data:t}))),t.lastTitle=s,r.hasMore&&r.cursor?t.titleCursor=r.cursor:(t.status=`done`,t.finishedAt=new Date().toISOString(),t.titleCursor=void 0,t.lastTitle=void 0)}function U(e,t=`fix`){return{collection:e.collection??``,id:e.entryId??``,title:e.title||`(no title)`,issues:(e.issues??[]).filter(e=>e.level===t).map(e=>e.message)}}function at(e,t,n){if(!(!t.status||t.status===`draft`||t.status===`skip`)){if(e.checked+=n,t.status===`hidden`){e.hidden+=n;let r=e.hiddenTitles??[],i=t.title||`(no title)`;e.hiddenTitles=n===1?[...r,i].slice(0,10):r.filter(e=>e!==i)}if(t.status===`look`){let r=e.look??[];e.look=n===1?[...r,U(t,`look`)].slice(0,10):r.filter(e=>e.id!==t.entryId)}t.status===`fix`&&(e.fix+=n,n===1?e.rows.length<10?e.rows.push(U(t)):e.more=!0:e.rows=e.rows.filter(e=>e.id!==t.entryId))}}async function ot(e,t,n){let r={...t,collections:[...t.collections],tally:{...t.tally,rows:[...t.tally.rows]}};if(r.status===`done`)return r;if(r.phase===`entries`){for(let t=0;t<5&&r.phase===`entries`;t+=1){let t=r.checked;if(await rt(e,r,n),r.checked!==t||r.status===`done`)break}r.phase===`titles`&&r.checked===0&&(r.status=`done`,r.finishedAt=new Date().toISOString())}else await it(e,r);if(r.status===`done`)try{await e.cron?.cancel(H)}catch{}return await e.kv.set(V,r),r}async function st(e){let t=z(e),[n,r,i,a,o]=await Promise.all([t.count({status:{in:[`ready`,`look`,`fix`,`hidden`]}}),t.count({status:`fix`}),t.query({where:{status:`fix`},limit:10}),t.query({where:{status:`look`},limit:10}),t.query({where:{status:`hidden`},limit:10})]);return{checked:n,fix:r,hidden:o.hasMore?11:o.items.length,rows:i.items.map(({data:e})=>U(e)),more:i.hasMore,look:a.items.map(({data:e})=>U(e,`look`)),hiddenTitles:o.items.map(({data:e})=>e.title||`(no title)`)}}const W={overview:0,redirects:1,identity:2,types:2},G=[{value:`FoodEstablishment`,label:`Restaurant, cafe or bakery`},{value:`Store`,label:`Shop`},{value:`MedicalBusiness`,label:`Health and medical`},{value:`Dentist`,label:`Dentist`},{value:`LegalService`,label:`Legal`},{value:`FinancialService`,label:`Financial`},{value:`HomeAndConstructionBusiness`,label:`Home services (plumber, electrician, builder)`},{value:`HealthAndBeautyBusiness`,label:`Beauty and hair`},{value:`SportsActivityLocation`,label:`Fitness`},{value:`LodgingBusiness`,label:`Hotel or lodging`},{value:`AutomotiveBusiness`,label:`Automotive`},{value:`ProfessionalService`,label:`Professional service`},{value:`LocalBusiness`,label:`Other`}],ct=[``,`$`,`$$`,`$$$`,`$$$$`],lt=[{id:`facebook`,label:`Facebook`,hosts:[`www.facebook.com`,`facebook.com`,`fb.com`]},{id:`instagram`,label:`Instagram`,hosts:[`www.instagram.com`,`instagram.com`]},{id:`linkedin`,label:`LinkedIn`,hosts:[`www.linkedin.com`,`linkedin.com`]},{id:`x`,label:`X (Twitter)`,hosts:[`x.com`,`www.x.com`,`twitter.com`,`www.twitter.com`]},{id:`youtube`,label:`YouTube`,hosts:[`www.youtube.com`,`youtube.com`]}];function ut(t){let n={},r=[];for(let i of t){let t=e(i)?.host??``,a=lt.find(e=>e.hosts.includes(t));a&&!(a.id in n)?n[a.id]=i:r.push(i)}return{named:n,other:r}}const dt={needed:`needed`,worth:`worth adding`,optional:`optional`};function K(e,t,n){let r=n.filter(e=>!!e);return r.length>0?`${e}: ${r.join(`, `)}`:`${e} (${dt[t]}, not added yet)`}function ft(e){return g.map(t=>{let n=b(t,e.openingHours).replace(`, `,` and `);return n===``?void 0:`${t.slice(0,3)} ${n}`}).filter(e=>e!==void 0)}function pt(e){let{site:t}=e.config,n=q(t),r=[];return n===`local`?((!t.address?.streetAddress||!t.address.addressLocality)&&r.push({what:`Address`,why:`Without it Google cannot treat you as a local business.`}),t.openingHours.length===0&&r.push({what:`Opening hours`,why:`Google cannot show when you are open.`}),t.telephone||r.push({what:`Phone number`,why:`Customers cannot call you from Google.`})):(n===`organization`&&!t.logo&&r.push({what:`Logo`,why:`Google shows it beside your name.`}),t.sameAs.length===0&&r.push({what:`Profiles elsewhere`,why:`They help Google connect your other pages to this site.`})),r}function q(e){return e.type===`Person`?`person`:e.type===`Organization`?`organization`:`local`}function mt(t,n){let r=e(t);if(!(!r||r.protocol!==`https:`&&r.protocol!==`http:`))return`${r.origin}${n}`}function ht(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function J(e,t,n=``){let r=e[t];return typeof r==`string`?r.trim():n}function gt(e,t,n){let r=e[t];return typeof r==`boolean`?r:n}function _t(e){return e.split(/\r?\n/).map(e=>e.trim()).filter(e=>e!==``)}async function vt(e){let[n,r,i]=await Promise.all([e.kv.list(),e.schema?.listCollections()??[],me(e)]),a=new Map(n.map(e=>[e.key,e.value])),o=a.get(M);return{config:h(a.get(t)),collections:r.filter(e=>e.routable&&!e.hidden),redirects:o&&Array.isArray(o.rows)?o:null,redirectRows:i.rows,moreRedirects:i.more,scan:a.get(V)??null,stats:a.get(et)??null}}async function yt(e,t){let n=await st(e);return JSON.stringify(n)!==JSON.stringify(t.stats)&&await e.kv.set(et,n),{...t,stats:n}}function bt(e){return e.stats??(e.scan?.status===`done`?e.scan.tally:null)}function xt(e){let{scan:t}=e,n=bt(e);if(!t||t.status===`running`)return[{type:`banner`,title:`Checking your pages`,description:t?`${t.checked} checked so far. The rest are checked in the background.`:`This starts by itself and takes about a minute.`}];if(!n)return[];let r=n.checked-n.fix-n.hidden;return[{type:`stats`,items:[{label:`Pages checked`,value:n.checked},{label:`Nothing to fix`,value:r},{label:`To fix`,value:n.fix}]}]}function St(e){let{scan:t}=e,n=bt(e);if(!n||!t||t.status===`running`)return[];let r=[];n.checked===0?r.push({type:`empty`,title:`No published pages yet`,size:`sm`}):n.fix===0?r.push({type:`empty`,title:`No pages to fix. Nice work.`,size:`sm`}):(r.push({type:`header`,text:`To fix`},{type:`table`,page_action_id:`fix_table`,columns:[{key:`page`,label:`Page`},{key:`missing`,label:`What's missing`},{key:`open`,label:``,format:`element`}],rows:n.rows.map(e=>({page:e.title,missing:e.issues.join(` `),open:{type:`link`,label:`Open page`,target:{kind:`content`,collection:e.collection,id:e.id}}}))}),n.more&&r.push({type:`context`,text:`Showing the first 10. Fix these and the next ones appear.`}));let i=n.look??[];if(i.length>0&&r.push({type:`accordion`,label:`Worth a look (${i.length}${i.length===10?`+`:``})`,blocks:[{type:`context`,text:`Nothing is wrong with these pages. These are improvements for when you have time.`},{type:`table`,page_action_id:`look_table`,columns:[{key:`page`,label:`Page`},{key:`advice`,label:`Could be better`},{key:`open`,label:``,format:`element`}],rows:i.map(e=>({page:e.title,advice:e.issues.join(` `),open:{type:`link`,label:`Open page`,target:{kind:`content`,collection:e.collection,id:e.id}}}))}]}),n.hidden>0){let e=(n.hiddenTitles??[]).join(`, `);r.push({type:`context`,text:`${n.hidden>10?`More than 10`:n.hidden} ${n.hidden===1?`page is`:`pages are`} hidden from Google and not counted${e?`: ${e}`:``}. To show one again, open it and switch off "Hide from search engines" in its SEO panel.`})}if(t.finishedAt){let e=new Date(t.finishedAt).toLocaleDateString(`en-GB`,{day:`numeric`,month:`short`});r.push({type:`context`,text:`Each page is checked again when it is saved. Last full check: ${e}.`})}return r}function Ct(e){if(!e.scan)return[];let t=e.scan.status===`running`;return[{type:`actions`,elements:[{type:`button`,action_id:t?`check_more`:`check_pages`,label:t?`Update`:`Check all pages again`}]}]}function wt(e){return e.collections.filter(t=>{let n=e.config.collections[t.slug];return n?.noindex===!0&&n.hiddenOk!==!0})}function Tt(e,t){let n=wt(e);if(n.length===0)return[];let r=[{type:`banner`,variant:`alert`,title:`Some pages are hidden from Google`,description:`Every page in ${n.map(e=>e.label).join(`, `)} is hidden from search engines. If that is a leftover from building the site, switch it back on in the Settings tab, under Content types.`}];return t&&r.push({type:`actions`,elements:[{type:`button`,action_id:`hidden_ok`,label:`This is intended`}]}),r}function Et(e,t){let{steps:n,site:r}=e.config;return n.identity?[]:[{type:`banner`,title:`Tell Google who this site is for`,description:`Google can then show your name and details beside your pages. It takes a minute.`},{type:`accordion`,label:`Set this up (1 minute)`,default_open:!0,blocks:[{type:`form`,block_id:`setup_identity`,fields:[{type:`radio`,action_id:`kind`,label:`This site is for`,options:[{label:`A local business customers visit (shop, cafe, clinic)`,value:`local`},{label:`A company or organisation`,value:`organization`},{label:`A person`,value:`person`}]},{type:`select`,action_id:`business`,label:`Kind of business`,options:[{label:`Choose the closest`,value:``},...G],initial_value:``,condition:{field:`kind`,eq:`local`}},{type:`text_input`,action_id:`name`,label:`Name`,placeholder:`Your name or your business's name`,initial_value:r.name??t},{type:`text_input`,action_id:`street`,label:`Street address`,initial_value:r.address?.streetAddress??``,condition:{field:`kind`,eq:`local`}},{type:`text_input`,action_id:`town`,label:`Town or city`,initial_value:r.address?.addressLocality??``,condition:{field:`kind`,eq:`local`}}],submit:{label:`Save`,action_id:`setup_identity`}}]}]}function Dt(e,t){let{site:n}=e.config,r=q(n),i=G.find(e=>e.value===n.type)?.label,a=r===`person`?`a person`:r===`local`?(i??`a local business`).toLowerCase():`a company`,o=[n.address?.streetAddress,n.address?.addressLocality].filter(Boolean).join(`, `),s=ft(n);return[`Google is told: ${n.name??t}, ${a}`,o?`, ${o}`:``,s.length>0?`, open ${s.join(`, `)}`:``,`. Change this in the Settings tab.`].join(``)}function Ot(e,t){if(!e.config.steps.identity)return[];let n=pt(e);return n.length===0?[{type:`context`,text:Dt(e,t)}]:[{type:`header`,text:`Your details`},{type:`context`,text:`${n.length} ${n.length===1?`thing`:`things`} Google needs from you. Add ${n.length===1?`it`:`them`} in the Settings tab.`},{type:`table`,page_action_id:`details_table`,columns:[{key:`what`,label:`Missing`},{key:`why`,label:`Why it matters`}],rows:n}]}function kt(e,t){return[...xt(e),...Tt(e,!0),...Et(e,t),...Ot(e,t),...St(e),...Ct(e)]}function At(e){let t=bt(e),n={type:`actions`,elements:[{type:`link`,label:`Open SEO`,target:{kind:`plugin-page`,path:`/overview`}}]};if(!t||t.checked===0){let{steps:t}=e.config;return{blocks:[{type:`section`,text:t.identity?`Your pages are being checked.`:`One thing left: tell Google who this site is for.`},n]}}return{blocks:[{type:`stats`,items:[t.fix===0?{label:`SEO`,value:`All pages ready`}:{label:t.fix===1?`Page to fix`:`Pages to fix`,value:t.fix}]},n]}}function jt(t,n){let{site:r}=t.config,i=q(r),a=i===`local`,o=t.config.steps.identity,s=ut(r.sameAs),c=[{type:`context`,text:`Google reads these details from your site and can show them beside your pages. Sections marked needed are followed up on the Overview; the rest are up to you.`},{type:`form`,block_id:`identity`,fields:[{type:`radio`,action_id:`kind`,label:`This site is for`,options:[{label:`A local business customers visit (shop, cafe, clinic)`,value:`local`},{label:`A company or organisation`,value:`organization`},{label:`A person`,value:`person`}],...o?{initial_value:i}:{}},{type:`select`,action_id:`business`,label:`Kind of business`,options:[{label:`Choose the closest`,value:``},...G],initial_value:a?r.type:``,condition:{field:`kind`,eq:`local`}},{type:`text_input`,action_id:`name`,label:`Name`,placeholder:`Your name or your business's name`,initial_value:r.name??n},{type:`text_input`,action_id:`description`,label:`What you do, in one or two sentences`,multiline:!0,initial_value:r.description??``}],submit:{label:`Save`,action_id:`save_identity`}},{type:`context`,text:`Each section below is saved with its own Save button.`}],l={type:`accordion`,label:K(`Address and phone`,a?`needed`:`optional`,[r.address?.streetAddress,r.address?.addressLocality,r.telephone,r.email]),default_open:a&&pt(t).some(e=>e.what!==`Opening hours`),blocks:[{type:`context`,text:a?`Google needs an address before it treats you as a local business. Use the same details as your Google Business Profile, which is what puts you on the map.`:`Leave the address empty if you have no public one.`},{type:`form`,block_id:`contact`,fields:[{type:`text_input`,action_id:`street`,label:`Street address`,initial_value:r.address?.streetAddress??``},{type:`text_input`,action_id:`town`,label:`Town or city`,initial_value:r.address?.addressLocality??``},{type:`text_input`,action_id:`region`,label:`State or region`,initial_value:r.address?.addressRegion??``},{type:`text_input`,action_id:`postcode`,label:`Postcode`,initial_value:r.address?.postalCode??``},{type:`text_input`,action_id:`country`,label:`Country code (GB, US, IN, AU)`,initial_value:r.address?.addressCountry??``},{type:`text_input`,action_id:`telephone`,label:`Phone`,initial_value:r.telephone??``},{type:`text_input`,action_id:`email`,label:`Email`,initial_value:r.email??``},{type:`select`,action_id:`price_range`,label:`Price range`,options:ct.map(e=>({label:e===``?`Not shown`:e,value:e})),initial_value:r.priceRange??``},{type:`text_input`,action_id:`areas`,label:`Areas you serve, one per line (if you travel to customers)`,multiline:!0,initial_value:r.areaServed.join(`
3
3
  `)}],submit:{label:`Save address and phone`,action_id:`save_contact`}}]},u=a?{type:`accordion`,label:K(`Opening hours`,`needed`,ft(r)),blocks:[{type:`context`,text:`Type the hours for each day, like 9:00-17:00. For a break in the day use two ranges: 12:00-14:30, 18:00-22:00. A day left empty is shown as closed.`},{type:`form`,block_id:`hours`,fields:g.map(e=>({type:`text_input`,action_id:`hours_${e.toLowerCase()}`,label:e,placeholder:`Closed`,initial_value:b(e,r.openingHours)})),submit:{label:`Save opening hours`,action_id:`save_hours`}}]}:{type:`accordion`,label:`Opening hours (only for a local business)`,blocks:[{type:`context`,text:`Opening hours are for a place customers visit. Choose "A local business customers visit" above and save, and this section opens up.`}]},d={type:`accordion`,label:K(`Logo`,i===`organization`?`needed`:`worth`,[r.logo?`added`:void 0]),blocks:[{type:`context`,text:`Google can show your logo beside your name. In Media, open your logo and use Copy URL, then paste it here. Use a square image at least 112 pixels wide.`},{type:`form`,block_id:`logo`,fields:[{type:`text_input`,action_id:`logo_url`,label:`Logo link`,placeholder:`https://example.com/logo.png`,initial_value:r.logo??``}],submit:{label:`Save logo`,action_id:`save_logo`}}]},f={type:`accordion`,label:K(`Your profiles elsewhere`,a?`worth`:`needed`,r.sameAs.map(t=>e(t)?.host.replace(/^www\./,``))),blocks:[{type:`context`,text:`Links to your pages on other sites help Google connect them to you. Paste the full address of each profile you have.`},{type:`form`,block_id:`profiles`,fields:[...lt.map(e=>({type:`text_input`,action_id:`profile_${e.id}`,label:e.label,placeholder:`https://${e.hosts[0]}/yourname`,initial_value:s.named[e.id]??``})),{type:`text_input`,action_id:`profiles`,label:`Other profiles, one per line`,multiline:!0,initial_value:s.other.join(`
4
4
  `)}],submit:{label:`Save profiles`,action_id:`save_profiles`}}]},p={type:`accordion`,label:K(`More about you`,`optional`,[r.alternateName,r.legalName,r.foundingDate?`founded ${r.foundingDate}`:void 0]),blocks:[{type:`context`,text:`Nothing here is needed. Skip anything that does not apply.`},{type:`form`,block_id:`company`,fields:[{type:`text_input`,action_id:`alternate_name`,label:`Also known as (a short name or abbreviation)`,initial_value:r.alternateName??``},{type:`text_input`,action_id:`legal_name`,label:`Registered legal name`,initial_value:r.legalName??``},{type:`text_input`,action_id:`founded`,label:`Founded (year, or YYYY-MM-DD)`,placeholder:`2012`,initial_value:r.foundingDate??``}],submit:{label:`Save`,action_id:`save_company`}}]},m=a?[l,u,d,f,p]:i===`organization`?[d,f,l,p,u]:[f,d,l,p,u];return c.push(...m),c}function Mt(e,t){return e.config.collections[t]?.type??a(t)}function Nt(e,t){let{config:n}=e,r=e.collections.find(e=>e.slug===t),i=[{type:`context`,text:`Google is told what each kind of content is, so it can show it properly: a blog post with its author and date, a service as a service. These were worked out from the names and are usually right. Every kind of content that has its own pages is listed here; content that only appears inside other pages, such as testimonials, is not. To change a single page (an About or Contact page, say), open it and use Change page type in its Google preview panel. Picking from a menu saves straight away.`}];return r&&i.push({type:`banner`,variant:`alert`,title:`Hide every page in ${r.label} from Google?`,description:`People will no longer find these pages through search engines. You can switch this back at any time.`},{type:`actions`,elements:[{type:`button`,action_id:`confirm_hide`,label:`Yes, hide them`,style:`danger`,value:r.slug},{type:`button`,action_id:`cancel_hide`,label:`Cancel`}]}),i.push({type:`table`,page_action_id:`types_table`,empty_text:`This site has no content types with pages yet.`,columns:[{key:`name`,label:`Content type`},{key:`shown`,label:``,format:`element`},{key:`visible`,label:``,format:`element`}],rows:e.collections.map(t=>{let r=n.collections[t.slug]?.noindex===!0;return{name:t.label,shown:{type:`menu`,action_id:`set_type`,label:`Google treats these as: ${S(Mt(e,t.slug))}`,items:x.map(({value:e,label:n})=>({label:n,value:`${t.slug}|${e}`}))},visible:{type:`menu`,action_id:`set_visible`,label:r?`Hidden from Google`:`Can appear on Google`,items:[{label:`Can appear on Google`,value:`${t.slug}|show`},{label:`Hide from Google and other search engines`,value:`${t.slug}|hide`}]}}})},{type:`form`,block_id:`breadcrumbs`,fields:[{type:`toggle`,action_id:`breadcrumbs`,label:`Show the path to each page on Google (Home > Services > Cakes)`,description:`Leave on unless your theme already does this.`,initial_value:n.breadcrumbs}],submit:{label:`Save`,action_id:`save_advanced`}}),[{type:`accordion`,label:`Content types (usually right as they are): ${e.collections.map(t=>{let r=n.collections[t.slug]?.noindex===!0;return`${t.label} as ${S(Mt(e,t.slug))}${r?`, hidden`:``}`}).join(`; `)}`,default_open:r!==void 0,blocks:i}]}function Y(e,t,n,r){let i=[{type:`header`,text:`SEO`},{type:`context`,text:`How your site appears on Google and other search engines.`}];return n?{blocks:[...i,{type:`tab`,default_tab:r.tab,panels:[{label:`Overview`,blocks:kt(e,t.site.name)},{label:`Redirects`,blocks:Se({state:e.redirects,rows:e.redirectRows,more:e.moreRedirects,editing:r.editing,panel:r.panel,problem:r.problem,exported:r.exported,managerUrl:mt(t.site.url,`/_emdash/admin/redirects`)})},{label:`Settings`,blocks:[...jt(e,t.site.name),...Nt(e,r.pendingHide)]}]}],...r.toast?{toast:r.toast}:{}}:{blocks:[...i,...xt(e),...Tt(e,!1),...St(e),{type:`context`,text:`Business details, opening hours and redirects are set by your site admin. Ask them if something there is wrong.`}]}}function Pt(t,n){if(t===``)return{};let r=e(t,`${n.replace(/\/+$/,``)}/`);return!r||r.protocol!==`https:`&&r.protocol!==`http:`?{error:`Couldn't save. The logo link must be a web address starting with https://`}:r.pathname.includes(`/_emdash/api/media/asset/`)?{error:`That link only works when signed in. In Media, use Copy URL on the image instead.`}:{logo:r.href}}function Ft(e,t){let n={tab:W.identity},{site:r}=e.config,i=J(t,`kind`,e.config.steps.identity?q(r):``);if(i!==`local`&&i!==`organization`&&i!==`person`)return{error:`Choose who this site is for.`,state:n};let a=J(t,`business`,q(r)===`local`?r.type:``),o=G.find(e=>e.value===a)?.value;if(i===`local`&&!o)return{error:`Choose the kind of business.`,state:n};let s=i===`person`?`Person`:i===`local`?o??`LocalBusiness`:`Organization`,c=J(t,`name`,r.name??``);return{config:{...e.config,site:{...r,type:s,name:c===``?void 0:c,description:J(t,`description`,r.description??``)||void 0,openingHours:i===`local`?r.openingHours:[]},steps:{...e.config.steps,identity:!0}},state:n}}function It(e,t,n){let r={tab:W.identity},i=Pt(J(t,`logo_url`,e.config.site.logo??``),n);return`error`in i?{error:i.error,state:r}:{config:{...e.config,site:{...e.config.site,logo:i.logo}},state:r}}function Lt(e,t){let{site:n}=e.config,r={streetAddress:J(t,`street`)||void 0,addressLocality:J(t,`town`)||void 0,addressRegion:J(t,`region`)||void 0,postalCode:J(t,`postcode`)||void 0,addressCountry:J(t,`country`).toUpperCase()||void 0},i=J(t,`price_range`);return{config:{...e.config,site:{...n,email:J(t,`email`)||void 0,telephone:J(t,`telephone`)||void 0,address:Object.values(r).some(e=>e!==void 0)?r:void 0,priceRange:ct.includes(i)&&i!==``?i:void 0,areaServed:_t(J(t,`areas`,n.areaServed.join(`
5
5
  `)))}},state:{tab:W.identity}}}function Rt(e,t){let n={tab:W.identity},r=[];for(let i of g){let a=y(i,J(t,`hours_${i.toLowerCase()}`,b(i,e.config.site.openingHours)));if(!a)return{error:`Couldn't read the hours for ${i}. Write them like 9:00-17:00, with at most two ranges.`,state:n};r.push(...a)}return{config:{...e.config,site:{...e.config.site,openingHours:r}},state:n}}function zt(t,n){let r={tab:W.identity},i=ut(t.config.site.sameAs),a=[...lt.map(e=>J(n,`profile_${e.id}`,i.named[e.id]??``)),..._t(J(n,`profiles`,i.other.join(`
6
- `)))].filter(e=>e!==``),o=[];for(let t of a){let n=e(/^https?:\/\//i.test(t)?t:`https://${t}`);if(!n||!n.host.includes(`.`))return{error:`"${t}" is not a web address. Paste the full link to the profile.`,state:r};o.push(n.href)}return{config:{...t.config,site:{...t.config.site,sameAs:o}},state:r}}function Bt(e,t){let n={tab:W.identity},{site:r}=e.config,i=J(t,`founded`,r.foundingDate??``);return i!==``&&!/^\d{4}(-\d{2}-\d{2})?$/.test(i)?{error:`Write the founding date as a year, like 2012, or as 2012-05-01.`,state:n}:{config:{...e.config,site:{...r,alternateName:J(t,`alternate_name`,r.alternateName??``)||void 0,legalName:J(t,`legal_name`,r.legalName??``)||void 0,foundingDate:i||void 0}},state:n}}function Vt(e,t){let{robots:n}=e.config,r=J(t,`snippet`,n.nosnippet?`none`:`full`),i=J(t,`image_preview`,n.maxImagePreview);return{config:{...e.config,breadcrumbs:gt(t,`breadcrumbs`,e.config.breadcrumbs),robots:{...n,nosnippet:r===`none`,maxSnippet:r===`short`?160:-1,maxImagePreview:i===`none`||i===`standard`||i===`large`?i:n.maxImagePreview}},state:{tab:W.types}}}function Ht(e,t,n){let r={tab:W.types},[i,a]=typeof t==`string`?t.split(`|`):[];if(!i||!e.collections.some(e=>e.slug===i))return{error:`That content type no longer exists.`,state:r};let o=e.config.collections[i]??{},s=n===`type`?x.find(e=>e.value===a)?.value:o.type??Mt(e,i);if(!s)return{error:`Choose one of the listed types.`,state:r};let c=n===`visible`?a===`hide`:o.noindex===!0;return{config:{...e.config,collections:{...e.config.collections,[i]:{type:s,noindex:c}},steps:{...e.config.steps,types:!0}},state:r}}async function Ut(e,t){if(t.scan?.status===`done`)return yt(e,t);let n=t.scan?await ot(e,t.scan,t.config):await nt(e,t.collections.map(e=>e.slug),t.config);return{...t,scan:n,stats:n.status===`done`?n.tally:null}}async function Wt(e,n,r){let i=await vt(r),a=(n??0)>=50,o=ht(e)?e:{},s=typeof o.action_id==`string`?o.action_id:``;if(o.type===`page_load`&&o.page===`widget:seo`)return At(await Ut(r,i));if(!a||o.type===`page_load`||s===``)return Y(await Ut(r,i),r,a,{tab:W.overview});let c=async(e,n=`Saved. Google will pick this up next time it visits.`)=>{if(`error`in e)return Y(i,r,a,{...e.state,toast:{type:`error`,message:e.error}});let o=h(e.config);return await r.kv.set(t,o),Y({...i,config:o},r,a,{...e.state,toast:{type:`success`,message:n}})},l=async(e,t,n)=>{if(e)return Y(i,r,a,{tab:W.redirects,editing:n,...n?{}:{panel:`add`},problem:e});let{rows:o,more:s}=await me(r);return Y({...i,redirectRows:o,moreRedirects:s},r,a,{tab:W.redirects,toast:{type:`success`,message:t}})};if(o.type===`block_action`){if(s===`check_pages`||s===`check_more`){let e=await Ut(r,s===`check_pages`?{...i,scan:null}:i);return Y(e,r,a,{tab:W.overview,toast:e.scan?.status===`done`?{type:`success`,message:`All pages checked.`}:{type:`info`,message:`${e.scan?.checked??0} pages checked so far. The rest follow in the background.`}})}if(s===`set_type`)return c(Ht(i,o.value,`type`));if(s===`set_visible`){let[e,t]=typeof o.value==`string`?o.value.split(`|`):[];return t===`hide`?Y(i,r,a,{tab:W.types,pendingHide:e}):c(Ht(i,o.value,`visible`),`These pages can appear on Google again.`)}if(s===`confirm_hide`&&typeof o.value==`string`)return c(Ht(i,`${o.value}|hide`,`visible`),`These pages are now hidden from Google and other search engines.`);if(s===`cancel_hide`)return Y(i,r,a,{tab:W.types});if(s===`hidden_ok`){let e={...i.config.collections};for(let t of wt(i))e[t.slug]={...e[t.slug],hiddenOk:!0};return c({config:{...i.config,collections:e},state:{tab:W.overview}},`Noted. These pages stay hidden from search.`)}if(s===`edit_redirect`){let e=i.redirectRows.find(e=>e.id===o.value);return Y(i,r,a,{tab:W.redirects,editing:e})}if(s===`open_add`)return Y(i,r,a,{tab:W.redirects,panel:`add`});if(s===`open_import`)return Y(i,r,a,{tab:W.redirects,panel:`import`});if(s===`close_panel`)return Y(i,r,a,{tab:W.redirects});if(s===`remove_redirect`&&typeof o.value==`string`)return l(await ye(r,o.value),`Redirect removed.`);if(s===`add_redirects`||s===`more_redirects`){if(!i.redirects||i.redirects.status===`done`)return Y(i,r,a,{tab:W.redirects});let e=await ce(r,i.redirects);return Y({...i,redirects:e},r,a,{tab:W.redirects,toast:e.status===`done`?{type:`success`,message:`${P(e.added,`redirect`)} added.`}:{type:`info`,message:`${e.added} added so far. The rest follow in the background.`}})}if(s===`stop_redirects`&&i.redirects?.status===`adding`){let e=await Ce(r,i.redirects);return Y({...i,redirects:e},r,a,{tab:W.redirects,toast:{type:`info`,message:`Stopped. ${P(e.added,`redirect`)} added.`}})}if(s===`reset_redirects`)return i.redirects?.status!==`adding`&&await r.kv.delete(M),Y({...i,redirects:i.redirects?.status===`adding`?i.redirects:null},r,a,{tab:W.redirects});if(s===`remove_redirects`&&i.redirects){let e=await le(r,i.redirects);return Y({...i,redirects:e===0?null:i.redirects},r,a,{tab:W.redirects,toast:e===0?{type:`success`,message:`Removed. Your other redirects are untouched.`}:{type:`info`,message:`${e} still to remove. Press Remove again.`}})}if(s===`export_redirects`){let{text:e,count:t}=await ue(r);return Y(i,r,a,{tab:W.redirects,panel:`export`,exported:e,toast:{type:`info`,message:`${P(t,`redirect`)} listed below.`}})}return Y(i,r,a,{tab:W.overview})}if(o.type!==`form_submit`)return Y(i,r,a,{tab:W.overview});let u=ht(o.values)?o.values:{};if(s===`check_redirects`){let e=J(u,`list`);if(e===``)return Y(i,r,a,{tab:W.redirects,toast:{type:`error`,message:`Paste a list first.`}});let t=await oe(r,e);return Y({...i,redirects:t},r,a,{tab:W.redirects,toast:{type:`success`,message:`Checked. Nothing has been added yet.`}})}if(s===`add_redirect`){let e=J(u,`source`),t=J(u,`code`,`301`)===`410`?``:J(u,`destination`);return l(await _e(r,e,t,J(u,`code`,`301`)),t===``?`Done. ${e} now tells visitors the page was removed.`:`Redirect added. ${e} now goes to ${t}.`)}if(s.startsWith(`save_redirect:`)){let e=s.slice(14),t=i.redirectRows.find(t=>t.id===e);return l(await ve(r,e,J(u,`source`,t?.source??``),J(u,`destination`,t?.destination??``),J(u,`code`,String(t?.type??301))),`Redirect changed.`,t)}if(s===`setup_identity`){let e=Ft(i,u);if(`error`in e)return c(e);let t=J(u,`street`),n=J(u,`town`),{site:r}=e.config,a=t!==``||n!==``?{...r.address,...t?{streetAddress:t}:{},...n?{addressLocality:n}:{}}:r.address;return c({config:{...e.config,site:{...r,address:a}},state:{tab:W.overview}})}return s===`save_identity`?c(Ft(i,u)):s===`save_logo`?c(It(i,u,r.site.url)):s===`save_contact`?c(Lt(i,u)):s===`save_hours`?c(Rt(i,u)):s===`save_profiles`?c(zt(i,u)):s===`save_company`?c(Bt(i,u)):s===`save_advanced`?c(Vt(i,u)):Y(i,r,a,{tab:W.overview})}function X(e){return{"@id":e}}function Z(e){let t={};for(let[n,r]of Object.entries(e))r==null||r===``||Array.isArray(r)&&r.length===0||(t[n]=r);return t}function Gt(t,n){return e(t,n)?.href??t}function Kt(t,n){return(t.siteUrl||n||(e(t.url)?.origin??``)).replace(/\/+$/,``)}function qt(t,n){if(t.canonical)return Gt(t.canonical,`${n}/`);let r=e(t.url,`${n}/`);return r?(r.search=``,r.hash=``,r.href):t.url}function Jt(e){return e.path===`/`||e.path===``}function Yt(e){return r.includes(e)}function Xt(e,t,n){let r=e.content?t.collections[e.content.collection]:void 0;if(n?.type)return n.type;if(r?.type)return r.type;let i=e.content?a(e.content.collection):`WebPage`;return i===`WebPage`?e.pageType===`article`?`BlogPosting`:`WebPage`:i}function Zt(e,t,n,r){let i=`${n}/#logo`,a=e.type===`Person`,o=!a&&e.type!==`Organization`&&e.address===void 0?`Organization`:e.type,s=!a&&(e.address!==void 0||e.geo!==void 0||e.openingHours.length>0);return Z({"@type":o,"@id":r,name:t,alternateName:e.alternateName,legalName:a?void 0:e.legalName,description:e.description,url:`${n}/`,logo:e.logo&&!a?{"@type":`ImageObject`,"@id":i,url:e.logo,contentUrl:e.logo,caption:t}:void 0,image:e.logo?a?e.logo:X(i):void 0,sameAs:e.sameAs,email:e.email,telephone:e.telephone,address:e.address?{"@type":`PostalAddress`,...e.address}:void 0,geo:e.geo&&!a?{"@type":`GeoCoordinates`,...e.geo}:void 0,openingHoursSpecification:s?e.openingHours.map(e=>({"@type":`OpeningHoursSpecification`,dayOfWeek:e.days,opens:e.opens,closes:e.closes})):void 0,priceRange:a?void 0:e.priceRange,areaServed:a?void 0:e.areaServed,foundingDate:a?void 0:e.foundingDate,vatID:a?void 0:e.vatID,taxID:a?void 0:e.taxID,numberOfEmployees:!a&&e.numberOfEmployees?{"@type":`QuantitativeValue`,value:e.numberOfEmployees}:void 0})}function Qt(e){let t=e;try{t=decodeURIComponent(e)}catch{}let n=t.replace(/[-_]+/g,` `).trim();return n.charAt(0).toUpperCase()+n.slice(1)}function $t(e,t,n){let{page:r,config:i,site:a}=e;if(r.breadcrumbs)return r.breadcrumbs.map(e=>({name:e.name,url:Gt(e.url,`${t}/`)}));if(!i.breadcrumbs||Jt(r))return[];let o=r.path.split(`/`).filter(e=>e!==``);if(o.length===0)return[];let s=a.trailingSlash===`always`?`/`:``,c=[{name:`Home`,url:`${t}/`}],l=``;return o.forEach((e,r)=>{l+=`/${e}`;let i=r===o.length-1;c.push({name:i?n:Qt(e),url:`${t}${l}${s}`})}),c}function en(e,t){return e.length<=t?e:`${e.slice(0,t-1).trimEnd()}…`}function tn(e,t,n){let r;for(let{"@context":i,...a}of t){let t=typeof a[`@id`]==`string`?a[`@id`]:void 0;if(t!==void 0){if(n.has(t))continue;n.add(t),r??=t}e.push(a)}return r}function nn(e){let{page:t,config:n,record:r,site:i}=e,a=Kt(t,i.url),o=qt(t,a),s=t.locale??i.locale,c=n.site.name??t.siteName??i.name,l=t.pageTitle??t.title??c,u=n.site.type===`Person`,d=`${a}/#${u?`person`:`organization`}`,f=`${a}/#website`,p=o,m=`${o}#breadcrumb`,h=Xt(t,n,r),g=Yt(h),_=h===`Service`,v=h===`ProfilePage`,y=`${o}#service`,b=`${o}#person`,x=r?.publishedAt??t.articleMeta?.publishedTime??void 0,S=r?.updatedAt??t.articleMeta?.modifiedTime??void 0,C=t.image?Gt(t.image,`${a}/`):void 0,w=C??``,T=$t(e,a,l),E=[Zt(n.site,c,a,d),Z({"@type":`WebSite`,"@id":f,url:`${a}/`,name:c,alternateName:n.site.alternateName,description:n.site.description,publisher:X(d),inLanguage:s})];if(e.noindex)return E;let D=Z({"@type":g||_?`WebPage`:h,"@id":p,url:o,name:l,description:t.description,isPartOf:X(f),about:Jt(t)?X(d):void 0,primaryImageOfPage:C?X(w):void 0,image:C?X(w):void 0,breadcrumb:T.length>0?X(m):void 0,mainEntity:_?X(y):v?X(b):void 0,datePublished:x,dateModified:S,inLanguage:s});if(E.push(D),g){let e=(r?.authors??[]).map(e=>Z({"@type":`Person`,name:e.name,url:e.url})),n=t.articleMeta?.author?[{"@type":`Person`,name:t.articleMeta.author}]:[X(d)];E.push(Z({"@type":h,"@id":`${o}#article`,isPartOf:X(p),mainEntityOfPage:X(p),headline:en(l,110),description:t.description,image:C?X(w):void 0,datePublished:x,dateModified:S,author:e.length>0?e:n,publisher:X(d),inLanguage:s}))}_&&E.push(Z({"@type":`Service`,"@id":y,name:l,description:t.description,url:o,image:C?X(w):void 0,provider:X(d),areaServed:n.site.areaServed})),v&&E.push(Z({"@type":`Person`,"@id":b,name:l,description:t.description,url:o,image:C?X(w):void 0,worksFor:u?void 0:X(d)})),C&&E.push({"@type":`ImageObject`,"@id":w,url:C,contentUrl:C,...Ve(r?.images,C,a)}),T.length>0&&E.push({"@type":`BreadcrumbList`,"@id":m,itemListElement:T.map((e,t)=>Z({"@type":`ListItem`,position:t+1,name:e.name,item:t===T.length-1?void 0:e.url}))});let O=new Set([`${a}/#logo`]);for(let e of E)typeof e[`@id`]==`string`&&O.add(e[`@id`]);let k=tn(E,r?.custom??[],O);return k!==void 0&&!g&&D.mainEntity===void 0&&(D.mainEntity=X(k)),E}const rn=`default`;function an(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function on(t){let n=e(t);if(!n)return t;let r=n.pathname.split(`/`).filter(e=>e!==``);return[n.host,...r].join(` › `)}function sn(e,t){return e.length<=t?e:`${e.slice(0,t-1).trimEnd()}…`}async function cn(n,r,i,o){if(!r)return{blocks:[]};let s=(i??0)>=50,c=an(n)?n:{},l=h(await o.kv.get(t)),u=B(o),d=z(r.collection,r.id),f,p=s&&c.type===`block_action`&&c.action_id===`set_type`,[m,g]=await Promise.all([Ze(o,r.collection,r.id,l),o.content?.getPublicUrl?.(r.collection,r.id)]),_=m?.record,v=m?.entry;if(_&&p){let e=typeof c.value==`string`?c.value:rn,t=x.find(t=>t.value===e)?.value,{type:n,...r}=_;_=t?{...r,type:t}:r,await u.put(d,_),f={type:`success`,message:`Saved. Google will pick this up next time it visits.`}}if(!_||!v)return{blocks:[{type:`context`,text:`Save this page to see how it may look on Google.`}]};if(_.status===`skip`)return{blocks:[{type:`context`,text:`This kind of content has no page of its own, so Google only sees it inside other pages. There is nothing to set here.`}]};let y=l.site.name??o.site.name,b=o.site.url.replace(/\/+$/,``),C=[`home`,`index`,``].includes(v.slug??``)?`${b}/`:`${b}/${v.slug}`,w=[`home`,`index`].includes(v.slug??``)?`${b}/`:g??C,T=_.title||`(no title)`,E=v.seo?.description?.trim()??``,D=E===``&&L(v.data)!==``,O=E||L(v.data),k=l.collections[r.collection]?.type??a(r.collection),ee=_.type??k,te=_.status===`hidden`,A=_.issues??[],j=[{type:`context`,text:y?`${y} · ${on(w)}`:on(w)},{type:`header`,text:Ee(T)},{type:`section`,text:O===``?`No description yet. Google will pick some text from the page.`:sn(O,160)},...D?[{type:`context`,text:`This description is the page's excerpt. To write a different one for search, fill in Meta Description in the SEO panel.`}]:[],{type:`context`,text:`What Google sees: the published page, with the SEO title and description as last saved. Google may rewrite them, and your site may add its name to the title.`},{type:`fields`,fields:[{label:`Shown to Google as`,value:S(ee)},{label:`In search results`,value:_.status===`draft`?`Not published yet`:te?`Hidden from search`:`Yes`}]}];if(A.length===0)j.push({type:`context`,text:`Nothing to fix on this page.`});else{let e=[...A].sort((e,t)=>Number(t.level===`fix`)-Number(e.level===`fix`));for(let t of e.slice(0,3))j.push({type:`section`,text:`${t.level===`fix`?`To fix`:`Worth a look`}: ${t.message}`},{type:`context`,text:t.fix});e.length>3&&j.push({type:`context`,text:`And ${e.length-3} more.`})}if(s){let t=nn({page:{url:w,path:e(w)?.pathname??`/`,locale:v.locale,kind:`content`,pageType:`website`,title:T,pageTitle:T,description:O===``?null:O,canonical:v.seo?.canonical??null,image:null,content:{collection:r.collection,id:r.id,slug:v.slug}},config:l,record:_,noindex:te,site:o.site});j.push({type:`divider`},{type:`actions`,elements:[{type:`menu`,action_id:`set_type`,label:`Change page type`,items:[{label:`Same as other pages here (${S(k)})`,value:rn},...x.map(({value:e,label:t})=>({value:e,label:t}))]},.../^https?:\/\//.test(w)?[{type:`link`,label:`Test on Google`,target:{kind:`external`,url:`https://search.google.com/test/rich-results?url=${encodeURIComponent(w)}`}}]:[]]},{type:`accordion`,label:`View the markup`,blocks:[{type:`context`,text:`The schema markup (JSON-LD) this page sends to search engines.`},{type:`code`,language:`jsonc`,code:JSON.stringify({"@context":`https://schema.org`,"@graph":t},null,2)}]})}return{blocks:j,...f?{toast:f}:{}}}const ln=/\s*,\s*/;function un(e,t,n){let r=(e??``).toLowerCase().split(ln).map(e=>e.trim()).filter(e=>e!==``),i=t||r.includes(`noindex`)||r.includes(`none`),a=r.includes(`nofollow`)||r.includes(`none`),o=[i?`noindex`:`index`,a?`nofollow`:`follow`],s=new Set([`index`,`noindex`,`follow`,`nofollow`,`none`,`all`]),c=r.filter(e=>!s.has(e));if(!i){let e=e=>c.some(t=>t.startsWith(e));n.nosnippet||c.includes(`nosnippet`)?c.includes(`nosnippet`)||c.push(`nosnippet`):e(`max-snippet:`)||c.push(`max-snippet:${n.maxSnippet}`),e(`max-image-preview:`)||c.push(`max-image-preview:${n.maxImagePreview}`),e(`max-video-preview:`)||c.push(`max-video-preview:${n.maxVideoPreview}`)}return[...o,...new Set(c)].join(`, `)}function dn(e){return e.startsWith(`noindex`)}async function Q(e){return h(await e.kv.get(t))}async function $(e,t,n){let r=n?.id;if(typeof r==`string`)try{await Xe(e,t,r,await Q(e))}catch(n){e.log.warn(`could not refresh entry record`,{collection:t,id:r,error:String(n)})}}const fn={hooks:{"page:metadata":async(e,t)=>{let{page:n}=e,r=await Q(t),i=null;if(n.content){let{collection:e,id:a}=n.content;try{let o=B(t),s=await o.get(z(e,a));i=s??await Xe(t,e,a,r);let c=!!(n.seo?.ogImage||n.image);if(s&&Je(s,c)){let t=await o.get(z(e,a)),n=t?Je(t,c):null;n&&await o.put(z(e,a),n),i=n??t??s}}catch(n){t.log.warn(`could not load entry record`,{collection:e,id:a,error:String(n)})}}let a=n.content?r.collections[n.content.collection]:void 0,o=un(n.seo?.robots,a?.noindex===!0,r.robots);return[{kind:`meta`,name:`robots`,content:o},{kind:`jsonld`,id:`primary`,graph:{"@context":`https://schema.org`,"@graph":nn({page:n,config:r,record:i,noindex:dn(o),site:t.site})}}]},"content:afterSave":async(e,t)=>{await $(t,e.collection,e.content)},"content:afterPublish":async(e,t)=>{await $(t,e.collection,e.content)},"content:afterUnpublish":async(e,t)=>{await $(t,e.collection,e.content)},"content:afterRestore":async(e,t)=>{await $(t,e.collection,e.content)},"plugin:activate":async(e,t)=>{try{if(await tt(t))return;await nt(t,(await t.schema?.listCollections()??[]).filter(e=>e.routable&&!e.hidden).map(e=>e.slug),await Q(t))}catch(e){t.log.warn(`could not start the first page check`,{error:String(e)})}},cron:async(e,t)=>{if(e.name===H){let e=await tt(t);e?.status===`running`?await ot(t,e,await Q(t)):await t.cron?.cancel(H);return}if(e.name!==N)return;let n=await ie(t);n?.status===`adding`?await ce(t,n):await t.cron?.cancel(N)},"content:afterDelete":async(e,t)=>{await Qe(t,e.collection,e.id)}},routes:{admin:{permission:`plugins:read`,handler:async(e,t)=>Wt(e.input,e.user?.role,t)},"editor/preview":{permission:`content:read`,handler:async(e,t)=>cn(e.input,e.ui?.entry,e.user?.role,t)},hello:{handler:async(e,t)=>(t.log.info(`hello route called`,{pluginId:t.plugin.id}),{greeting:`hello`,pluginId:t.plugin.id})}}};export{fn as default};
6
+ `)))].filter(e=>e!==``),o=[];for(let t of a){let n=e(/^https?:\/\//i.test(t)?t:`https://${t}`);if(!n||!n.host.includes(`.`))return{error:`"${t}" is not a web address. Paste the full link to the profile.`,state:r};o.push(n.href)}return{config:{...t.config,site:{...t.config.site,sameAs:o}},state:r}}function Bt(e,t){let n={tab:W.identity},{site:r}=e.config,i=J(t,`founded`,r.foundingDate??``);return i!==``&&!/^\d{4}(-\d{2}-\d{2})?$/.test(i)?{error:`Write the founding date as a year, like 2012, or as 2012-05-01.`,state:n}:{config:{...e.config,site:{...r,alternateName:J(t,`alternate_name`,r.alternateName??``)||void 0,legalName:J(t,`legal_name`,r.legalName??``)||void 0,foundingDate:i||void 0}},state:n}}function Vt(e,t){let{robots:n}=e.config,r=J(t,`snippet`,n.nosnippet?`none`:`full`),i=J(t,`image_preview`,n.maxImagePreview);return{config:{...e.config,breadcrumbs:gt(t,`breadcrumbs`,e.config.breadcrumbs),robots:{...n,nosnippet:r===`none`,maxSnippet:r===`short`?160:-1,maxImagePreview:i===`none`||i===`standard`||i===`large`?i:n.maxImagePreview}},state:{tab:W.types}}}function Ht(e,t,n){let r={tab:W.types},[i,a]=typeof t==`string`?t.split(`|`):[];if(!i||!e.collections.some(e=>e.slug===i))return{error:`That content type no longer exists.`,state:r};let o=e.config.collections[i]??{},s=n===`type`?x.find(e=>e.value===a)?.value:o.type??Mt(e,i);if(!s)return{error:`Choose one of the listed types.`,state:r};let c=n===`visible`?a===`hide`:o.noindex===!0;return{config:{...e.config,collections:{...e.config.collections,[i]:{type:s,noindex:c}},steps:{...e.config.steps,types:!0}},state:r}}async function Ut(e,t){if(t.scan?.status===`done`)return yt(e,t);let n=t.scan?await ot(e,t.scan,t.config):await nt(e,t.collections.map(e=>e.slug),t.config);return{...t,scan:n,stats:n.status===`done`?n.tally:null}}async function Wt(e,n,r){let i=await vt(r),a=(n??0)>=50,o=ht(e)?e:{},s=typeof o.action_id==`string`?o.action_id:``;if(o.type===`page_load`&&o.page===`widget:seo`)return At(await Ut(r,i));if(!a||o.type===`page_load`||s===``)return Y(await Ut(r,i),r,a,{tab:W.overview});let c=async(e,n=`Saved. Google will pick this up next time it visits.`)=>{if(`error`in e)return Y(i,r,a,{...e.state,toast:{type:`error`,message:e.error}});let o=h(e.config);return await r.kv.set(t,o),Y({...i,config:o},r,a,{...e.state,toast:{type:`success`,message:n}})},l=async(e,t,n)=>{if(e)return Y(i,r,a,{tab:W.redirects,editing:n,...n?{}:{panel:`add`},problem:e});let{rows:o,more:s}=await me(r);return Y({...i,redirectRows:o,moreRedirects:s},r,a,{tab:W.redirects,toast:{type:`success`,message:t}})};if(o.type===`block_action`){if(s===`check_pages`||s===`check_more`){let e=await Ut(r,s===`check_pages`?{...i,scan:null}:i);return Y(e,r,a,{tab:W.overview,toast:e.scan?.status===`done`?{type:`success`,message:`All pages checked.`}:{type:`info`,message:`${e.scan?.checked??0} pages checked so far. The rest follow in the background.`}})}if(s===`set_type`)return c(Ht(i,o.value,`type`));if(s===`set_visible`){let[e,t]=typeof o.value==`string`?o.value.split(`|`):[];return t===`hide`?Y(i,r,a,{tab:W.types,pendingHide:e}):c(Ht(i,o.value,`visible`),`These pages can appear on Google again.`)}if(s===`confirm_hide`&&typeof o.value==`string`)return c(Ht(i,`${o.value}|hide`,`visible`),`These pages are now hidden from Google and other search engines.`);if(s===`cancel_hide`)return Y(i,r,a,{tab:W.types});if(s===`hidden_ok`){let e={...i.config.collections};for(let t of wt(i))e[t.slug]={...e[t.slug],hiddenOk:!0};return c({config:{...i.config,collections:e},state:{tab:W.overview}},`Noted. These pages stay hidden from search.`)}if(s===`edit_redirect`){let e=i.redirectRows.find(e=>e.id===o.value);return Y(i,r,a,{tab:W.redirects,editing:e})}if(s===`open_add`)return Y(i,r,a,{tab:W.redirects,panel:`add`});if(s===`open_import`)return Y(i,r,a,{tab:W.redirects,panel:`import`});if(s===`close_panel`)return Y(i,r,a,{tab:W.redirects});if(s===`remove_redirect`&&typeof o.value==`string`)return l(await ye(r,o.value),`Redirect removed.`);if(s===`add_redirects`||s===`more_redirects`){if(!i.redirects||i.redirects.status===`done`)return Y(i,r,a,{tab:W.redirects});let e=await ce(r,i.redirects);return Y({...i,redirects:e},r,a,{tab:W.redirects,toast:e.status===`done`?{type:`success`,message:`${P(e.added,`redirect`)} added.`}:{type:`info`,message:`${e.added} added so far. The rest follow in the background.`}})}if(s===`stop_redirects`&&i.redirects?.status===`adding`){let e=await Ce(r,i.redirects);return Y({...i,redirects:e},r,a,{tab:W.redirects,toast:{type:`info`,message:`Stopped. ${P(e.added,`redirect`)} added.`}})}if(s===`reset_redirects`)return i.redirects?.status!==`adding`&&await r.kv.delete(M),Y({...i,redirects:i.redirects?.status===`adding`?i.redirects:null},r,a,{tab:W.redirects});if(s===`remove_redirects`&&i.redirects){let e=await le(r,i.redirects);return Y({...i,redirects:e===0?null:i.redirects},r,a,{tab:W.redirects,toast:e===0?{type:`success`,message:`Removed. Your other redirects are untouched.`}:{type:`info`,message:`${e} still to remove. Press Remove again.`}})}if(s===`export_redirects`){let{text:e,count:t}=await ue(r);return Y(i,r,a,{tab:W.redirects,panel:`export`,exported:e,toast:{type:`info`,message:`${P(t,`redirect`)} listed below.`}})}return Y(i,r,a,{tab:W.overview})}if(o.type!==`form_submit`)return Y(i,r,a,{tab:W.overview});let u=ht(o.values)?o.values:{};if(s===`check_redirects`){let e=J(u,`list`);if(e===``)return Y(i,r,a,{tab:W.redirects,toast:{type:`error`,message:`Paste a list first.`}});let t=await oe(r,e);return Y({...i,redirects:t},r,a,{tab:W.redirects,toast:{type:`success`,message:`Checked. Nothing has been added yet.`}})}if(s===`add_redirect`){let e=J(u,`source`),t=J(u,`code`,`301`)===`410`?``:J(u,`destination`);return l(await _e(r,e,t,J(u,`code`,`301`)),t===``?`Done. ${e} now tells visitors the page was removed.`:`Redirect added. ${e} now goes to ${t}.`)}if(s.startsWith(`save_redirect:`)){let e=s.slice(14),t=i.redirectRows.find(t=>t.id===e);return l(await ve(r,e,J(u,`source`,t?.source??``),J(u,`destination`,t?.destination??``),J(u,`code`,String(t?.type??301))),`Redirect changed.`,t)}if(s===`setup_identity`){let e=Ft(i,u);if(`error`in e)return c(e);let t=J(u,`street`),n=J(u,`town`),{site:r}=e.config,a=t!==``||n!==``?{...r.address,...t?{streetAddress:t}:{},...n?{addressLocality:n}:{}}:r.address;return c({config:{...e.config,site:{...r,address:a}},state:{tab:W.overview}})}return s===`save_identity`?c(Ft(i,u)):s===`save_logo`?c(It(i,u,r.site.url)):s===`save_contact`?c(Lt(i,u)):s===`save_hours`?c(Rt(i,u)):s===`save_profiles`?c(zt(i,u)):s===`save_company`?c(Bt(i,u)):s===`save_advanced`?c(Vt(i,u)):Y(i,r,a,{tab:W.overview})}function X(e){return{"@id":e}}function Z(e){let t={};for(let[n,r]of Object.entries(e))r==null||r===``||Array.isArray(r)&&r.length===0||(t[n]=r);return t}function Gt(t,n){return e(t,n)?.href??t}function Kt(t,n){return(t.siteUrl||n||(e(t.url)?.origin??``)).replace(/\/+$/,``)}function qt(t,n){if(t.canonical)return Gt(t.canonical,`${n}/`);let r=e(t.url,`${n}/`);return r?(r.search=``,r.hash=``,r.href):t.url}function Jt(e){return e.path===`/`||e.path===``}function Yt(e){return r.includes(e)}function Xt(e,t,n){let r=e.content?t.collections[e.content.collection]:void 0;if(n?.type)return n.type;if(r?.type)return r.type;let i=e.content?a(e.content.collection):`WebPage`;return i===`WebPage`?e.pageType===`article`?`BlogPosting`:`WebPage`:i}function Zt(e,t,n,r){let i=`${n}/#logo`,a=e.type===`Person`,o=!a&&e.type!==`Organization`&&e.address===void 0?`Organization`:e.type,s=!a&&(e.address!==void 0||e.geo!==void 0||e.openingHours.length>0);return Z({"@type":o,"@id":r,name:t,alternateName:e.alternateName,legalName:a?void 0:e.legalName,description:e.description,url:`${n}/`,logo:e.logo&&!a?{"@type":`ImageObject`,"@id":i,url:e.logo,contentUrl:e.logo,caption:t}:void 0,image:e.logo?a?e.logo:X(i):void 0,sameAs:e.sameAs,email:e.email,telephone:e.telephone,address:e.address?{"@type":`PostalAddress`,...e.address}:void 0,geo:e.geo&&!a?{"@type":`GeoCoordinates`,...e.geo}:void 0,openingHoursSpecification:s?e.openingHours.map(e=>({"@type":`OpeningHoursSpecification`,dayOfWeek:e.days,opens:e.opens,closes:e.closes})):void 0,priceRange:a?void 0:e.priceRange,areaServed:a?void 0:e.areaServed,foundingDate:a?void 0:e.foundingDate,vatID:a?void 0:e.vatID,taxID:a?void 0:e.taxID,numberOfEmployees:!a&&e.numberOfEmployees?{"@type":`QuantitativeValue`,value:e.numberOfEmployees}:void 0})}function Qt(e){let t=e;try{t=decodeURIComponent(e)}catch{}let n=t.replace(/[-_]+/g,` `).trim();return n.charAt(0).toUpperCase()+n.slice(1)}function $t(e,t,n){let{page:r,config:i,site:a}=e;if(r.breadcrumbs)return r.breadcrumbs.map(e=>({name:e.name,url:Gt(e.url,`${t}/`)}));if(!i.breadcrumbs||Jt(r))return[];let o=r.path.split(`/`).filter(e=>e!==``);if(o.length===0)return[];let s=a.trailingSlash===`always`?`/`:``,c=[{name:`Home`,url:`${t}/`}],l=``;return o.forEach((e,r)=>{l+=`/${e}`;let i=r===o.length-1;c.push({name:i?n:Qt(e),url:`${t}${l}${s}`})}),c}function en(e,t){return e.length<=t?e:`${e.slice(0,t-1).trimEnd()}…`}function tn(e,t,n){let r;for(let{"@context":i,...a}of t){let t=typeof a[`@id`]==`string`?a[`@id`]:void 0;if(t!==void 0){if(n.has(t))continue;n.add(t),r??=t}e.push(a)}return r}function nn(e){let{page:t,config:n,record:r,site:i}=e,a=Kt(t,i.url),o=qt(t,a),s=t.locale??i.locale,c=n.site.name??t.siteName??i.name,l=t.pageTitle??t.title??c,u=n.site.type===`Person`,d=`${a}/#${u?`person`:`organization`}`,f=`${a}/#website`,p=o,m=`${o}#breadcrumb`,h=Xt(t,n,r),g=Yt(h),_=h===`Service`,v=h===`ProfilePage`,y=`${o}#service`,b=`${o}#person`,x=r?.publishedAt??t.articleMeta?.publishedTime??void 0,S=r?.updatedAt??t.articleMeta?.modifiedTime??void 0,C=t.image?Gt(t.image,`${a}/`):void 0,w=C??``,T=$t(e,a,l),E=[Zt(n.site,c,a,d),Z({"@type":`WebSite`,"@id":f,url:`${a}/`,name:c,alternateName:n.site.alternateName,description:n.site.description,publisher:X(d),inLanguage:s})];if(e.noindex)return E;let D=Z({"@type":g||_?`WebPage`:h,"@id":p,url:o,name:l,description:t.description,isPartOf:X(f),about:Jt(t)?X(d):void 0,primaryImageOfPage:C?X(w):void 0,image:C?X(w):void 0,breadcrumb:T.length>0?X(m):void 0,mainEntity:_?X(y):v?X(b):void 0,datePublished:x,dateModified:S,inLanguage:s});if(E.push(D),g){let e=(r?.authors??[]).map(e=>Z({"@type":`Person`,name:e.name,url:e.url})),n=t.articleMeta?.author?[{"@type":`Person`,name:t.articleMeta.author}]:[X(d)];E.push(Z({"@type":h,"@id":`${o}#article`,isPartOf:X(p),mainEntityOfPage:X(p),headline:en(l,110),description:t.description,image:C?X(w):void 0,datePublished:x,dateModified:S,author:e.length>0?e:n,publisher:X(d),inLanguage:s}))}_&&E.push(Z({"@type":`Service`,"@id":y,name:l,description:t.description,url:o,image:C?X(w):void 0,provider:X(d),areaServed:n.site.areaServed})),v&&E.push(Z({"@type":`Person`,"@id":b,name:l,description:t.description,url:o,image:C?X(w):void 0,worksFor:u?void 0:X(d)})),C&&E.push({"@type":`ImageObject`,"@id":w,url:C,contentUrl:C,...He(r?.images,C,a)}),T.length>0&&E.push({"@type":`BreadcrumbList`,"@id":m,itemListElement:T.map((e,t)=>Z({"@type":`ListItem`,position:t+1,name:e.name,item:t===T.length-1?void 0:e.url}))});let O=new Set([`${a}/#logo`]);for(let e of E)typeof e[`@id`]==`string`&&O.add(e[`@id`]);let k=tn(E,r?.custom??[],O);return k!==void 0&&!g&&D.mainEntity===void 0&&(D.mainEntity=X(k)),E}const rn=`default`;function an(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function on(t){let n=e(t);if(!n)return t;let r=n.pathname.split(`/`).filter(e=>e!==``);return[n.host,...r].join(` › `)}function sn(e,t){return e.length<=t?e:`${e.slice(0,t-1).trimEnd()}…`}async function cn(n,r,i,o){if(!r)return{blocks:[]};let s=(i??0)>=50,c=an(n)?n:{},l=h(await o.kv.get(t)),u=z(o),d=R(r.collection,r.id),f,p=s&&c.type===`block_action`&&c.action_id===`set_type`,[m,g]=await Promise.all([Qe(o,r.collection,r.id,l),o.content?.getPublicUrl?.(r.collection,r.id)]),_=m?.record,v=m?.entry;if(_&&p){let e=typeof c.value==`string`?c.value:rn,t=x.find(t=>t.value===e)?.value,{type:n,...r}=_;_=t?{...r,type:t}:r,await u.put(d,_),f={type:`success`,message:`Saved. Google will pick this up next time it visits.`}}if(!_||!v)return{blocks:[{type:`context`,text:`Save this page to see how it may look on Google.`}]};if(_.status===`skip`)return{blocks:[{type:`context`,text:`This kind of content has no page of its own, so Google only sees it inside other pages. There is nothing to set here.`}]};let y=l.site.name??o.site.name,b=o.site.url.replace(/\/+$/,``),C=[`home`,`index`,``].includes(v.slug??``)?`${b}/`:`${b}/${v.slug}`,w=[`home`,`index`].includes(v.slug??``)?`${b}/`:g??C,T=_.title||`(no title)`,E=v.seo?.description?.trim()??``,D=E===``&&L(v.data)!==``,O=E||L(v.data),k=l.collections[r.collection]?.type??a(r.collection),ee=_.type??k,te=_.status===`hidden`,A=_.issues??[],j=[{type:`context`,text:y?`${y} · ${on(w)}`:on(w)},{type:`header`,text:Ee(T)},{type:`section`,text:O===``?`No description yet. Google will pick some text from the page.`:sn(O,160)},...D?[{type:`context`,text:`This description is the page's excerpt. To write a different one for search, fill in Meta Description in the SEO panel.`}]:[],{type:`context`,text:`What Google sees: the published page, with the SEO title and description as last saved. Google may rewrite them, and your site may add its name to the title.`},{type:`fields`,fields:[{label:`Shown to Google as`,value:S(ee)},{label:`In search results`,value:_.status===`draft`?`Not published yet`:te?`Hidden from search`:`Yes`}]}];if(A.length===0)j.push({type:`context`,text:`Nothing to fix on this page.`});else{let e=[...A].sort((e,t)=>Number(t.level===`fix`)-Number(e.level===`fix`));for(let t of e.slice(0,3))j.push({type:`section`,text:`${t.level===`fix`?`To fix`:`Worth a look`}: ${t.message}`},{type:`context`,text:t.fix});e.length>3&&j.push({type:`context`,text:`And ${e.length-3} more.`})}if(s){let t=nn({page:{url:w,path:e(w)?.pathname??`/`,locale:v.locale,kind:`content`,pageType:`website`,title:T,pageTitle:T,description:O===``?null:O,canonical:v.seo?.canonical??null,image:null,content:{collection:r.collection,id:r.id,slug:v.slug}},config:l,record:_,noindex:te,site:o.site});j.push({type:`divider`},{type:`actions`,elements:[{type:`menu`,action_id:`set_type`,label:`Change page type`,items:[{label:`Same as other pages here (${S(k)})`,value:rn},...x.map(({value:e,label:t})=>({value:e,label:t}))]},.../^https?:\/\//.test(w)?[{type:`link`,label:`Test on Google`,target:{kind:`external`,url:`https://search.google.com/test/rich-results?url=${encodeURIComponent(w)}`}}]:[]]},{type:`accordion`,label:`View the markup`,blocks:[{type:`context`,text:`The schema markup (JSON-LD) this page sends to search engines.`},{type:`code`,language:`jsonc`,code:JSON.stringify({"@context":`https://schema.org`,"@graph":t},null,2)}]})}return{blocks:j,...f?{toast:f}:{}}}const ln=/\s*,\s*/;function un(e,t,n){let r=(e??``).toLowerCase().split(ln).map(e=>e.trim()).filter(e=>e!==``),i=t||r.includes(`noindex`)||r.includes(`none`),a=r.includes(`nofollow`)||r.includes(`none`),o=[i?`noindex`:`index`,a?`nofollow`:`follow`],s=new Set([`index`,`noindex`,`follow`,`nofollow`,`none`,`all`]),c=r.filter(e=>!s.has(e));if(!i){let e=e=>c.some(t=>t.startsWith(e));n.nosnippet||c.includes(`nosnippet`)?c.includes(`nosnippet`)||c.push(`nosnippet`):e(`max-snippet:`)||c.push(`max-snippet:${n.maxSnippet}`),e(`max-image-preview:`)||c.push(`max-image-preview:${n.maxImagePreview}`),e(`max-video-preview:`)||c.push(`max-video-preview:${n.maxVideoPreview}`)}return[...o,...new Set(c)].join(`, `)}function dn(e){return e.startsWith(`noindex`)}async function Q(e){return h(await e.kv.get(t))}async function $(e,t,n){let r=n?.id;if(typeof r==`string`)try{await Ze(e,t,r,await Q(e))}catch(n){e.log.warn(`could not refresh entry record`,{collection:t,id:r,error:String(n)})}}const fn={hooks:{"page:metadata":async(e,t)=>{let{page:n}=e,r=await Q(t),i=null;if(n.content){let{collection:e,id:a}=n.content;try{let o=z(t),s=await o.get(R(e,a));i=s??await Ze(t,e,a,r);let c=!!(n.seo?.ogImage||n.image);if(s&&Ye(s,c)){let t=await o.get(R(e,a)),n=t?Ye(t,c):null;n&&await o.put(R(e,a),n),i=n??t??s}}catch(n){t.log.warn(`could not load entry record`,{collection:e,id:a,error:String(n)})}}let a=n.content?r.collections[n.content.collection]:void 0,o=un(n.seo?.robots,a?.noindex===!0,r.robots);return[{kind:`meta`,name:`robots`,content:o},{kind:`jsonld`,id:`primary`,graph:{"@context":`https://schema.org`,"@graph":nn({page:n,config:r,record:i,noindex:dn(o),site:t.site})}}]},"content:afterSave":async(e,t)=>{await $(t,e.collection,e.content)},"content:afterPublish":async(e,t)=>{await $(t,e.collection,e.content)},"content:afterUnpublish":async(e,t)=>{await $(t,e.collection,e.content)},"content:afterRestore":async(e,t)=>{await $(t,e.collection,e.content)},"plugin:activate":async(e,t)=>{try{if(await tt(t))return;let e=await nt(t,(await t.schema?.listCollections()??[]).filter(e=>e.routable&&!e.hidden).map(e=>e.slug),await Q(t));e.status===`done`&&e.checked===0&&await t.kv.delete(V)}catch(e){t.log.warn(`could not start the first page check`,{error:String(e)})}},cron:async(e,t)=>{if(e.name===H){let e=await tt(t);e?.status===`running`?await ot(t,e,await Q(t)):await t.cron?.cancel(H);return}if(e.name!==N)return;let n=await ie(t);n?.status===`adding`?await ce(t,n):await t.cron?.cancel(N)},"content:afterDelete":async(e,t)=>{await $e(t,e.collection,e.id)}},routes:{admin:{permission:`plugins:read`,handler:async(e,t)=>Wt(e.input,e.user?.role,t)},"editor/preview":{permission:`content:read`,handler:async(e,t)=>cn(e.input,e.ui?.entry,e.user?.role,t)},hello:{handler:async(e,t)=>(t.log.info(`hello route called`,{pluginId:t.plugin.id}),{greeting:`hello`,pluginId:t.plugin.id})}}};export{fn as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moorage/plugin-seo",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "SEO for EmDash: schema markup, JSON-LD structured data, Google preview, SEO checks, noindex, WordPress redirects import. Yoast alternative.",
5
5
  "keywords": [
6
6
  "emdash",
@@ -51,11 +51,11 @@
51
51
  "emdash": ">=1.2.0"
52
52
  },
53
53
  "devDependencies": {
54
- "@emdash-cms/blocks": "1.2.0",
55
- "@emdash-cms/plugin-cli": "0.13.3",
56
- "@emdash-cms/plugin-test": "0.2.8",
54
+ "@emdash-cms/blocks": "1.3.1",
55
+ "@emdash-cms/plugin-cli": "0.13.4",
56
+ "@emdash-cms/plugin-test": "0.2.10",
57
57
  "@moorage/typescript-config": "0.0.0",
58
- "emdash": "1.2.0",
58
+ "emdash": "1.3.1",
59
59
  "typescript": "~6.0.3",
60
60
  "vitest": "4.1.10"
61
61
  },