wiki-plugin-similarity 0.15.0 → 0.15.1
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/client/similarity.js +1 -1
- package/package.json +1 -1
- package/pages/about-similarity-plugin +2 -2
- package/server/server.js +12 -2
package/client/similarity.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* wiki-plugin-similarity - 0.15.
|
|
1
|
+
/* wiki-plugin-similarity - 0.15.1 - Thu, 03 Sep 2026 21:16:47 GMT */
|
|
2
2
|
(()=>{var _={high:.78,medium:.68,low:.58},q=_.medium,Y=10,B=t=>{let e=[],s=[],n=[],m=null,b=null,u="search",y=!1,k=!1,S=!1,R=null,j=null,E=(i,r)=>i===r||i.startsWith(r)&&/^[\s:]/.test(i.slice(r.length)),w=(i,r)=>i.slice(r.length).replace(/^\s*:?\s*/,"").trim();for(let i of t.split(`
|
|
3
3
|
`)){let r=i.trim();if(!r||r.startsWith("#"))continue;let p=r.toUpperCase();if(E(p,"LIVE")){y=!0;continue}if(E(p,"SUBJECT")){k=!0;continue}if(E(p,"AUTHOR")){!e.length&&u==="search"&&(u="author");continue}if(E(p,"REPORT")){u==="search"&&(u="report");continue}if(E(p,"KEYWORD")){u==="search"&&(u="keyword");continue}if(E(p,"SITES")){u==="search"&&(u="sites");continue}if(E(p,"BUILD")){u==="search"&&(u="build");continue}if(E(p,"FORCE")){S=!0;continue}if(E(p,"GHOST")){R=w(r,"GHOST"),u==="search"&&(u="ghost");continue}if(E(p,"BUTTON")){j=w(r,"BUTTON");continue}if(E(p,"ROSTER")){let v=w(r,"ROSTER");v&&s.push(v);continue}if(E(p,"FARM")){let v=w(r,"FARM");v&&n.push(v);continue}if(E(p,"LIST")){!e.length&&u==="search"&&(u="list");continue}if(E(p,"SIMILAR")){let v=w(p,"SIMILAR").toLowerCase();m=_[v]||q,!e.length&&u==="search"&&(u="similar");continue}if(E(p,"THRESHOLD")){let v=w(r,"THRESHOLD");m=_[v.toLowerCase()]??(parseFloat(v)||q);continue}if(E(p,"LIMIT")){b=parseInt(w(r,"LIMIT"))||Y;continue}e.push(["PUBLIC","LOCAL","PRIVATE","GALAXY"].includes(p)?p:r)}return{mode:u,specs:e,rosterRefs:s,farms:n,threshold:m??q,limit:b??Y,live:y,subject:k,force:S,ghostUrl:R,label:j,thresholdSet:m!==null}},Z=t=>t.includes("*")||t.includes("?"),X=t=>t==="PUBLIC"||t==="LOCAL"||t==="PRIVATE"||t==="GALAXY";var tt=t=>t.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,""),G=new Map,pt=async(t,e)=>{if(G.has(t))return G.get(t);let s=`${e}/system/indexed-domains.json?pattern=${encodeURIComponent(t)}`,n=await fetch(s);if(!n.ok)throw new Error(`indexed-domains failed: ${n.status}`);let m=await n.json();return G.set(t,m),m},P=async(t,e)=>{t.length||(t=[window.location.hostname]);let s=new Set,n=[];for(let m of t)if(m==="*"||Z(m)||X(m))for(let b of await pt(m,e))s.has(b.domain)||(s.add(b.domain),n.push(b));else s.has(m)||(s.add(m),n.push({domain:m,page_count:null}));return n},gt=/^([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+|localhost)(:\d+)?$/,ht=/^ROSTER ([A-Za-z0-9.\-:]+\/[a-z0-9-]+)$/,wt=/^REFERENCES ([A-Za-z0-9.\-:]+\/[a-z0-9-]+)$/,Q=async t=>{let e=t.indexOf("/"),s=await fetch(`//${t.slice(0,e)}/${t.slice(e+1)}.json`);if(!s.ok)throw new Error(`roster page ${t} failed: ${s.status}`);return s.json()},et=async(t,e,s)=>{if(s.has(t))return;s.add(t);let n;try{n=await Q(t)}catch(m){console.warn(m.message);return}for(let m of n.story||[])if(m.type==="roster")for(let b of(m.text||"").split(/\r?\n/)){let u=b.trim();if(!u)continue;let y=u.match(gt);if(y){e.add(y[0]);continue}let k=u.match(ht);if(k){await et(k[1],e,s);continue}let S=u.match(wt);if(S)try{let R=await Q(S[1]);for(let j of R.story||[])j.type==="reference"&&j.site&&e.add(j.site)}catch(R){console.warn(R.message)}}},yt=async t=>{let e=new Set,s=new Set;for(let n of t)await et(n,e,s);return[...e]},st=async(t,e)=>e.length?[...t,...await yt(e)]:t;var V=new Map,$t=t=>`${window.location.origin}/system/semantic-vectors.json?domain=${encodeURIComponent(t)}`,nt=async t=>{if(V.has(t))return V.get(t);let e=await fetch($t(t));if(!e.ok)return[];let s=await e.json();return V.set(t,s),s},M=async(t,e)=>{let s=await fetch(`${e}/system/embed.json`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:t})});if(!s.ok)throw new Error(`embed failed: ${s.status}`);return(await s.json()).vector},it=async(t,e)=>{let n=(await nt(e)).find(m=>m.slug===t);return n?n.vector:null},z=(t,e,{threshold:s,limit:n,excludeSlug:m,excludeDomain:b})=>{let u=[];for(let{domain:y,pages:k}of e)for(let{slug:S,title:R,vector:j}of k){if(S===m&&y===b)continue;let E=0;for(let w=0;w<t.length;w++)E+=t[w]*j[w];E>=s&&u.push({domain:y,slug:S,title:R,score:E})}return u.sort((y,k)=>k.score-y.score),u.slice(0,n)},F=async(t,e)=>{let s=await P(t,e);return(await Promise.all(s.map(async({domain:m})=>({domain:m,pages:await nt(m)})))).filter(m=>m.pages.length>0)};var ot=t=>{let e=t.parents(".page:first"),s=e.prev(".page"),n=s.length?s:e,m=(n.attr("id")||"").split("_rev")[0],b=n.data("site")||window.location.hostname,u=n.data("data")||{},y=u.title||n.find(".title").text().trim()||m,k=[y,...(u.story||[]).filter(S=>S.type==="markdown"||S.type==="paragraph").map(S=>(S.text||"").trim()).filter(Boolean)].join(`
|
|
4
4
|
`).slice(0,2e3);return{slug:m,site:b,title:y,text:k,isSelf:!s.length}},I=t=>`Subject: ${t.title}${t.isSelf?" (this page)":` @ ${t.site}`}`;var at=t=>`sim-cache-${t}`,rt=t=>{try{let e=JSON.parse(localStorage.getItem(at(t.id))||"null");return e?.text===(t.text||"")?e:null}catch{return null}},U=(t,e)=>{try{localStorage.setItem(at(t.id),JSON.stringify({text:t.text||"",ts:Date.now(),...e}))}catch{}},lt=t=>{let e=Math.floor((Date.now()-t)/1e3);return e<60?`${e}s ago`:e<3600?`${Math.floor(e/60)}m ago`:e<86400?`${Math.floor(e/3600)}h ago`:`${Math.floor(e/86400)}d ago`};var O=`
|
package/package.json
CHANGED
|
@@ -74,12 +74,12 @@
|
|
|
74
74
|
{
|
|
75
75
|
"type": "markdown",
|
|
76
76
|
"id": "c72e1a4f08b5d693",
|
|
77
|
-
"text": "0.15.0 makes a dead embedder degrade honestly. The embedder and the keyword index share nothing, so one can be down while the other is fine \u2014 but a report that could not embed its query used to leave a bare status code and no results. It now says semantic search is unavailable, gives the reason, and answers the same query by keyword instead. The result page is titled \u201cKeyword Search\u201d by the server, so the reader can see which tier answered."
|
|
77
|
+
"text": "0.15.0 makes a dead embedder degrade honestly. The embedder and the keyword index share nothing, so one can be down while the other is fine \u2014 but a report that could not embed its query used to leave a bare status code and no results. It now says semantic search is unavailable, gives the reason, and answers the same query by keyword instead. The result page is titled \u201cKeyword Search\u201d by the server, so the reader can see which tier answered. 0.15.1 makes that fallback actually fire: an unreachable remote embedder arrived as a bare 500, indistinguishable from a bad query, so the client had nothing to trigger on. A proxy that cannot be reached is now the same outage as a local embedder that will not start, and answers 503."
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"type": "markdown",
|
|
81
81
|
"id": "21eb5354d2adb29e",
|
|
82
|
-
"text": "**Version:** 0.15.
|
|
82
|
+
"text": "**Version:** 0.15.1\n\n# Links\n\n- [https://github.com/Hitchhikers-Guide-to-the-Galaxy/wiki-plugin-similarity GitHub repository]\n- [https://www.npmjs.com/package/wiki-plugin-similarity npm package]\n- [https://plugin.fedwiki.club/similarity-plugin.html Plugin documentation]\n- [https://search.fedwiki.club/ Search hub] \u2014 architecture, indexing, freshness."
|
|
83
83
|
}
|
|
84
84
|
],
|
|
85
85
|
"plugin": "similarity",
|
package/server/server.js
CHANGED
|
@@ -267,8 +267,18 @@ const startServer = ({ argv, app }) => {
|
|
|
267
267
|
|
|
268
268
|
async function embedText(text) {
|
|
269
269
|
const url = embedUrl()
|
|
270
|
-
if (url) return (
|
|
271
|
-
|
|
270
|
+
if (!url) return embedder.embed(text)
|
|
271
|
+
try {
|
|
272
|
+
return (await postJson(url, { text })).vector
|
|
273
|
+
} catch (e) {
|
|
274
|
+
// A proxy we cannot reach is the same outage as a local embedder that
|
|
275
|
+
// will not start, and callers must be able to tell "no vector to be had"
|
|
276
|
+
// from "bad query". Without this the failure arrived as a bare 500 and
|
|
277
|
+
// the client could not know to fall back to keyword search.
|
|
278
|
+
const err = new Error(`remote embedder at ${url}: ${e.message}`)
|
|
279
|
+
err.code = 'EMBEDDER_DOWN'
|
|
280
|
+
throw err
|
|
281
|
+
}
|
|
272
282
|
}
|
|
273
283
|
|
|
274
284
|
const cors = res => {
|