🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

staticsearch

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

staticsearch - npm Package Compare versions

Comparing version
0.6.0
to
0.7.0
+12
lib/util.js
// utility functions
import process from 'node:process';
// process an environment variable
export function env(name, def = null) {
const val = process.env[name];
if (!val) return def;
const num = +val;
return isNaN(num) ? val : num;
}
+1
-1

@@ -1,1 +0,1 @@

:host{--fsize: var(--staticsearch-fontsize, 1em);--dmax: var(--staticsearch-maxwidth, 60ch);--dmargin: var(--staticsearch-margin, 3vmin);--dpadding: var(--staticsearch-padding, 2vmin);--sheight: var(--staticsearch-fieldset-height, calc(3em + (2 * var(--dpadding))) );--colback0: var(--staticsearch-color-back, Canvas);--colbord0: var(--staticsearch-color-border, ButtonFace);--colfore0: var(--staticsearch-color-fore0, CanvasText);--colfore1: var(--staticsearch-color-fore1, color-mix(in oklab, CanvasText 80%, Canvas));--colfore2: var(--staticsearch-color-fore2, color-mix(in oklab, CanvasText 60%, Canvas));--collinkd: var(--staticsearch-color-link, color-mix(in oklab, LinkText 80%, CanvasText));--collinkv: var(--staticsearch-color-visited, color-mix(in oklab, VisitedText 80%, CanvasText));--colshad0: var(--staticsearch-color-shadow, #000);--colshad1: var(--staticsearch-color-backdrop, color-mix(in srgb, var(--colshad0), transparent 25%));--colblur0: var(--staticsearch-backdrop-blur, 3px)}template{display:none}*{box-sizing:border-box;font-size:inherit;padding:0;margin:0}@media(prefers-reduced-motion:reduce){*,*:before,*:after,::backdrop{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;transition-behavior:normal!important;scroll-behavior:auto!important}}dialog{position:fixed;font-size:var(--fsize);inline-size:var(--dmax);block-size:100%;max-inline-size:calc(100dvw - (2 * var(--dmargin)));max-block-size:calc(100dvh - (2 * var(--dmargin)));margin:auto;padding:0;color:var(--colfore0);background-color:var(--colback0);border:1px solid var(--colbord0);border-radius:5px;box-shadow:0 2px 4px var(--colshad0);overflow:hidden;transform-origin:50% 0;&,&::backdrop{transition:all .3s ease-in-out;transition-behavior:allow-discrete}&[open]{opacity:1;transform:scale(1);@starting-style{display:block;opacity:.5;transform:scaleX(.5) scaleY(0)}&::backdrop{background-color:var(--colshad1);-webkit-backdrop-filter:blur(var(--colblur0));backdrop-filter:blur(var(--colblur0));@starting-style{background-color:#0000;-webkit-backdrop-filter:blur(0);backdrop-filter:blur(0)}}}&:not([open]){display:none;opacity:0;transform:scaleX(.5) scaleY(0)}}[part=close]{position:absolute;inset-inline-end:0;inset-block-start:0;block-size:1.4em;inline-size:1.4em;min-block-size:unset;min-inline-size:unset;padding:0;margin:0;background-color:transparent;border:0 none;cursor:pointer;z-index:3;&:is(:hover,:focus,:active){color:var(--collinkd)}}[part=search]{position:absolute;block-size:var(--sheight);inset-inline:0;inset-block-start:0;padding-inline:var(--dpadding);padding-block-start:var(--dpadding);box-shadow:0 1px 5px var(--colshad0);overflow:hidden;z-index:2;&:focus-within{label,input{color:var(--colfore0)}input{border-color:var(--colfore1)}}}[part=searchlabel]{display:block;font-size:.8em;padding-inline-start:.625em;padding-block-end:.2em;color:var(--colfore1);-webkit-user-select:none;user-select:none}input[part=searchinput]{display:block;inline-size:100%;line-height:1.5;padding-block:.2em;padding-inline:.5em;color:var(--colfore1);background-color:transparent;border:1px solid transparent;border-radius:3px;outline-style:none}[part=results]{position:absolute;inset-inline:0;inset-block:0;inset-block-start:var(--sheight);padding:var(--dpadding);overflow-x:hidden;overflow-y:auto;&:after{position:fixed;content:"";block-size:5vh;inset-inline:0;inset-block-end:0;background:linear-gradient(transparent,var(--colback0));pointer-events:none}}[part=searchterm]{font-style:italic;&:before,&:after{content:'"'}}[part=item]{margin-block:1.5em;margin-inline-start:1.5em}[part=title]{font-weight:bolder;line-height:1.2;text-wrap:balance;text-underline-offset:.2em;margin-block-end:.1em}[part=meta]{font-size:.75em;line-height:1.4;color:var(--colfore2)}[part=description]{font-size:.85em;line-height:1.4;text-wrap:pretty;color:var(--colfore1)}a:focus-visible{outline-color:var(--collinkd);outline-style:solid;outline-offset:.2em;outline-width:.2rem}a:link{display:block;text-decoration:none;color:var(--collinkd)}a:visited{color:var(--collinkv)}a:hover,a:focus,a:active{color:var(--collinkd);[part=title]{text-decoration:underline}}
:host{--fsize: var(--staticsearch-fontsize, 1em);--dmax: var(--staticsearch-maxwidth, 60ch);--dmargin: var(--staticsearch-margin, 3vmin);--dpadding: var(--staticsearch-padding, 2vmin);--sheight: var(--staticsearch-fieldset-height, calc(3em + (2 * var(--dpadding))) );--colback0: var(--staticsearch-color-back, Canvas);--colbord0: var(--staticsearch-color-border, ButtonFace);--colfore0: var(--staticsearch-color-fore0, CanvasText);--colfore1: var(--staticsearch-color-fore1, color-mix(in oklab, CanvasText 80%, Canvas));--colfore2: var(--staticsearch-color-fore2, color-mix(in oklab, CanvasText 60%, Canvas));--collinkd: var(--staticsearch-color-link, color-mix(in oklab, LinkText 80%, CanvasText));--collinkv: var(--staticsearch-color-visited, color-mix(in oklab, VisitedText 80%, CanvasText));--colshad0: var(--staticsearch-color-shadow, #000);--colshad1: var(--staticsearch-color-backdrop, color-mix(in srgb, var(--colshad0), transparent 25%));--colblur0: var(--staticsearch-backdrop-blur, 3px)}template{display:none}*{box-sizing:border-box;font-size:inherit;padding:0;margin:0}@media(prefers-reduced-motion:reduce){*,*:before,*:after,::backdrop{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;transition-behavior:normal!important;scroll-behavior:auto!important}}[part=activate]{color:var(--colfore2);outline:none;&:hover,&:active,&:focus{color:var(--collinkd)}}dialog{position:fixed;font-size:var(--fsize);inline-size:var(--dmax);block-size:100%;max-inline-size:calc(100dvi - (2 * var(--dmargin)));max-block-size:calc(100dvb - (2 * var(--dmargin)));margin:auto;padding:0;color:var(--colfore0);background-color:var(--colback0);border:1px solid var(--colbord0);border-radius:5px;box-shadow:0 2px 4px var(--colshad0);overflow:hidden;transform-origin:50% 0;&,&::backdrop{transition:all .3s ease-in-out;transition-behavior:allow-discrete}&[open]{opacity:1;transform:scale(1);@starting-style{display:block;opacity:.5;transform:scaleX(.5) scaleY(0)}&::backdrop{background-color:var(--colshad1);-webkit-backdrop-filter:blur(var(--colblur0));backdrop-filter:blur(var(--colblur0));@starting-style{background-color:#0000;-webkit-backdrop-filter:blur(0);backdrop-filter:blur(0)}}}&:not([open]){display:none;opacity:0;transform:scaleX(.5) scaleY(0)}}[part=close]{position:absolute;inset-inline-end:0;inset-block-start:0;block-size:1.4em;inline-size:1.4em;min-block-size:unset;min-inline-size:unset;padding:0;margin:0;background-color:transparent;border:0 none;cursor:pointer;z-index:3;&:is(:hover,:focus,:active){color:var(--collinkd)}}[part=search]{position:absolute;block-size:var(--sheight);inset-inline:0;inset-block-start:0;padding-inline:var(--dpadding);padding-block-start:var(--dpadding);box-shadow:0 1px 5px var(--colshad0);overflow:hidden;z-index:2;&:focus-within{label,input{color:var(--colfore0)}input{border-color:var(--colfore1)}}}[part=searchlabel]{display:block;font-size:.8em;padding-inline-start:.625em;padding-block-end:.2em;color:var(--colfore1);-webkit-user-select:none;user-select:none}input[part=searchinput]{display:block;inline-size:100%;line-height:1.5;padding-block:.2em;padding-inline:.5em;color:var(--colfore1);background-color:transparent;border:1px solid transparent;border-radius:3px;outline-style:none}[part=results]{position:absolute;inset-inline:0;inset-block:0;inset-block-start:var(--sheight);padding:var(--dpadding);overflow:hidden auto;scrollbar-width:thin;scrollbar-color:var(--colfore2) transparent;overscroll-behavior-block:contain;outline:none;&:after{position:fixed;content:"";block-size:5vb;inset-inline:var(--dpadding);inset-block-end:0;background:linear-gradient(transparent,var(--colback0));pointer-events:none}}[part=searchterm]{font-style:italic;&:before,&:after{content:'"'}}[part=item]{margin-block:1.5em;margin-inline-start:1.5em}[part=title]{font-weight:bolder;line-height:1.2;text-wrap:balance;text-underline-offset:.2em;margin-block-end:.1em}[part=meta]{font-size:.75em;line-height:1.4;color:var(--colfore2)}[part=description]{font-size:.85em;line-height:1.4;text-wrap:pretty;color:var(--colfore1)}a:focus-visible{outline-color:var(--collinkd);outline-style:solid;outline-offset:.2em;outline-width:.2rem}a:link{display:block;text-decoration:none;color:var(--collinkd)}a:visited{color:var(--collinkv)}a:hover,a:focus,a:active{color:var(--collinkd);[part=title]{text-decoration:underline}}

@@ -1,1 +0,1 @@

import{staticsearch as v}from"./__SSDIR__/staticsearch.js";var E="q",y=0,b="__AGENT__",S=Symbol(b),C=500,w={date:new Intl.DateTimeFormat([],{dateStyle:"long"}).format,number:new Intl.NumberFormat([],{maximumFractionDigits:0}).format};(()=>{let t=document.getElementById("staticsearch_search"),e=document.getElementById("staticsearch_result");e&&A(e,{minFound:e.getAttribute("minfound"),minScore:e.getAttribute("minscore"),maxResults:e.getAttribute("maxresults"),highlight:e.hasAttribute("highlight")}),t&&t.tagName==="INPUT"&&R(t)})();function x(){return new URLSearchParams(location.search).get(E)||""}function L(t,e){let a=new URL(location);t?a.searchParams.set(E,t):a.searchParams.delete(E),a.hash=e||"",history.replaceState({},"",a.href)}function R(t){if(t[S])return;t[S]=!0,t.name&&(E=t.name);let e=x()||sessionStorage.getItem(b)||"";e&&(t.value=e);let a=t.value;a&&m(a);let h;t.addEventListener("input",n=>{let d=n.target.value;clearTimeout(h),h=setTimeout(()=>m(d),C)},!1);function m(n){sessionStorage.setItem(b,n),!(n.length<2)&&v.find(n).then(d=>{})}}function A(t,e={}){if(t[S])return;t[S]=!0;let a="",h=parseFloat(e.minFound)||0,m=parseFloat(e.minScore)||0,n=parseFloat(e.maxResults)||0,d=!!e.highlight,I=e.resultElement||"ol",p=e.messageTemplate||document.getElementById("staticsearch_resultmessage");p||(p=document.createElement("template"),p.innerHTML='<p part="resultmessage"><span part="resultcount">0</span> found for <span part="searchterm"></span>&hellip;</p>');let f=e.itemTemplate||document.getElementById("staticsearch_item");f||(f=document.createElement("template"),f.innerHTML='<li part="item"><a part="link"><h2 part="title"></h2><p part="meta"><time part="date"></time> &ndash; <span part="words">0</span> words</p><p part="description"></p></a></li>'),t.addEventListener("click",i=>{let c=i.target.closest("a");c&&a&&L(a,c.id)}),document.addEventListener("staticsearch:result",i=>{a=i.detail.search;let c=i.detail.result,g=d?"#:~:"+a.split(/\W+/).reverse().map(r=>"text="+encodeURIComponent(r.trim().toLowerCase())).join("&"):"";t.innerHTML="";let l=document.createElement(I);l.setAttribute("part","searchresult"),c.forEach((r,T)=>{if(r.found<h||m&&r.relevancy<m||n&&T>=n)return;let u=f.content.cloneNode(!0);o(u,"link",null,{href:r.url+g,id:`staticsearchresult-${r.id}`}),o(u,"title",r.title),o(u,"description",r.description),r.date&&o(u,"date",w.date(new Date(r.date)),{datetime:r.date}),r.words&&o(u,"words",w.number(r.words)),l.appendChild(u)});let s=p.content.cloneNode(!0);if(o(s,"resultcount",l.childElementCount),o(s,"searchterm",a),t.appendChild(s),t.appendChild(l),!y&&location.hash){let r=t.querySelector(location.hash);r&&(r.focus(),r.scrollIntoView({behavior:"smooth",block:"nearest"}))}else t.scrollTop=0;y++},!1);function o(i,c,g,l={}){i.querySelectorAll(`[part="${c}"]`).forEach(s=>{for(let[r,T]of Object.entries(l))s.setAttribute(r,T);if(g){for(;s.firstElementChild;)s=s.firstElementChild;s.textContent=g}})}}export{R as staticSearchInput,x as staticSearchQuery,A as staticSearchResult,L as staticSearchSetQuery};
import{staticsearch as v}from"./__SSDIR__/staticsearch.js";var S="q",w=0,T="__AGENT__",y=Symbol(T),C=500,I={date:new Intl.DateTimeFormat([],{dateStyle:"long"}).format,number:new Intl.NumberFormat([],{maximumFractionDigits:0}).format};(()=>{let t=document.getElementById("staticsearch_search"),r=document.getElementById("staticsearch_result");r&&L(r,{minFound:r.getAttribute("minfound"),minScore:r.getAttribute("minscore"),maxResults:r.getAttribute("maxresults"),highlight:r.hasAttribute("highlight")}),t&&t.tagName==="INPUT"&&F(t,{fuzzy:t.getAttribute("fuzzy")})})();function x(){return new URLSearchParams(location.search).get(S)||""}function A(t,r){let a=new URL(location);t?a.searchParams.set(S,t):a.searchParams.delete(S),a.hash=r||"",history.replaceState({},"",a.href)}function F(t,r={}){if(t[y])return;t[y]=!0;let a=parseFloat(r.fuzzy);(isNaN(a)||a<0)&&(a=6),t.name&&(S=t.name);let p=x()||sessionStorage.getItem(T)||"";p&&(t.value=p);let h=t.value;h&&f(h);let d;t.addEventListener("input",n=>{let o=n.target.value;clearTimeout(d),d=setTimeout(()=>f(o),C)},!1);function f(n){sessionStorage.setItem(T,n),!(n.length<2)&&v.find(n,a).then(o=>{})}}function L(t,r={}){if(t[y])return;t[y]=!0;let a="",p=parseFloat(r.minFound)||0,h=parseFloat(r.minScore)||0,d=parseFloat(r.maxResults)||0,f=!!r.highlight,n=r.resultElement||"ol",o=r.messageTemplate||document.getElementById("staticsearch_resultmessage");o||(o=document.createElement("template"),o.innerHTML='<p part="resultmessage"><span part="resultcount">0</span> found for <span part="searchterm"></span>&hellip;</p>');let g=r.itemTemplate||document.getElementById("staticsearch_item");g||(g=document.createElement("template"),g.innerHTML='<li part="item"><a part="link"><h2 part="title"></h2><p part="meta"><time part="date"></time> &ndash; <span part="words">0</span> words</p><p part="description"></p></a></li>'),t.addEventListener("click",l=>{let c=l.target.closest("a");c&&a&&A(a,c.id)}),document.addEventListener("staticsearch:result",l=>{a=l.detail.search;let c=l.detail.result,E=f?"#:~:"+a.split(/\W+/).reverse().map(e=>"text="+encodeURIComponent(e.trim().toLowerCase())).join("&"):"";t.innerHTML="";let u=document.createElement(n);u.setAttribute("part","searchresult"),c.forEach((e,b)=>{if(e.found<p||h&&e.relevancy<h||d&&b>=d)return;let m=g.content.cloneNode(!0);i(m,"link",null,{href:e.url+E,id:`staticsearchresult-${e.id}`}),i(m,"title",e.title),i(m,"description",e.description),e.date&&i(m,"date",I.date(new Date(e.date)),{datetime:e.date}),e.words&&i(m,"words",I.number(e.words)),u.appendChild(m)});let s=o.content.cloneNode(!0);if(i(s,"resultcount",u.childElementCount),i(s,"searchterm",a),t.appendChild(s),t.appendChild(u),!w&&location.hash){let e=t.querySelector(location.hash);e&&(e.focus(),e.scrollIntoView({behavior:"smooth",block:"nearest"}))}else t.scrollTop=0;w++},!1);function i(l,c,E,u={}){l.querySelectorAll(`[part="${c}"]`).forEach(s=>{for(let[e,b]of Object.entries(u))s.setAttribute(e,b);if(E){for(;s.firstElementChild;)s=s.firstElementChild;s.textContent=E}})}}export{F as staticSearchInput,x as staticSearchQuery,L as staticSearchResult,A as staticSearchSetQuery};

@@ -1,1 +0,1 @@

import{staticSearchQuery as h,staticSearchSetQuery as l,staticSearchInput as o,staticSearchResult as n}from"./__SSDIR__/staticsearch-bind.js";var i=class r extends HTMLElement{static path=new URL(import.meta.url).pathname.replace("__FILENAME__","");#t=null;#e=null;#a=null;connectedCallback(){let t=this.firstElementChild;if(this.attachShadow({mode:"open"}),!t)return;t.setAttribute("part","startsearch");let s=document.createElement("link");s.rel="stylesheet",s.href=`${r.path}css/component.css`,this.shadowRoot.appendChild(s);let a=this.shadowRoot.appendChild(t);this.#r(),a.style.cursor="pointer",a.addEventListener("click",e=>{e.preventDefault(),this.#s()}),window.addEventListener("keydown",e=>{e.key==="k"&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this.#s())}),h()&&this.#s()}#s(){this.#t.open?this.#i():(this.#t.showModal(),this.setAttribute("aria-expanded","true"),this.#e.select(),this.#e.focus())}#i(){this.#t.close(),this.removeAttribute("aria-expanded"),l()}#r(){let t=document.createElement("dialog");t.setAttribute("closedby","any"),t.setAttribute("part","dialog"),t.innerHTML=`<form method="dialog"><button part="close"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path xmlns="http://www.w3.org/2000/svg" fill="currentColor" d="M5.3 5.3a1 1 0 0 1 1.4 0l5.3 5.3 5.3-5.3a1 1 0 1 1 1.4 1.4L13.4 12l5.3 5.3a1 1 0 0 1-1.4 1.4L12 13.4l-5.3 5.3a1 1 0 0 1-1.4-1.4l5.3-5.3-5.3-5.3a1 1 0 0 1 0-1.4Z"/></svg></button></form><search part="search"><label for="search" part="searchlabel">${this.getAttribute("label")||"search"}</label><input type="search" id="search" name="q" minlength="2" maxlength="300" part="searchinput" /></search><div part="results"></div>`,this.#t=this.shadowRoot.appendChild(t),this.#e=this.shadowRoot.querySelector("#search"),this.#a=this.shadowRoot.querySelector('[part="results"]'),n(this.#a,{minFound:this.getAttribute("minfound"),minScore:this.getAttribute("minscore"),maxResults:this.getAttribute("maxresults"),highlight:this.hasAttribute("highlight")}),o(this.#e),this.#t.addEventListener("close",()=>this.#i()),this.#t.addEventListener("click",s=>{this.#t.open&&s.target===this.#t&&this.#i()})}};customElements.define("static-search",i);
import{staticSearchQuery as h,staticSearchSetQuery as l,staticSearchInput as n,staticSearchResult as o}from"./__SSDIR__/staticsearch-bind.js";var a=class r extends HTMLElement{static path=new URL(import.meta.url).pathname.replace("__FILENAME__","");#t=null;#s=null;#a=null;connectedCallback(){let e=this.firstElementChild;if(this.tabIndex=0,this.attachShadow({mode:"open",delegatesFocus:!0}),!e)return;e.getAttribute("part")||e.setAttribute("part","activate");let s=document.createElement("link");s.rel="stylesheet",s.href=`${r.path}css/component.css`,this.shadowRoot.appendChild(s);let i=this.shadowRoot.appendChild(e);this.#r(),i.tabIndex=0,i.style.cursor="pointer",i.addEventListener("click",t=>this.#e(t)),i.addEventListener("keydown",t=>{(t.key==="Enter"||t.key===" ")&&this.#e(t)}),window.addEventListener("keydown",t=>{t.key==="k"&&(t.ctrlKey||t.metaKey)&&this.#e(t)}),window.addEventListener("click",t=>{!this.#t.open&&t.target.dataset.staticSearch&&this.#e(t)}),h()&&this.#e()}#e(e){e&&e.preventDefault(),this.#t.open?this.#i():(this.#t.showModal(),this.setAttribute("aria-expanded","true"),this.#s.select(),this.#s.focus())}#i(){this.#t.close(),this.removeAttribute("aria-expanded"),l()}#r(){let e=document.createElement("dialog");e.setAttribute("closedby","any"),e.setAttribute("part","dialog"),e.innerHTML=`<form method="dialog"><button part="close"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path xmlns="http://www.w3.org/2000/svg" fill="currentColor" d="M5.3 5.3a1 1 0 0 1 1.4 0l5.3 5.3 5.3-5.3a1 1 0 1 1 1.4 1.4L13.4 12l5.3 5.3a1 1 0 0 1-1.4 1.4L12 13.4l-5.3 5.3a1 1 0 0 1-1.4-1.4l5.3-5.3-5.3-5.3a1 1 0 0 1 0-1.4Z"/></svg></button></form><search part="search"><label for="search" part="searchlabel">${this.getAttribute("label")||"search"}</label><input type="search" id="search" name="q" minlength="2" maxlength="300" part="searchinput" /></search><div part="results"></div>`,this.#t=this.shadowRoot.appendChild(e),this.#s=this.shadowRoot.querySelector("#search"),this.#a=this.shadowRoot.querySelector('[part="results"]'),o(this.#a,{minFound:this.getAttribute("minfound"),minScore:this.getAttribute("minscore"),maxResults:this.getAttribute("maxresults"),highlight:this.hasAttribute("highlight")}),n(this.#s,{fuzzy:this.getAttribute("fuzzy")}),this.#t.addEventListener("close",()=>this.#i()),this.#t.addEventListener("click",s=>{this.#t.open&&s.target===this.#t&&this.#i()})}};customElements.define("static-search",a);

@@ -1,1 +0,1 @@

import{stem as p}from"./stem/__STEMFILE__";function d(o,e,t=7,r=new Set){return new Set(o.normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[\u2018\u2019\u201A\u201B\u0027]/g,"").toLowerCase().replace(/[^a-z\s]/g," ").split(/\s+/g).map(s=>e(s.trim()).slice(0,t)).filter(s=>s.length>1&&!r.has(s)).sort())}var h=class{#t=null;#e=null;#r=null;constructor(o,e,t){return this.#e=o||"db",this.#r=e||1,this.#s(t)}connect(){return this.#s().then(()=>!0).catch(()=>!1)}close(){this.#t.close(),this.#t=null}#s(o){return new Promise((e,t)=>{if(!("indexedDB"in window)){t(new Error("No indexedDB support"));return}let r=indexedDB.open(this.#e,this.#r);r.onsuccess=()=>{this.#t=r.result,e(this)},r.onerror=s=>{t(new Error(`IndexedDB error ${s.target.errorCode}: ${s.target.message}`,{cause:s}))},o&&(r.onupgradeneeded=s=>{o(r.result,s.oldVersion,s.newVersion)})})}get isConnected(){return!!this.#t}get name(){return this.#e}get version(){return this.#r}#n(o,e,t){return new Promise((r,s)=>{let{transaction:i,store:l}=this.#a(o,null,!0);i.oncomplete=()=>r(),i.onerror=n=>{s(new Error(n.target.error.message,{cause:n}))},e=Array.isArray(e)?e:[e],e.forEach(n=>{t?l.put(n):l.add(n)}),i.commit()})}add({store:o,item:e=[]}={}){return this.#n(o,e,!1)}put({store:o,item:e=[]}={}){return this.#n(o,e,!0)}#o(o,e,t,r){return new Promise((s,i)=>{r=Array.isArray(r)?r:[r];let l=t==="delete"||t==="clear",n=this.#a(o,e,l).store[t](...r);n.onsuccess=()=>s(n.result),n.onerror=()=>i(n.error??!1)})}count({store:o,index:e,lowerBound:t,upperBound:r}={}){return this.#o(o,e,"count",this.#i(t,r))}get({store:o,index:e,key:t}={}){return this.#o(o,e,"get",t)}getAll({store:o,index:e,lowerBound:t,upperBound:r,count:s}={}){return this.#o(o,e,"getAll",[this.#i(t,r),s])}getAllKeys({store:o,index:e,lowerBound:t,upperBound:r,count:s}={}){return this.#o(o,e,"getAllKeys",[this.#i(t,r),s])}delete({store:o,key:e}={}){return this.#o(o,null,"delete",[e])}deleteAll({store:o,index:e,lowerBound:t,upperBound:r}={}){return this.#o(o,e,"delete",[this.#i(t,r)])}clear({store:o}={}){return this.#o(o,null,"clear")}getCursor({store:o,index:e,lowerBound:t,upperBound:r,direction:s="next",callback:i}={}){return new Promise((l,n)=>{let a=this.#a(o,e).store.openCursor(this.#i(t,r),s);a.onsuccess=()=>{let u=a.result;u?u.advance(i&&i(u)||1):l(!0)},a.onerror=()=>n(a.error)})}drop(){return new Promise((o,e)=>{this.close();let t=indexedDB.deleteDatabase(this.#e);t.onsuccess=()=>{this.#e=null,this.#r=null,o(!0)},t.onerror=()=>e(!1)})}#a(o,e,t){let r=this.#t.transaction(o,t?"readwrite":"readonly",{durability:t?"strict":"default"}),s=r.objectStore(o);return{transaction:r,store:e&&!t?s.index(e):s}}#i(o,e){let t;return o&&e?t=IDBKeyRange.bound(o,e):o?t=IDBKeyRange.lowerBound(o):e&&(t=IDBKeyRange.upperBound(e)),t}};var c=class o{static path=new URL(import.meta.url).pathname.replace("__FILENAME__","");static dbName="__AGENT__";static version="__VERSION__";static wordCrop=__WORDCROP__;#t=!1;#e=null;#r=null;fetchTimeout=5e3;constructor(){}async init(){if(this.#t)return!0;if(this.#e=this.#e||await new h(o.dbName,1,(r,s,i)=>{s===0&&(r.createObjectStore("cfg",{keyPath:"name"}),r.createObjectStore("page",{keyPath:"id"}),r.createObjectStore("file",{keyPath:"id"}),r.createObjectStore("index",{keyPath:"word"}))}),!this.#e?.isConnected)return!1;if((await this.#e.get({store:"cfg",key:"version"}))?.value===o.version)return this.#r=new Set((await this.#e.get({store:"cfg",key:"stopword"}))?.value),this.#t=!0,!0;await Promise.allSettled([this.#e.put({store:"cfg",item:{name:"version",value:o.version}}),this.#e.clear({store:"page"}),this.#e.clear({store:"file"}),this.#e.clear({store:"index"})]);let t=await this.#n("index.json");return t===null?!1:(this.#r=new Set(t.stopword),await this.#e.put({store:"cfg",item:{name:"stopword",value:t.stopword}}),await this.#e.add({store:"page",item:t.page.map((r,s)=>({id:s,url:r.u,title:r.t||r.u,description:r.d||"",date:r.p||"",words:r.w||0}))}),await this.#e.add({store:"file",item:t.file.map(r=>({id:r,loaded:!1}))}),this.#t=!0,!0)}async find(e){if(!this.#t)throw new Error("StaticSearch failed to initialize");e=e.trim(),this.#s("find",{search:e});let t=[],r=[...this.wordList(e)],s=r.length;if(!s)return this.#s("result",{search:e,result:t}),t;await Promise.allSettled(r.map(n=>this.loadIndex(n)));let i={};(await Promise.allSettled(r.map(n=>this.#e.get({store:"index",key:n})))).forEach(n=>{if(n.value?.page)for(let a in n.value.page)i[a]=i[a]||{rel:0,wc:0},i[a].rel+=n.value.page[a],i[a].wc++});let l=[];for(let n in i)l.push({id:n,relevancy:i[n].rel,found:i[n].wc/s});return(await Promise.allSettled(l.map(n=>this.#e.get({store:"page",key:parseFloat(n.id)})))).forEach((n,a)=>{n.value&&(t[a]=n.value,t[a].relevancy=l[a].relevancy,t[a].found=l[a].found)}),t.sort((n,a)=>a.relevancy-n.relevancy||a.found-n.found||new Date(a.date||"0")-new Date(n.date||"0")),this.#s("result",{search:e,result:t}),t}wordList(e){return d(e,p,o.wordCrop,this.#r)}async loadIndex(e){if(!this.#t)throw new Error("StaticSearch failed to initialize");if(e=e.toLowerCase().replace(/[^a-z]/g,"").slice(0,2),!e)return;let t=e.slice(0,e.length-1)+String.fromCharCode(e.charCodeAt(e.length-1)+1),r=(await this.#e.getAll({store:"file",lowerBound:e,upperBound:t})).filter(l=>!l.loaded);if(!r.length)return;let s=[],i=[];(await Promise.allSettled(r.map(l=>this.#n(`data/${l.id}.json`)))).map((l,n)=>{l.value&&(s.push({id:r[n].id,loaded:!0}),i.push(...Object.getOwnPropertyNames(l.value).map(a=>({word:a,page:l.value[a]}))))}),await Promise.allSettled([this.#e.put({store:"file",item:s}),this.#e.put({store:"index",item:i})])}#s(e="unknown",t={}){document.dispatchEvent(new CustomEvent("staticsearch:"+e,{detail:t}))}async#n(e){let t=o.path+e;try{let r=new AbortController,s=setTimeout(()=>r.abort(),this.fetchTimeout),i=await fetch(t,{signal:r.signal});if(clearTimeout(s),!i.ok)throw new Error(`Fetch failed ${t}: ${i.status}`);return await i.json()}catch{return null}}},w=new c;await w.init();export{w as staticsearch};
import{stem as w}from"./stem/__STEMFILE__";function h(o,e,r=7,t=new Set){return new Set(o.normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[\u2018\u2019\u201A\u201B\u0027]/g,"").toLowerCase().replace(/[^a-z\s]/g," ").split(/\s+/g).map(s=>e(s.trim()).slice(0,r)).filter(s=>s.length>1&&!t.has(s)).sort())}var p=class{#t=null;#e=null;#r=null;constructor(o,e,r){return this.#e=o||"db",this.#r=e||1,this.#s(r)}connect(){return this.#s().then(()=>!0).catch(()=>!1)}close(){this.#t.close(),this.#t=null}#s(o){return new Promise((e,r)=>{if(!("indexedDB"in window)){r(new Error("No indexedDB support"));return}let t=indexedDB.open(this.#e,this.#r);t.onsuccess=()=>{this.#t=t.result,e(this)},t.onerror=s=>{r(new Error(`IndexedDB error ${s.target.errorCode}: ${s.target.message}`,{cause:s}))},o&&(t.onupgradeneeded=s=>{o(t.result,s.oldVersion,s.newVersion)})})}get isConnected(){return!!this.#t}get name(){return this.#e}get version(){return this.#r}#n(o,e,r){return new Promise((t,s)=>{let{transaction:l,store:a}=this.#a(o,null,!0);l.oncomplete=()=>t(),l.onerror=u=>{s(new Error(u.target.error.message,{cause:u}))},e=Array.isArray(e)?e:[e],e.forEach(u=>{r?a.put(u):a.add(u)}),l.commit()})}add({store:o,item:e=[]}={}){return this.#n(o,e,!1)}put({store:o,item:e=[]}={}){return this.#n(o,e,!0)}#o(o,e,r,t){return new Promise((s,l)=>{t=Array.isArray(t)?t:[t];let a=r==="delete"||r==="clear",u=this.#a(o,e,a).store[r](...t);u.onsuccess=()=>s(u.result),u.onerror=()=>l(u.error??!1)})}count({store:o,index:e,lowerBound:r,upperBound:t}={}){return this.#o(o,e,"count",this.#i(r,t))}get({store:o,index:e,key:r}={}){return this.#o(o,e,"get",r)}getAll({store:o,index:e,lowerBound:r,upperBound:t,count:s}={}){return this.#o(o,e,"getAll",[this.#i(r,t),s])}getAllKeys({store:o,index:e,lowerBound:r,upperBound:t,count:s}={}){return this.#o(o,e,"getAllKeys",[this.#i(r,t),s])}delete({store:o,key:e}={}){return this.#o(o,null,"delete",[e])}deleteAll({store:o,index:e,lowerBound:r,upperBound:t}={}){return this.#o(o,e,"delete",[this.#i(r,t)])}clear({store:o}={}){return this.#o(o,null,"clear")}getCursor({store:o,index:e,lowerBound:r,upperBound:t,direction:s="next",callback:l}={}){return new Promise((a,u)=>{let n=this.#a(o,e).store.openCursor(this.#i(r,t),s);n.onsuccess=()=>{let i=n.result;i?i.advance(l&&l(i)||1):a(!0)},n.onerror=()=>u(n.error)})}drop(){return new Promise((o,e)=>{this.close();let r=indexedDB.deleteDatabase(this.#e);r.onsuccess=()=>{this.#e=null,this.#r=null,o(!0)},r.onerror=()=>e(!1)})}#a(o,e,r){let t=this.#t.transaction(o,r?"readwrite":"readonly",{durability:r?"strict":"default"}),s=t.objectStore(o);return{transaction:t,store:e&&!r?s.index(e):s}}#i(o,e){let r;return o&&e?r=IDBKeyRange.bound(o,e):o?r=IDBKeyRange.lowerBound(o):e&&(r=IDBKeyRange.upperBound(e)),r}};var d=class o{static path=new URL(import.meta.url).pathname.replace("__FILENAME__","");static dbName="__AGENT__";static version="__VERSION__";static wordCrop=__WORDCROP__;#t=!1;#e=null;#r=null;fetchTimeout=5e3;constructor(){}async init(){if(this.#t)return!0;if(this.#e=this.#e||await new p(o.dbName,1,(t,s,l)=>{s===0&&(t.createObjectStore("cfg",{keyPath:"name"}),t.createObjectStore("page",{keyPath:"id"}),t.createObjectStore("file",{keyPath:"id"}),t.createObjectStore("index",{keyPath:"word"}))}),!this.#e?.isConnected)return!1;if((await this.#e.get({store:"cfg",key:"version"}))?.value===o.version)return this.#r=new Set((await this.#e.get({store:"cfg",key:"stopword"}))?.value),this.#t=!0,!0;await Promise.allSettled([this.#e.put({store:"cfg",item:{name:"version",value:o.version}}),this.#e.clear({store:"page"}),this.#e.clear({store:"file"}),this.#e.clear({store:"index"})]);let r=await this.#n("index.json");return r===null?!1:(this.#r=new Set(r.stopword),await this.#e.put({store:"cfg",item:{name:"stopword",value:r.stopword}}),await this.#e.add({store:"page",item:r.page.map((t,s)=>({id:s,url:t.u,title:t.t||t.u,description:t.d||"",date:t.p||"",words:t.w||0}))}),await this.#e.add({store:"file",item:r.file.map(t=>({id:t,loaded:!1}))}),this.#t=!0,!0)}async find(e,r=6){if(!this.#t)throw new Error("StaticSearch failed to initialize");e=e.trim(),this.#s("find",{search:e});let t=[],s=[...this.wordList(e)],l=s.length;if(!l)return this.#s("result",{search:e,result:t}),t;await Promise.allSettled(s.map(n=>this.loadIndex(n)));let a={};(await Promise.allSettled(r>1?s.map(n=>this.#e.getAll({store:"index",lowerBound:n,count:r}).then(i=>i.filter(c=>c.word.startsWith(n))).then(i=>i.map(c=>(c.key=n,c)))):s.map(n=>this.#e.get({store:"index",key:n}).then(i=>i?(i.key=n,[i]):[])))).forEach(n=>{n.value?.forEach(i=>{if(i.page)for(let c in i.page)a[c]=a[c]||{rel:0,wc:new Set},a[c].rel+=i.page[c],a[c].wc.add(i.key)})});let u=[];for(let n in a)u.push({id:n,relevancy:a[n].rel,found:a[n].wc.size/l});return(await Promise.allSettled(u.map(n=>this.#e.get({store:"page",key:parseFloat(n.id)})))).forEach((n,i)=>{n.value&&(t[i]=n.value,t[i].relevancy=u[i].relevancy,t[i].found=u[i].found)}),t.sort((n,i)=>i.relevancy-n.relevancy||i.found-n.found||new Date(i.date||"0")-new Date(n.date||"0")),this.#s("result",{search:e,result:t}),t}wordList(e){return h(e,w,o.wordCrop,this.#r)}async loadIndex(e){if(!this.#t)throw new Error("StaticSearch failed to initialize");if(e=e.toLowerCase().replace(/[^a-z]/g,"").slice(0,2),!e)return;let r=e.slice(0,e.length-1)+String.fromCharCode(e.charCodeAt(e.length-1)+1),t=(await this.#e.getAll({store:"file",lowerBound:e,upperBound:r})).filter(a=>!a.loaded);if(!t.length)return;let s=[],l=[];(await Promise.allSettled(t.map(a=>this.#n(`data/${a.id}.json`)))).map((a,u)=>{a.value&&(s.push({id:t[u].id,loaded:!0}),l.push(...Object.getOwnPropertyNames(a.value).map(n=>({word:n,page:a.value[n]}))))}),await Promise.allSettled([this.#e.put({store:"file",item:s}),this.#e.put({store:"index",item:l})])}#s(e="unknown",r={}){document.dispatchEvent(new CustomEvent("staticsearch:"+e,{detail:r}))}async#n(e){let r=o.path+e;try{let t=new AbortController,s=setTimeout(()=>t.abort(),this.fetchTimeout),l=await fetch(r,{signal:t.signal});if(clearTimeout(s),!l.ok)throw new Error(`Fetch failed ${r}: ${l.status}`);return await l.json()}catch{return null}}},g=new d;await g.init();export{g as staticsearch};
+53
-47

@@ -11,33 +11,36 @@ #!/usr/bin/env node

// option configuration
/* eslint-disable no-multi-spaces */
config = [
{ env: null, cli: 'env', clis: 'e', prop: null, type: 'file', default: null, help: 'load defaults from an .env file' },
{ env: 'BUILD_DIR', cli: 'builddir', clis: 'b', prop: 'buildDir', type: 'path', default: './build/', help: 'directory containing static files' },
{ env: 'SEARCH_DIR', cli: 'searchdir', clis: 's', prop: 'searchDir', type: 'path', default: './build/search/', help: 'index file directory' },
{ env: 'SITE_DOMAIN', cli: 'domain', clis: 'd', prop: 'siteDomain', type: 'domain', default: 'http://localhost', help: 'site domain if links use full URL' },
{ env: 'BUILD_ROOT', cli: 'root', clis: 'r', prop: 'buildRoot', type: 'path', default: '/', help: 'site root path' },
{ env: 'SITE_INDEXFILE', cli: 'indexfile', clis: 'i', prop: 'siteIndexFile', type: 'name', default: 'index.html', help: 'default index file' },
{ env: 'SITE_PARSEROBOTSFILE', cli: 'ignorerobotfile', clis: 'f', prop: 'siteParseRobotsFile', type: 'true|false', default: true, help: 'parse robot.txt Disallows' },
{ env: 'SITE_PARSEROBOTSMETA', cli: 'ignorerobotmeta', clis: 'm', prop: 'siteParseRobotsMeta', type: 'true|false', default: true, help: 'parse robot meta noindex' },
{ env: 'PAGE_DOMSELECTORS', cli: 'dom', clis: 'D', prop: 'pageDOMSelectors', type: 'str', default: '', help: 'CSS selector: nodes to include' },
{ env: 'PAGE_DOMEXCLUDE', cli: 'domx', clis: 'X', prop: 'pageDOMExclude', type: 'str', default: '', help: 'CSS selector: nodes to exclude' },
{ env: 'LANGUAGE', cli: 'language', clis: 'l', prop: 'language', type: 'str', default: 'en', help: 'language' },
{ env: 'WORDCROP', cli: 'wordcrop', clis: 'c', prop: 'wordCrop', type: 'num', default: 7, help: 'crop word letters' },
{ env: 'STOPWORDS', cli: 'stopwords', clis: 'S', prop: 'stopWords', type: 'str', default: null, help: 'comma-separated list of stop words' },
{ env: 'WEIGHT_LINK', cli: 'weightlink', clis: '', prop: 'wordWeight.link', type: 'num', default: 5, help: 'word weight for inbound links' },
{ env: 'WEIGHT_TITLE', cli: 'weighttitle', clis: '', prop: 'wordWeight.title', type: 'num', default: 10, help: 'word weight for main title' },
{ env: 'WEIGHT_DESCRIPTION', cli: 'weightdesc', clis: '', prop: 'wordWeight.description', type: 'num', default: 8, help: 'word weight for description' },
{ env: 'WEIGHT_H2', cli: 'weighth2', clis: '', prop: 'wordWeight.h2', type: 'num', default: 6, help: 'word weight for H2 headings' },
{ env: 'WEIGHT_H3', cli: 'weighth3', clis: '', prop: 'wordWeight.h3', type: 'num', default: 5, help: 'word weight for H3 headings' },
{ env: 'WEIGHT_H4', cli: 'weighth4', clis: '', prop: 'wordWeight.h4', type: 'num', default: 4, help: 'word weight for H4 headings' },
{ env: 'WEIGHT_H5', cli: 'weighth5', clis: '', prop: 'wordWeight.h5', type: 'num', default: 3, help: 'word weight for H5 headings' },
{ env: 'WEIGHT_H6', cli: 'weighth6', clis: '', prop: 'wordWeight.h6', type: 'num', default: 2, help: 'word weight for H6 headings' },
{ env: 'WEIGHT_EMPHASIS', cli: 'weightemphasis', clis: '', prop: 'wordWeight.emphasis', type: 'num', default: 2, help: 'word weight for bold and italic' },
{ env: 'WEIGHT_ALT', cli: 'weightalt', clis: '', prop: 'wordWeight.alt', type: 'num', default: 1, help: 'word weight for alt tags' },
{ env: 'WEIGHT_CONTENT', cli: 'weightcontent', clis: '', prop: 'wordWeight.content', type: 'num', default: 1, help: 'word weight for content' },
{ env: 'LOGLEVEL', cli: 'loglevel', clis: 'L', prop: 'logLevel', type: 'num', default: 2, help: 'logging verbosity' },
{ env: null, cli: 'version', clis: 'v', prop: null, type: null, default: null, help: 'show application version' },
{ env: null, cli: 'help', clis: '?', prop: null, type: null, default: null, help: 'show help' },
{ env: null, cli: 'helpenv', clis: 'E', prop: null, type: null, default: null, help: 'show .env/environment variable help' },
{ env: null, cli: 'helpapi', clis: 'A', prop: null, type: null, default: null, help: 'show Node.js API help' },
{ env: null, cli: 'env', clis: 'e', prop: null, type: 'file', default: null, help: 'load defaults from an .env file' },
{ env: 'BUILD_DIR', cli: 'builddir', clis: 'b', prop: 'buildDir', type: 'path', default: './build/', help: 'directory containing static files' },
{ env: 'SEARCH_DIR', cli: 'searchdir', clis: 's', prop: 'searchDir', type: 'path', default: './build/search/', help: 'index file directory' },
{ env: 'SITE_DOMAIN', cli: 'domain', clis: 'd', prop: 'siteDomain', type: 'domain', default: 'http://localhost', help: 'site domain if links use full URL' },
{ env: 'BUILD_ROOT', cli: 'root', clis: 'r', prop: 'buildRoot', type: 'path', default: '/', help: 'site root path' },
{ env: 'SITE_INDEXFILE', cli: 'indexfile', clis: 'i', prop: 'siteIndexFile', type: 'name', default: 'index.html', help: 'default index file' },
{ env: 'SITE_PARSEROBOTSFILE', cli: 'ignorerobotfile', clis: 'f', prop: 'siteParseRobotsFile', type: 'true|false', default: true, help: 'parse robot.txt Disallows' },
{ env: 'SITE_PARSEROBOTSMETA', cli: 'ignorerobotmeta', clis: 'm', prop: 'siteParseRobotsMeta', type: 'true|false', default: true, help: 'parse robot meta noindex' },
{ env: 'PAGE_DOMSELECTORS', cli: 'dom', clis: 'D', prop: 'pageDOMSelectors', type: 'str', default: '', help: 'CSS selector: nodes to include' },
{ env: 'PAGE_DOMEXCLUDE', cli: 'domx', clis: 'X', prop: 'pageDOMExclude', type: 'str', default: '', help: 'CSS selector: nodes to exclude' },
{ env: 'LANGUAGE', cli: 'language', clis: 'l', prop: 'language', type: 'str', default: 'en', help: 'language' },
{ env: 'WORDCROP', cli: 'wordcrop', clis: 'c', prop: 'wordCrop', type: 'num', default: 7, help: 'crop word letters' },
{ env: 'STOPWORDS', cli: 'stopwords', clis: 'S', prop: 'stopWords', type: 'str', default: null, help: 'comma-separated list of stop words' },
{ env: 'STOPWORDS_DEFAULT', cli: 'ignorestopdefault', clis: 'W', prop: 'stopWordsDefault', type: 'true|false', default: true, help: 'use language default stop words' },
{ env: 'WEIGHT_LINK', cli: 'weightlink', clis: '', prop: 'wordWeight.link', type: 'num', default: 5, help: 'word weight for inbound links' },
{ env: 'WEIGHT_TITLE', cli: 'weighttitle', clis: '', prop: 'wordWeight.title', type: 'num', default: 10, help: 'word weight for main title' },
{ env: 'WEIGHT_DESCRIPTION', cli: 'weightdesc', clis: '', prop: 'wordWeight.description', type: 'num', default: 8, help: 'word weight for description' },
{ env: 'WEIGHT_H2', cli: 'weighth2', clis: '', prop: 'wordWeight.h2', type: 'num', default: 6, help: 'word weight for H2 headings' },
{ env: 'WEIGHT_H3', cli: 'weighth3', clis: '', prop: 'wordWeight.h3', type: 'num', default: 5, help: 'word weight for H3 headings' },
{ env: 'WEIGHT_H4', cli: 'weighth4', clis: '', prop: 'wordWeight.h4', type: 'num', default: 4, help: 'word weight for H4 headings' },
{ env: 'WEIGHT_H5', cli: 'weighth5', clis: '', prop: 'wordWeight.h5', type: 'num', default: 3, help: 'word weight for H5 headings' },
{ env: 'WEIGHT_H6', cli: 'weighth6', clis: '', prop: 'wordWeight.h6', type: 'num', default: 2, help: 'word weight for H6 headings' },
{ env: 'WEIGHT_EMPHASIS', cli: 'weightemphasis', clis: '', prop: 'wordWeight.emphasis', type: 'num', default: 2, help: 'word weight for bold and italic' },
{ env: 'WEIGHT_ALT', cli: 'weightalt', clis: '', prop: 'wordWeight.alt', type: 'num', default: 1, help: 'word weight for alt tags' },
{ env: 'WEIGHT_CONTENT', cli: 'weightcontent', clis: '', prop: 'wordWeight.content', type: 'num', default: 1, help: 'word weight for content' },
{ env: 'LOGLEVEL', cli: 'loglevel', clis: 'L', prop: 'logLevel', type: 'num', default: 2, help: 'logging verbosity' },
{ env: null, cli: 'version', clis: 'v', prop: null, type: null, default: null, help: 'show application version' },
{ env: null, cli: 'help', clis: '?', prop: null, type: null, default: null, help: 'show help' },
{ env: null, cli: 'helpenv', clis: 'E', prop: null, type: null, default: null, help: 'show .env/environment variable help' },
{ env: null, cli: 'helpapi', clis: 'A', prop: null, type: null, default: null, help: 'show Node.js API help' },
],
/* eslint-enable no-multi-spaces */
helpLink = styleText(['cyanBright'], 'For full help, refer to https://staticsearch.com/');

@@ -105,3 +108,3 @@

.filter(c => c.cli)
.map(c => ` ${ (c.clis ? `-${ c.clis }, ` : ' ') }--${ c.cli.padEnd(15) }${ styleText(['dim'], (c.type && c.type !== 'true|false' ? ' <' + c.type + '>' : '').padEnd(13)) } ${ c.help } ${ c.default ? styleText(['dim'], `(${ c.default })`) : '' }`)
.map(c => ` ${ (c.clis ? `-${ c.clis }, ` : ' ') }--${ c.cli.padEnd(18) }${ styleText(['dim'], (c.type && c.type !== 'true|false' ? ' <' + c.type + '>' : '').padEnd(10)) } ${ c.help } ${ c.default !== null && c.default !== '' ? styleText(['dim'], `(${ String( c.default ) })`) : '' }`)
.join('\n')

@@ -119,2 +122,5 @@ }

When --builddir is not set, the indexer attempts to find static files in:
./build/, ./dist/, ./dest/, ./out/, ./target/, or the current directory.
`);

@@ -137,3 +143,3 @@ }

.filter(c => c.env)
.map(c => ` ${ c.env }${ styleText(['dim'], (c.type ? '=<' + c.type + '>' : '').padEnd(33 - c.env.length)) } ${ c.help } ${ c.default ? styleText(['dim'], `(${ c.default })`) : '' }`)
.map(c => ` ${ c.env }${ styleText(['dim'], (c.type ? '=<' + c.type + '>' : '').padEnd(34 - c.env.length)) } ${ c.help } ${ c.default ? styleText(['dim'], `(${ c.default })`) : '' }`)
.join('\n')

@@ -144,3 +150,3 @@ }

BUILD_DIR=./dest/
SEARCH_DIR=./dest/index/
SEARCH_DIR=./dest/searchindex/
BUILD_ROOT=/blog/

@@ -165,3 +171,3 @@ `) }

You can use the Node.js API to programmatically index a static site.
Use the Node.js API to programmatically index a static site.

@@ -179,3 +185,3 @@ Install the module into a Node.js project:

staticsearch.buildDir = './dest/';
staticsearch.searchDir = './dest/index/';
staticsearch.searchDir = './dest/searchindex/';
staticsearch.buildRoot = './blog/';

@@ -195,3 +201,3 @@ staticsearch.wordWeight.title = 20;

.filter(c => c.prop)
.map(c => ` ${ styleText(['green'], '.' + c.prop) }${ styleText(['dim'], (c.type ? ' = <' + c.type + '>;' : '').padEnd(35 - c.prop.length)) } ${ c.help } ${ c.default ? styleText(['dim'], `(${ c.type != 'num' && c.type != 'true|false' ? '\'' : ''}${ c.default }${ c.type != 'num' && c.type != 'true|false' ? '\'' : ''})`) : '' }`)
.map(c => ` ${ styleText(['green'], '.' + c.prop) }${ styleText(['dim'], (c.type ? ' = <' + c.type + '>;' : '').padEnd(36 - c.prop.length)) } ${ c.help } ${ c.default ? styleText(['dim'], `(${ c.type != 'num' && c.type != 'true|false' ? '\'' : ''}${ c.default }${ c.type != 'num' && c.type != 'true|false' ? '\'' : ''})`) : '' }`)
.join('\n')

@@ -219,3 +225,2 @@ }

// set defaults

@@ -232,14 +237,15 @@ config.forEach(c => {

case 'int':
case 'num':
value = parseFloat(value);
break;
case 'int':
case 'num':
value = parseFloat(value);
break;
case 'str':
value = value.trim();
break;
case 'str':
value = value.trim();
break;
case 'true|false':
if (c.cli.startsWith('ignore')) value = !value;
break;
case 'true|false':
value = String(value).trim().toLowerCase() !== 'false';
if (opt[c.cli] && c.cli.startsWith('ignore')) value = !value;
break;

@@ -249,3 +255,3 @@ }

// set property
const [p1,p2] = c.prop.split('.');
const [p1, p2] = c.prop.split('.');

@@ -252,0 +258,0 @@ if (p2) {

@@ -9,4 +9,5 @@ import process from 'node:process';

import { env } from './lib/util.js';
import { parseRobotsTxt, parseHTML } from './lib/parser.js';
import { writePath, deletePath } from './lib/file.js';
import { findReadableDir, writePath, deletePath } from './lib/file.js';
import { stemFilename, stemFunction, stopWords } from './lib/lang.js';

@@ -30,32 +31,34 @@

language = process.env.LOCALE;
wordCrop = process.env.WORDCROP;
stopWords = process.env.STOPWORDS;
buildDir = process.env.BUILD_DIR || './build/';
searchDir = process.env.SEARCH_DIR || join(this.buildDir, 'search/');
buildRoot = process.env.BUILD_ROOT || '/';
siteDomain = process.env.SITE_DOMAIN || 'http://localhost';
siteIndexFile = process.env.SITE_INDEXFILE || 'index.html';
buildDir = env('BUILD_DIR');
searchDir = env('SEARCH_DIR');
buildRoot = env('BUILD_ROOT', '/');
siteDomain = env('SITE_DOMAIN', 'http://localhost');
siteIndexFile = env('SITE_INDEXFILE', 'index.html');
siteParseRobotsFile = (process.env.SITE_PARSEROBOTSFILE?.toLowerCase() !== 'false');
siteParseRobotsMeta = (process.env.SITE_PARSEROBOTSMETA?.toLowerCase() !== 'false');
siteParseRobotsFile = (env('SITE_PARSEROBOTSFILE', 'true').toLowerCase() !== 'false');
siteParseRobotsMeta = (env('SITE_PARSEROBOTSMETA', 'true').toLowerCase() !== 'false');
pageDOMSelectors = (process.env.PAGE_DOMSELECTORS || '');
pageDOMExclude = (process.env.PAGE_DOMEXCLUDE || '');
pageDOMSelectors = env('PAGE_DOMSELECTORS', '');
pageDOMExclude = env('PAGE_DOMEXCLUDE', '');
language = env('LOCALE');
wordCrop = env('WORDCROP');
stopWords = env('STOPWORDS');
stopWordsDefault = (env('STOPWORDS_DEFAULT', 'true').toLowerCase() !== 'false');
wordWeight = {
title: parseFloat(process.env.WEIGHT_TITLE || 10),
description: parseFloat(process.env.WEIGHT_DESCRIPTION || 8),
h2: parseFloat(process.env.WEIGHT_H2 || 6),
h3: parseFloat(process.env.WEIGHT_H3 || 5),
h4: parseFloat(process.env.WEIGHT_H4 || 4),
h5: parseFloat(process.env.WEIGHT_H5 || 3),
h6: parseFloat(process.env.WEIGHT_H6 || 2),
content: parseFloat(process.env.WEIGHT_CONTENT || 1),
emphasis: parseFloat(process.env.WEIGHT_EMPHASIS || 2),
alt: parseFloat(process.env.WEIGHT_ALT || 1),
link: parseFloat(process.env.WEIGHT_LINK || 5)
title: env('WEIGHT_TITLE', 10),
description: env('WEIGHT_DESCRIPTION', 8),
h2: env('WEIGHT_H2', 6),
h3: env('WEIGHT_H3', 5),
h4: env('WEIGHT_H4', 4),
h5: env('WEIGHT_H5', 3),
h6: env('WEIGHT_H6', 2),
content: env('WEIGHT_CONTENT', 1),
emphasis: env('WEIGHT_EMPHASIS', 2),
alt: env('WEIGHT_ALT', 1),
link: env('WEIGHT_LINK', 5)
};
logLevel = process.env.LOGLEVEL || 2;
logLevel = env('LOGLEVEL', 2);

@@ -65,9 +68,18 @@ // start indexing

// resolved working directories
// concol defaults
concol = new ConCol('StaticSearch', 'magentaBright', parseFloat(this.logLevel) || 0);
// find readable build directory
const workingBuildDir = await findReadableDir(process.cwd(), this.buildDir || './build,./dist,./dest,./out,./target,./');
if (!workingBuildDir) {
concol.error('no static site build directory found');
return;
}
// set working directories
const
workingBuildDir = resolve(process.cwd(), this.buildDir),
workingSearchDir = resolve(process.cwd(), this.searchDir),
workingSearchDir = this.searchDir ? resolve(process.cwd(), this.searchDir) : resolve(workingBuildDir, './search'),
workingStaticSite = resolve( '/', dirname( import.meta.url.replace(/^[^/]*\/+/, '') ) );
concol = new ConCol('StaticSearch', 'magentaBright', parseFloat(this.logLevel) || 0);
concol.log(['StaticSearch indexing started', '', ['processing HTML files in', workingBuildDir], ['writing index data to', workingSearchDir], '' ], 1);

@@ -81,3 +93,3 @@

stem = await stemFunction(this.language),
stopword = await stopWords(this.language, this.wordCrop, this.stopWords);
stopword = await stopWords(this.language, this.stopWordsDefault, this.stopWords, this.wordCrop);

@@ -117,3 +129,3 @@ // parse robots.txt

}
slug = slug.replace(sep, '/');
slug = slug.replaceAll(sep, '/');

@@ -120,0 +132,0 @@ return { file, slug };

// file utilities
import { access, stat, writeFile, mkdir, rm } from 'node:fs/promises';
import { constants as fsConstants } from 'node:fs';
import { dirname } from 'node:path';
import { dirname, resolve } from 'node:path';

@@ -33,3 +33,5 @@ import { concol } from '../indexer.js';

}
catch (e) {}
catch (e) {
// no read access
}

@@ -40,3 +42,5 @@ try {

}
catch (e) {}
catch (e) {
// no write access
}

@@ -48,2 +52,22 @@ return info;

// find readable directory
export async function findReadableDir(rootDir, subDir) {
subDir = subDir.split(',').map(s => s.trim()).filter(s => s);
let fd;
for (let d = 0; d < subDir.length; d++) {
fd = resolve(rootDir, subDir[d]);
const fInfo = await fileInfo(fd);
if (fInfo.isDir && fInfo.canRead) break;
else fd = null;
}
return fd;
}
// write file content

@@ -50,0 +74,0 @@ export async function writePath(filename, content) {

// returns stopwords and stem functions
import { readFile } from 'node:fs/promises';
import { resolve, dirname } from 'node:path';
import crypto from 'node:crypto';

@@ -9,6 +10,8 @@ import { fileInfo } from './file.js';

const
dirStem = resolve( '/', dirname( import.meta.url.replace(/^[^/]*\/+/, '') ), '../stem/' ),
dirLoc = dirname( import.meta.url.replace(/^[^/]*\/+/, '') ),
dirStem = resolve( '/', dirLoc, '../stem/' ),
mapStem = new Map(),
dirWord = resolve( '/', dirname( import.meta.url.replace(/^[^/]*\/+/, '') ), '../stopwords/' ),
dirWord = resolve( '/', dirLoc, '../stopwords/' ),
mapWord = new Map();

@@ -56,6 +59,6 @@

// get stopword list
export async function stopWords(language = 'en', maxLength = 7, extrawords = '') {
export async function stopWords(language = 'en', setDefaults, extrawords, maxLength = 7) {
// stopwords already loaded
const mapId = language + maxLength;
// stopwords already loaded?
const mapId = crypto.createHash('md5').update(language + (setDefaults ? 'T' : 'F') + maxLength + extrawords).digest('base64');

@@ -66,17 +69,25 @@ if (mapWord.has(mapId)) {

const
stem = await stemFunction(language),
swFile = resolve(dirWord, `stopwords_${language}.txt`),
swFileInfo = await fileInfo(swFile);
const stem = await stemFunction(language);
let words = extrawords || '';
// read stopword file
if (swFileInfo.exists && swFileInfo.canRead) {
// read default stopword file
if (setDefaults) {
const
swFile = resolve(dirWord, `stopwords_${language}.txt`),
swFileInfo = await fileInfo(swFile);
if (swFileInfo.exists && swFileInfo.canRead) {
words += '\n' + await readFile(swFile, { encoding: 'utf8' });
}
}
// generate stopword set
words = words.trim();
if (words) {
mapWord.set(
mapId,
wordSet(
(await readFile(swFile, { encoding: 'utf8' })) + '\n' + extrawords,
stem,
maxLength
)
wordSet(words, stem, maxLength)
);

@@ -87,5 +98,4 @@

}
else return new Set();
}

@@ -267,3 +267,5 @@ // data parsing functions

}
catch (e) {}
catch (e) {
return;
}

@@ -270,0 +272,0 @@ }

{
"name": "staticsearch",
"version": "0.6.0",
"version": "0.7.0",
"description": "Simple static site search system.",

@@ -51,9 +51,9 @@ "type": "module",

"concol": "2.1.1",
"node-html-parser": "7.0.1",
"node-html-parser": "7.1.0",
"perfpro": "0.2.0"
},
"devDependencies": {
"esbuild": "0.27.2",
"esbuild": "0.28.0",
"pixdb": "1.0.2"
}
}

@@ -6,3 +6,2 @@ a

about
above
abroad

@@ -18,5 +17,2 @@ accordance

adopted
affected
affecting
affects
afore

@@ -101,3 +97,2 @@ after

aslant
associated
astraddle

@@ -112,3 +107,2 @@ astride

august
available
aw

@@ -145,6 +139,2 @@ away

believe
below
beneath
beside
besides
best

@@ -155,3 +145,2 @@ better

betters
between
betwixt

@@ -169,5 +158,3 @@ beyond

bo
boo
both
bottom
bravo

@@ -213,3 +200,2 @@ brief

comes
computer
con

@@ -220,5 +206,2 @@ concerning

considering
contain
containing
contains
conversely

@@ -290,3 +273,2 @@ copy

eek
effect
eg

@@ -334,3 +316,2 @@ ie

exactly
example
except

@@ -340,3 +321,2 @@ excepted

excepts
excluding
exes

@@ -434,6 +414,2 @@ f

greetings
group
grouped
grouping
groups
h

@@ -490,4 +466,2 @@ had

homepage
hoo
hoos
hopefully

@@ -561,4 +535,2 @@ how

kept
key
keys
kg

@@ -623,8 +595,4 @@ kind

me
mean
means
meantime
meanwhile
member
members
men

@@ -750,6 +718,2 @@ merely

or
order
ordered
ordering
orders
other

@@ -775,11 +739,4 @@ others

owing
own
owned
owning
owns
owt
p
page
pages
part
parted

@@ -800,3 +757,2 @@ particular

places
plaintiff
please

@@ -860,3 +816,2 @@ pleased

relatively
res
research

@@ -866,6 +821,2 @@ reserved

respectively
resulted
resulting
results
right
ring

@@ -879,5 +830,2 @@ room

sames
sans
sanserif
sanserifs
sat

@@ -1004,4 +952,2 @@ saturday

tenth
test
text
than

@@ -1083,3 +1029,2 @@ thank

took
top
toward

@@ -1165,5 +1110,2 @@ towards

we
web
webpage
website
wed

@@ -1263,3 +1205,2 @@ wednesday

wow
write
x

@@ -1266,0 +1207,0 @@ xi