@vielzeug/scout 3.0.0 → 26.10.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/README.md +1 -1
- package/dist/adapters.cjs +1 -1
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.ts +3 -2
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +1 -12
- package/dist/adapters.js.map +1 -1
- package/dist/entries.cjs +2 -0
- package/dist/entries.cjs.map +1 -0
- package/dist/entries.d.ts +45 -0
- package/dist/entries.d.ts.map +1 -0
- package/dist/entries.js +2 -0
- package/dist/entries.js.map +1 -0
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -9
- package/dist/errors.js.map +1 -1
- package/dist/highlight.cjs +1 -1
- package/dist/highlight.cjs.map +1 -1
- package/dist/highlight.d.ts +2 -2
- package/dist/highlight.js +1 -45
- package/dist/highlight.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -7
- package/dist/reactive.cjs +1 -1
- package/dist/reactive.cjs.map +1 -1
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +1 -114
- package/dist/reactive.js.map +1 -1
- package/dist/scout-index.cjs +1 -1
- package/dist/scout-index.cjs.map +1 -1
- package/dist/scout-index.d.ts +6 -6
- package/dist/scout-index.js +1 -191
- package/dist/scout-index.js.map +1 -1
- package/dist/scout.cjs +1 -1
- package/dist/scout.cjs.map +1 -1
- package/dist/scout.iife.js +1 -1
- package/dist/scout.iife.js.map +1 -1
- package/dist/scout.js +1 -1
- package/dist/scout.js.map +1 -1
- package/dist/segment.cjs +1 -1
- package/dist/segment.cjs.map +1 -1
- package/dist/segment.d.ts +2 -2
- package/dist/segment.js +1 -12
- package/dist/segment.js.map +1 -1
- package/dist/tokenize.cjs +1 -1
- package/dist/tokenize.cjs.map +1 -1
- package/dist/tokenize.js +1 -10
- package/dist/tokenize.js.map +1 -1
- package/dist/trigram.cjs +1 -1
- package/dist/trigram.cjs.map +1 -1
- package/dist/trigram.js +1 -15
- package/dist/trigram.js.map +1 -1
- package/dist/types.d.ts +6 -8
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -40,4 +40,4 @@ console.log(index.search('ada')[0]?.item.name); // Ada Lovelace
|
|
|
40
40
|
|
|
41
41
|
## License
|
|
42
42
|
|
|
43
|
-
MIT © [Helmuth Saatkamp](https://github.com/helmuthdu)
|
|
43
|
+
MIT © [Helmuth Saatkamp](https://github.com/helmuthdu): part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
|
package/dist/adapters.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function
|
|
1
|
+
function toSearchMatcher(e,t){let n,r=-1,i=new Set;return(a,o)=>((o!==n||e.revision!==r)&&(n=o,r=e.revision,i=new Set(e.search(o,t).map(e=>e.item))),i.has(a))}function toFilterPredicate(e,t,n){let r=new Set(e.search(t,n).map(e=>e.item));return e=>r.has(e)}exports.toFilterPredicate=toFilterPredicate,exports.toSearchMatcher=toSearchMatcher;
|
|
2
2
|
//# sourceMappingURL=adapters.cjs.map
|
package/dist/adapters.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapters.cjs","names":[],"sources":["../src/adapters.ts"],"sourcesContent":["import type { ScoutIndex } from './scout-index';\nimport type { SearchConstraints } from './types';\n\n/**\n * Adapts a `ScoutIndex` to sourcerer's
|
|
1
|
+
{"version":3,"file":"adapters.cjs","names":[],"sources":["../src/adapters.ts"],"sourcesContent":["import type { ScoutIndex } from './scout-index';\nimport type { SearchConstraints } from './types';\n\n/**\n * Adapts a `ScoutIndex` to sourcerer's local-source `filter` callback\n * (`filter?: (item, params) => boolean`, with the query passed as `params`).\n * Caches one match set per query so local filtering does not repeat index work per item.\n */\nexport function toSearchMatcher<T>(\n index: ScoutIndex<T>,\n options?: SearchConstraints,\n): (item: T, query: string) => boolean {\n let lastQuery: string | undefined;\n let lastRevision = -1;\n let matches = new Set<T>();\n\n return (item, query) => {\n if (query !== lastQuery || index.revision !== lastRevision) {\n lastQuery = query;\n lastRevision = index.revision;\n matches = new Set(index.search(query, options).map((result) => result.item));\n }\n\n return matches.has(item);\n };\n}\n\n/**\n * Returns a predicate that returns `true` for items matching `query` in the given index.\n *\n * The predicate is computed once at call time: call `toFilterPredicate` again if the\n * query or corpus changes.\n *\n * Compatible with `Array.filter`, `vault`'s `query.filter()`, or any predicate pipeline.\n */\nexport function toFilterPredicate<T>(\n index: ScoutIndex<T>,\n query: string,\n options?: SearchConstraints,\n): (item: T) => boolean {\n const matchSet = new Set(index.search(query, options).map((result) => result.item));\n\n return (item) => matchSet.has(item);\n}\n"],"mappings":"AAQA,SAAgB,gBACd,EACA,EACqC,CACrC,IAAI,EACA,EAAe,GACf,EAAU,IAAI,IAElB,OAAQ,EAAM,MACR,IAAU,GAAa,EAAM,WAAa,KAC5C,EAAY,EACZ,EAAe,EAAM,SACrB,EAAU,IAAI,IAAI,EAAM,OAAO,EAAO,CAAO,CAAC,CAAC,IAAK,GAAW,EAAO,IAAI,CAAC,GAGtE,EAAQ,IAAI,CAAI,EAE3B,CAUA,SAAgB,kBACd,EACA,EACA,EACsB,CACtB,IAAM,EAAW,IAAI,IAAI,EAAM,OAAO,EAAO,CAAO,CAAC,CAAC,IAAK,GAAW,EAAO,IAAI,CAAC,EAElF,MAAQ,IAAS,EAAS,IAAI,CAAI,CACpC"}
|
package/dist/adapters.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import type { ScoutIndex } from './scout-index';
|
|
2
2
|
import type { SearchConstraints } from './types';
|
|
3
3
|
/**
|
|
4
|
-
* Adapts a `ScoutIndex` to sourcerer's
|
|
4
|
+
* Adapts a `ScoutIndex` to sourcerer's local-source `filter` callback
|
|
5
|
+
* (`filter?: (item, params) => boolean`, with the query passed as `params`).
|
|
5
6
|
* Caches one match set per query so local filtering does not repeat index work per item.
|
|
6
7
|
*/
|
|
7
8
|
export declare function toSearchMatcher<T>(index: ScoutIndex<T>, options?: SearchConstraints): (item: T, query: string) => boolean;
|
|
8
9
|
/**
|
|
9
10
|
* Returns a predicate that returns `true` for items matching `query` in the given index.
|
|
10
11
|
*
|
|
11
|
-
* The predicate is computed once at call time
|
|
12
|
+
* The predicate is computed once at call time: call `toFilterPredicate` again if the
|
|
12
13
|
* query or corpus changes.
|
|
13
14
|
*
|
|
14
15
|
* Compatible with `Array.filter`, `vault`'s `query.filter()`, or any predicate pipeline.
|
package/dist/adapters.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EACpB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAcrC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EACpB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,iBAAiB,GAC1B,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAItB"}
|
package/dist/adapters.js
CHANGED
|
@@ -1,13 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
function e(e, t) {
|
|
3
|
-
let n, r = -1, i = /* @__PURE__ */ new Set();
|
|
4
|
-
return (a, o) => ((o !== n || e.revision !== r) && (n = o, r = e.revision, i = new Set(e.search(o, t).map((e) => e.item))), i.has(a));
|
|
5
|
-
}
|
|
6
|
-
function t(e, t, n) {
|
|
7
|
-
let r = new Set(e.search(t, n).map((e) => e.item));
|
|
8
|
-
return (e) => r.has(e);
|
|
9
|
-
}
|
|
10
|
-
//#endregion
|
|
11
|
-
export { t as toFilterPredicate, e as toSearchMatcher };
|
|
12
|
-
|
|
1
|
+
function toSearchMatcher(e,t){let n,r=-1,i=new Set;return(a,o)=>((o!==n||e.revision!==r)&&(n=o,r=e.revision,i=new Set(e.search(o,t).map(e=>e.item))),i.has(a))}function toFilterPredicate(e,t,n){let r=new Set(e.search(t,n).map(e=>e.item));return e=>r.has(e)}export{toFilterPredicate,toSearchMatcher};
|
|
13
2
|
//# sourceMappingURL=adapters.js.map
|
package/dist/adapters.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapters.js","names":[],"sources":["../src/adapters.ts"],"sourcesContent":["import type { ScoutIndex } from './scout-index';\nimport type { SearchConstraints } from './types';\n\n/**\n * Adapts a `ScoutIndex` to sourcerer's
|
|
1
|
+
{"version":3,"file":"adapters.js","names":[],"sources":["../src/adapters.ts"],"sourcesContent":["import type { ScoutIndex } from './scout-index';\nimport type { SearchConstraints } from './types';\n\n/**\n * Adapts a `ScoutIndex` to sourcerer's local-source `filter` callback\n * (`filter?: (item, params) => boolean`, with the query passed as `params`).\n * Caches one match set per query so local filtering does not repeat index work per item.\n */\nexport function toSearchMatcher<T>(\n index: ScoutIndex<T>,\n options?: SearchConstraints,\n): (item: T, query: string) => boolean {\n let lastQuery: string | undefined;\n let lastRevision = -1;\n let matches = new Set<T>();\n\n return (item, query) => {\n if (query !== lastQuery || index.revision !== lastRevision) {\n lastQuery = query;\n lastRevision = index.revision;\n matches = new Set(index.search(query, options).map((result) => result.item));\n }\n\n return matches.has(item);\n };\n}\n\n/**\n * Returns a predicate that returns `true` for items matching `query` in the given index.\n *\n * The predicate is computed once at call time: call `toFilterPredicate` again if the\n * query or corpus changes.\n *\n * Compatible with `Array.filter`, `vault`'s `query.filter()`, or any predicate pipeline.\n */\nexport function toFilterPredicate<T>(\n index: ScoutIndex<T>,\n query: string,\n options?: SearchConstraints,\n): (item: T) => boolean {\n const matchSet = new Set(index.search(query, options).map((result) => result.item));\n\n return (item) => matchSet.has(item);\n}\n"],"mappings":"AAQA,SAAgB,gBACd,EACA,EACqC,CACrC,IAAI,EACA,EAAe,GACf,EAAU,IAAI,IAElB,OAAQ,EAAM,MACR,IAAU,GAAa,EAAM,WAAa,KAC5C,EAAY,EACZ,EAAe,EAAM,SACrB,EAAU,IAAI,IAAI,EAAM,OAAO,EAAO,CAAO,CAAC,CAAC,IAAK,GAAW,EAAO,IAAI,CAAC,GAGtE,EAAQ,IAAI,CAAI,EAE3B,CAUA,SAAgB,kBACd,EACA,EACA,EACsB,CACtB,IAAM,EAAW,IAAI,IAAI,EAAM,OAAO,EAAO,CAAO,CAAC,CAAC,IAAK,GAAW,EAAO,IAAI,CAAC,EAElF,MAAQ,IAAS,EAAS,IAAI,CAAI,CACpC"}
|
package/dist/entries.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={Exact:0,FuzzyOnly:5,LabelContains:2,LabelStart:1,MetaContains:3,TextContains:4};function rankEntries(t,n,r={}){let i=n.trim().toLowerCase();if(!i)return[...t];let a=r.fuzzyIds??new Set,o=[];for(let n of t){let t=n.label.toLowerCase(),r=t===i?e.Exact:t.startsWith(i)?e.LabelStart:t.includes(i)?e.LabelContains:n.meta.toLowerCase().includes(i)?e.MetaContains:n.text.toLowerCase().includes(i)?e.TextContains:a.has(n.id)?e.FuzzyOnly:null;r!==null&&o.push([r,n])}return o.sort((e,t)=>e[0]-t[0]||e[1].label.localeCompare(t[1].label)||e[1].text.localeCompare(t[1].text)).map(([,e])=>e)}function dedupeEntries(e){let t=new Set;return e.filter(e=>{let n=`${e.label}\u0000${e.text}`.toLowerCase();return!t.has(n)&&(t.add(n),!0)})}function splitPattern(e,t){if(!t)return[{text:e}];let n=[],r=0;for(let i of e.matchAll(t)){if(i[0].length===0)continue;let t=i.index??0;t>r&&n.push({text:e.slice(r,t)}),n.push({matched:!0,text:i[0]}),r=t+i[0].length}return r<e.length&&n.push({text:e.slice(r)}),n.length?n:[{text:e}]}var t=/[.*+?^${}()|[\]\\]/g,escapeRegExp=e=>e.replace(t,`\\$&`);exports.dedupeEntries=dedupeEntries,exports.escapeRegExp=escapeRegExp,exports.rankEntries=rankEntries,exports.splitPattern=splitPattern;
|
|
2
|
+
//# sourceMappingURL=entries.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entries.cjs","names":[],"sources":["../src/entries.ts"],"sourcesContent":["/**\n * Entry ranking and segmentation for searchable reference lists: the presentation half of\n * search: a heterogeneous set of entries (glossary terms, rules, profiles) ordered by how\n * directly they match a typed query, plus segmentation helpers for rendering matched and\n * keyword-linked text.\n */\n\n/** An entry a reference list ranks and renders: identified, labeled, with summary and body text. */\nexport interface RankableEntry {\n id: string;\n /** The entry's title: the primary match surface. */\n label: string;\n /** One-line context shown beside the label. */\n meta: string;\n /** The entry's body text. */\n text: string;\n}\n\nconst Score = {\n Exact: 0,\n FuzzyOnly: 5,\n LabelContains: 2,\n LabelStart: 1,\n MetaContains: 3,\n TextContains: 4,\n} as const;\ntype Score = (typeof Score)[keyof typeof Score];\n\nexport interface RankEntriesOptions {\n /**\n * Entry ids the fuzzy index already matched: offered as the last resort when no field\n * contains the query, so approximate hits still surface.\n */\n fuzzyIds?: ReadonlySet<string>;\n}\n\n/**\n * Orders entries by how directly they match `query`: exact label, then label prefix, label,\n * meta, text, and finally fuzzy-index hits (`fuzzyIds`) as last resort. Entries matching none\n * of these are dropped. Ties break alphabetically by label, then by body text.\n */\nexport function rankEntries<T extends RankableEntry>(\n entries: readonly T[],\n query: string,\n options: RankEntriesOptions = {},\n): T[] {\n const lower = query.trim().toLowerCase();\n if (!lower) return [...entries];\n const fuzzyIds = options.fuzzyIds ?? new Set<string>();\n const scored: [Score, T][] = [];\n for (const entry of entries) {\n const label = entry.label.toLowerCase();\n const score =\n label === lower\n ? Score.Exact\n : label.startsWith(lower)\n ? Score.LabelStart\n : label.includes(lower)\n ? Score.LabelContains\n : entry.meta.toLowerCase().includes(lower)\n ? Score.MetaContains\n : entry.text.toLowerCase().includes(lower)\n ? Score.TextContains\n : fuzzyIds.has(entry.id)\n ? Score.FuzzyOnly\n : null;\n if (score !== null) scored.push([score, entry]);\n }\n return scored\n .sort((a, b) => a[0] - b[0] || a[1].label.localeCompare(b[1].label) || a[1].text.localeCompare(b[1].text))\n .map(([, entry]) => entry);\n}\n\n/** Drops entries whose label and body are identical to an earlier entry (alias rows). */\nexport function dedupeEntries<T extends { label: string; text: string }>(entries: readonly T[]): T[] {\n const seen = new Set<string>();\n return entries.filter((entry) => {\n const key = `${entry.label}\\u0000${entry.text}`.toLowerCase();\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n/** One renderable piece of a text: matched a pattern, or plain. */\nexport interface SplitSegment {\n /** Piece matched the pattern and should render emphasized or linked. */\n matched?: boolean;\n text: string;\n}\n\n/** Splits `text` on `pattern` matches, marking the matched pieces; a null pattern keeps it whole. */\nexport function splitPattern(text: string, pattern: RegExp | null): SplitSegment[] {\n if (!pattern) return [{ text }];\n const segments: SplitSegment[] = [];\n let last = 0;\n for (const match of text.matchAll(pattern)) {\n // Zero-length matches (optional-quantifier patterns) carry no text to emphasize.\n if (match[0].length === 0) continue;\n const start = match.index ?? 0;\n if (start > last) segments.push({ text: text.slice(last, start) });\n segments.push({ matched: true, text: match[0] });\n last = start + match[0].length;\n }\n if (last < text.length) segments.push({ text: text.slice(last) });\n return segments.length ? segments : [{ text }];\n}\n\nconst REGEXP_SPECIAL = /[.*+?^${}()|[\\]\\\\]/g;\n\n/** Escapes a literal so it can be embedded in a regular expression. */\nexport const escapeRegExp = (value: string): string => value.replace(REGEXP_SPECIAL, '\\\\$&');\n"],"mappings":"AAkBA,IAAM,EAAQ,CACZ,MAAO,EACP,UAAW,EACX,cAAe,EACf,WAAY,EACZ,aAAc,EACd,aAAc,CAChB,EAgBA,SAAgB,YACd,EACA,EACA,EAA8B,CAAC,EAC1B,CACL,IAAM,EAAQ,EAAM,KAAK,CAAC,CAAC,YAAY,EACvC,GAAI,CAAC,EAAO,MAAO,CAAC,GAAG,CAAO,EAC9B,IAAM,EAAW,EAAQ,UAAY,IAAI,IACnC,EAAuB,CAAC,EAC9B,IAAK,IAAM,KAAS,EAAS,CAC3B,IAAM,EAAQ,EAAM,MAAM,YAAY,EAChC,EACJ,IAAU,EACN,EAAM,MACN,EAAM,WAAW,CAAK,EACpB,EAAM,WACN,EAAM,SAAS,CAAK,EAClB,EAAM,cACN,EAAM,KAAK,YAAY,CAAC,CAAC,SAAS,CAAK,EACrC,EAAM,aACN,EAAM,KAAK,YAAY,CAAC,CAAC,SAAS,CAAK,EACrC,EAAM,aACN,EAAS,IAAI,EAAM,EAAE,EACnB,EAAM,UACN,KACZ,IAAU,MAAM,EAAO,KAAK,CAAC,EAAO,CAAK,CAAC,CAChD,CACA,OAAO,EACJ,MAAM,EAAG,IAAM,EAAE,GAAK,EAAE,IAAM,EAAE,EAAE,CAAC,MAAM,cAAc,EAAE,EAAE,CAAC,KAAK,GAAK,EAAE,EAAE,CAAC,KAAK,cAAc,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CACzG,KAAK,EAAG,KAAW,CAAK,CAC7B,CAGA,SAAgB,cAAyD,EAA4B,CACnG,IAAM,EAAO,IAAI,IACjB,OAAO,EAAQ,OAAQ,GAAU,CAC/B,IAAM,EAAM,GAAG,EAAM,MAAM,QAAQ,EAAM,OAAO,YAAY,EAG5D,MAFA,CAAI,EAAK,IAAI,CAAG,IAChB,EAAK,IAAI,CAAG,EACL,GACT,CAAC,CACH,CAUA,SAAgB,aAAa,EAAc,EAAwC,CACjF,GAAI,CAAC,EAAS,MAAO,CAAC,CAAE,MAAK,CAAC,EAC9B,IAAM,EAA2B,CAAC,EAC9B,EAAO,EACX,IAAK,IAAM,KAAS,EAAK,SAAS,CAAO,EAAG,CAE1C,GAAI,EAAM,EAAE,CAAC,SAAW,EAAG,SAC3B,IAAM,EAAQ,EAAM,OAAS,EACzB,EAAQ,GAAM,EAAS,KAAK,CAAE,KAAM,EAAK,MAAM,EAAM,CAAK,CAAE,CAAC,EACjE,EAAS,KAAK,CAAE,QAAS,GAAM,KAAM,EAAM,EAAG,CAAC,EAC/C,EAAO,EAAQ,EAAM,EAAE,CAAC,MAC1B,CAEA,OADI,EAAO,EAAK,QAAQ,EAAS,KAAK,CAAE,KAAM,EAAK,MAAM,CAAI,CAAE,CAAC,EACzD,EAAS,OAAS,EAAW,CAAC,CAAE,MAAK,CAAC,CAC/C,CAEA,IAAM,EAAiB,sBAGV,aAAgB,GAA0B,EAAM,QAAQ,EAAgB,MAAM"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry ranking and segmentation for searchable reference lists: the presentation half of
|
|
3
|
+
* search: a heterogeneous set of entries (glossary terms, rules, profiles) ordered by how
|
|
4
|
+
* directly they match a typed query, plus segmentation helpers for rendering matched and
|
|
5
|
+
* keyword-linked text.
|
|
6
|
+
*/
|
|
7
|
+
/** An entry a reference list ranks and renders: identified, labeled, with summary and body text. */
|
|
8
|
+
export interface RankableEntry {
|
|
9
|
+
id: string;
|
|
10
|
+
/** The entry's title: the primary match surface. */
|
|
11
|
+
label: string;
|
|
12
|
+
/** One-line context shown beside the label. */
|
|
13
|
+
meta: string;
|
|
14
|
+
/** The entry's body text. */
|
|
15
|
+
text: string;
|
|
16
|
+
}
|
|
17
|
+
export interface RankEntriesOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Entry ids the fuzzy index already matched: offered as the last resort when no field
|
|
20
|
+
* contains the query, so approximate hits still surface.
|
|
21
|
+
*/
|
|
22
|
+
fuzzyIds?: ReadonlySet<string>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Orders entries by how directly they match `query`: exact label, then label prefix, label,
|
|
26
|
+
* meta, text, and finally fuzzy-index hits (`fuzzyIds`) as last resort. Entries matching none
|
|
27
|
+
* of these are dropped. Ties break alphabetically by label, then by body text.
|
|
28
|
+
*/
|
|
29
|
+
export declare function rankEntries<T extends RankableEntry>(entries: readonly T[], query: string, options?: RankEntriesOptions): T[];
|
|
30
|
+
/** Drops entries whose label and body are identical to an earlier entry (alias rows). */
|
|
31
|
+
export declare function dedupeEntries<T extends {
|
|
32
|
+
label: string;
|
|
33
|
+
text: string;
|
|
34
|
+
}>(entries: readonly T[]): T[];
|
|
35
|
+
/** One renderable piece of a text: matched a pattern, or plain. */
|
|
36
|
+
export interface SplitSegment {
|
|
37
|
+
/** Piece matched the pattern and should render emphasized or linked. */
|
|
38
|
+
matched?: boolean;
|
|
39
|
+
text: string;
|
|
40
|
+
}
|
|
41
|
+
/** Splits `text` on `pattern` matches, marking the matched pieces; a null pattern keeps it whole. */
|
|
42
|
+
export declare function splitPattern(text: string, pattern: RegExp | null): SplitSegment[];
|
|
43
|
+
/** Escapes a literal so it can be embedded in a regular expression. */
|
|
44
|
+
export declare const escapeRegExp: (value: string) => string;
|
|
45
|
+
//# sourceMappingURL=entries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../src/entries.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,oGAAoG;AACpG,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAYD,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,aAAa,EACjD,OAAO,EAAE,SAAS,CAAC,EAAE,EACrB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,CAAC,EAAE,CA0BL;AAED,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAQnG;AAED,mEAAmE;AACnE,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qGAAqG;AACrG,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,EAAE,CAcjF;AAID,uEAAuE;AACvE,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAA+C,CAAC"}
|
package/dist/entries.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={Exact:0,FuzzyOnly:5,LabelContains:2,LabelStart:1,MetaContains:3,TextContains:4};function rankEntries(t,n,r={}){let i=n.trim().toLowerCase();if(!i)return[...t];let a=r.fuzzyIds??new Set,o=[];for(let n of t){let t=n.label.toLowerCase(),r=t===i?e.Exact:t.startsWith(i)?e.LabelStart:t.includes(i)?e.LabelContains:n.meta.toLowerCase().includes(i)?e.MetaContains:n.text.toLowerCase().includes(i)?e.TextContains:a.has(n.id)?e.FuzzyOnly:null;r!==null&&o.push([r,n])}return o.sort((e,t)=>e[0]-t[0]||e[1].label.localeCompare(t[1].label)||e[1].text.localeCompare(t[1].text)).map(([,e])=>e)}function dedupeEntries(e){let t=new Set;return e.filter(e=>{let n=`${e.label}\u0000${e.text}`.toLowerCase();return!t.has(n)&&(t.add(n),!0)})}function splitPattern(e,t){if(!t)return[{text:e}];let n=[],r=0;for(let i of e.matchAll(t)){if(i[0].length===0)continue;let t=i.index??0;t>r&&n.push({text:e.slice(r,t)}),n.push({matched:!0,text:i[0]}),r=t+i[0].length}return r<e.length&&n.push({text:e.slice(r)}),n.length?n:[{text:e}]}var t=/[.*+?^${}()|[\]\\]/g,escapeRegExp=e=>e.replace(t,`\\$&`);export{dedupeEntries,escapeRegExp,rankEntries,splitPattern};
|
|
2
|
+
//# sourceMappingURL=entries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entries.js","names":[],"sources":["../src/entries.ts"],"sourcesContent":["/**\n * Entry ranking and segmentation for searchable reference lists: the presentation half of\n * search: a heterogeneous set of entries (glossary terms, rules, profiles) ordered by how\n * directly they match a typed query, plus segmentation helpers for rendering matched and\n * keyword-linked text.\n */\n\n/** An entry a reference list ranks and renders: identified, labeled, with summary and body text. */\nexport interface RankableEntry {\n id: string;\n /** The entry's title: the primary match surface. */\n label: string;\n /** One-line context shown beside the label. */\n meta: string;\n /** The entry's body text. */\n text: string;\n}\n\nconst Score = {\n Exact: 0,\n FuzzyOnly: 5,\n LabelContains: 2,\n LabelStart: 1,\n MetaContains: 3,\n TextContains: 4,\n} as const;\ntype Score = (typeof Score)[keyof typeof Score];\n\nexport interface RankEntriesOptions {\n /**\n * Entry ids the fuzzy index already matched: offered as the last resort when no field\n * contains the query, so approximate hits still surface.\n */\n fuzzyIds?: ReadonlySet<string>;\n}\n\n/**\n * Orders entries by how directly they match `query`: exact label, then label prefix, label,\n * meta, text, and finally fuzzy-index hits (`fuzzyIds`) as last resort. Entries matching none\n * of these are dropped. Ties break alphabetically by label, then by body text.\n */\nexport function rankEntries<T extends RankableEntry>(\n entries: readonly T[],\n query: string,\n options: RankEntriesOptions = {},\n): T[] {\n const lower = query.trim().toLowerCase();\n if (!lower) return [...entries];\n const fuzzyIds = options.fuzzyIds ?? new Set<string>();\n const scored: [Score, T][] = [];\n for (const entry of entries) {\n const label = entry.label.toLowerCase();\n const score =\n label === lower\n ? Score.Exact\n : label.startsWith(lower)\n ? Score.LabelStart\n : label.includes(lower)\n ? Score.LabelContains\n : entry.meta.toLowerCase().includes(lower)\n ? Score.MetaContains\n : entry.text.toLowerCase().includes(lower)\n ? Score.TextContains\n : fuzzyIds.has(entry.id)\n ? Score.FuzzyOnly\n : null;\n if (score !== null) scored.push([score, entry]);\n }\n return scored\n .sort((a, b) => a[0] - b[0] || a[1].label.localeCompare(b[1].label) || a[1].text.localeCompare(b[1].text))\n .map(([, entry]) => entry);\n}\n\n/** Drops entries whose label and body are identical to an earlier entry (alias rows). */\nexport function dedupeEntries<T extends { label: string; text: string }>(entries: readonly T[]): T[] {\n const seen = new Set<string>();\n return entries.filter((entry) => {\n const key = `${entry.label}\\u0000${entry.text}`.toLowerCase();\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n/** One renderable piece of a text: matched a pattern, or plain. */\nexport interface SplitSegment {\n /** Piece matched the pattern and should render emphasized or linked. */\n matched?: boolean;\n text: string;\n}\n\n/** Splits `text` on `pattern` matches, marking the matched pieces; a null pattern keeps it whole. */\nexport function splitPattern(text: string, pattern: RegExp | null): SplitSegment[] {\n if (!pattern) return [{ text }];\n const segments: SplitSegment[] = [];\n let last = 0;\n for (const match of text.matchAll(pattern)) {\n // Zero-length matches (optional-quantifier patterns) carry no text to emphasize.\n if (match[0].length === 0) continue;\n const start = match.index ?? 0;\n if (start > last) segments.push({ text: text.slice(last, start) });\n segments.push({ matched: true, text: match[0] });\n last = start + match[0].length;\n }\n if (last < text.length) segments.push({ text: text.slice(last) });\n return segments.length ? segments : [{ text }];\n}\n\nconst REGEXP_SPECIAL = /[.*+?^${}()|[\\]\\\\]/g;\n\n/** Escapes a literal so it can be embedded in a regular expression. */\nexport const escapeRegExp = (value: string): string => value.replace(REGEXP_SPECIAL, '\\\\$&');\n"],"mappings":"AAkBA,IAAM,EAAQ,CACZ,MAAO,EACP,UAAW,EACX,cAAe,EACf,WAAY,EACZ,aAAc,EACd,aAAc,CAChB,EAgBA,SAAgB,YACd,EACA,EACA,EAA8B,CAAC,EAC1B,CACL,IAAM,EAAQ,EAAM,KAAK,CAAC,CAAC,YAAY,EACvC,GAAI,CAAC,EAAO,MAAO,CAAC,GAAG,CAAO,EAC9B,IAAM,EAAW,EAAQ,UAAY,IAAI,IACnC,EAAuB,CAAC,EAC9B,IAAK,IAAM,KAAS,EAAS,CAC3B,IAAM,EAAQ,EAAM,MAAM,YAAY,EAChC,EACJ,IAAU,EACN,EAAM,MACN,EAAM,WAAW,CAAK,EACpB,EAAM,WACN,EAAM,SAAS,CAAK,EAClB,EAAM,cACN,EAAM,KAAK,YAAY,CAAC,CAAC,SAAS,CAAK,EACrC,EAAM,aACN,EAAM,KAAK,YAAY,CAAC,CAAC,SAAS,CAAK,EACrC,EAAM,aACN,EAAS,IAAI,EAAM,EAAE,EACnB,EAAM,UACN,KACZ,IAAU,MAAM,EAAO,KAAK,CAAC,EAAO,CAAK,CAAC,CAChD,CACA,OAAO,EACJ,MAAM,EAAG,IAAM,EAAE,GAAK,EAAE,IAAM,EAAE,EAAE,CAAC,MAAM,cAAc,EAAE,EAAE,CAAC,KAAK,GAAK,EAAE,EAAE,CAAC,KAAK,cAAc,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CACzG,KAAK,EAAG,KAAW,CAAK,CAC7B,CAGA,SAAgB,cAAyD,EAA4B,CACnG,IAAM,EAAO,IAAI,IACjB,OAAO,EAAQ,OAAQ,GAAU,CAC/B,IAAM,EAAM,GAAG,EAAM,MAAM,QAAQ,EAAM,OAAO,YAAY,EAG5D,MAFA,CAAI,EAAK,IAAI,CAAG,IAChB,EAAK,IAAI,CAAG,EACL,GACT,CAAC,CACH,CAUA,SAAgB,aAAa,EAAc,EAAwC,CACjF,GAAI,CAAC,EAAS,MAAO,CAAC,CAAE,MAAK,CAAC,EAC9B,IAAM,EAA2B,CAAC,EAC9B,EAAO,EACX,IAAK,IAAM,KAAS,EAAK,SAAS,CAAO,EAAG,CAE1C,GAAI,EAAM,EAAE,CAAC,SAAW,EAAG,SAC3B,IAAM,EAAQ,EAAM,OAAS,EACzB,EAAQ,GAAM,EAAS,KAAK,CAAE,KAAM,EAAK,MAAM,EAAM,CAAK,CAAE,CAAC,EACjE,EAAS,KAAK,CAAE,QAAS,GAAM,KAAM,EAAM,EAAG,CAAC,EAC/C,EAAO,EAAQ,EAAM,EAAE,CAAC,MAC1B,CAEA,OADI,EAAO,EAAK,QAAQ,EAAS,KAAK,CAAE,KAAM,EAAK,MAAM,CAAI,CAAE,CAAC,EACzD,EAAS,OAAS,EAAW,CAAC,CAAE,MAAK,CAAC,CAC/C,CAEA,IAAM,EAAiB,sBAGV,aAAgB,GAA0B,EAAM,QAAQ,EAAgB,MAAM"}
|
package/dist/errors.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var ScoutError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},ScoutDisposedError=class extends ScoutError{},ScoutConfigError=class extends ScoutError{};exports.ScoutConfigError=ScoutConfigError,exports.ScoutDisposedError=ScoutDisposedError,exports.ScoutError=ScoutError;
|
|
2
2
|
//# sourceMappingURL=errors.cjs.map
|
package/dist/errors.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all scout errors. Use `instanceof ScoutError` to catch any scout-originated error. */\nexport class ScoutError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a method is called on a disposed search state instance. */\nexport class ScoutDisposedError extends ScoutError {}\n\n/** Thrown when an index, search, or reactive search receives an invalid configuration. */\nexport class
|
|
1
|
+
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all scout errors. Use `instanceof ScoutError` to catch any scout-originated error. */\nexport class ScoutError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a method is called on a disposed search state instance. */\nexport class ScoutDisposedError extends ScoutError {}\n\n/** Thrown when an index, search, or reactive search receives an invalid configuration. */\nexport class ScoutConfigError extends ScoutError {}\n"],"mappings":"AACA,IAAa,WAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,mBAAb,cAAwC,UAAW,CAAC,EAGvC,iBAAb,cAAsC,UAAW,CAAC"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -6,6 +6,6 @@ export declare class ScoutError extends Error {
|
|
|
6
6
|
export declare class ScoutDisposedError extends ScoutError {
|
|
7
7
|
}
|
|
8
8
|
/** Thrown when an index, search, or reactive search receives an invalid configuration. */
|
|
9
|
-
export declare class
|
|
9
|
+
export declare class ScoutConfigError extends ScoutError {
|
|
10
10
|
}
|
|
11
11
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAKjD;AAED,0EAA0E;AAC1E,qBAAa,kBAAmB,SAAQ,UAAU;CAAG;AAErD,0FAA0F;AAC1F,qBAAa,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAKjD;AAED,0EAA0E;AAC1E,qBAAa,kBAAmB,SAAQ,UAAU;CAAG;AAErD,0FAA0F;AAC1F,qBAAa,gBAAiB,SAAQ,UAAU;CAAG"}
|
package/dist/errors.js
CHANGED
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
var e = class extends Error {
|
|
3
|
-
constructor(e, t) {
|
|
4
|
-
super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
|
|
5
|
-
}
|
|
6
|
-
}, t = class extends e {}, n = class extends e {};
|
|
7
|
-
//#endregion
|
|
8
|
-
export { n as ScoutConfigurationError, t as ScoutDisposedError, e as ScoutError };
|
|
9
|
-
|
|
1
|
+
var ScoutError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},ScoutDisposedError=class extends ScoutError{},ScoutConfigError=class extends ScoutError{};export{ScoutConfigError,ScoutDisposedError,ScoutError};
|
|
10
2
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all scout errors. Use `instanceof ScoutError` to catch any scout-originated error. */\nexport class ScoutError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a method is called on a disposed search state instance. */\nexport class ScoutDisposedError extends ScoutError {}\n\n/** Thrown when an index, search, or reactive search receives an invalid configuration. */\nexport class
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all scout errors. Use `instanceof ScoutError` to catch any scout-originated error. */\nexport class ScoutError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a method is called on a disposed search state instance. */\nexport class ScoutDisposedError extends ScoutError {}\n\n/** Thrown when an index, search, or reactive search receives an invalid configuration. */\nexport class ScoutConfigError extends ScoutError {}\n"],"mappings":"AACA,IAAa,WAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,mBAAb,cAAwC,UAAW,CAAC,EAGvC,iBAAb,cAAsC,UAAW,CAAC"}
|
package/dist/highlight.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./tokenize.cjs");function
|
|
1
|
+
const e=require("./tokenize.cjs");function findMatchRanges(t,n){let r=t.toLowerCase(),i=e.tokenize(n).split(` `).filter(Boolean),a=[];for(let e of i){let t=0;for(;t<r.length;){let n=r.indexOf(e,t);if(n===-1)break;a.push([n,n+e.length]),t=n+1}}a.sort((e,t)=>e[0]-t[0]);let o=[];for(let e of a){let t=o[o.length-1];t&&e[0]<=t[1]?t[1]=Math.max(t[1],e[1]):o.push([e[0],e[1]])}return o}function highlight(e,t){if(!e)return[];if(!t.length)return[{highlighted:!1,text:e}];let n=[],r=0;for(let[i,a]of t)i>r&&n.push({highlighted:!1,text:e.slice(r,i)}),a>i&&n.push({highlighted:!0,text:e.slice(i,a)}),r=a;return r<e.length&&n.push({highlighted:!1,text:e.slice(r)}),n}function highlightField(e,t,n){return highlight(n,e.matches.find(e=>e.field===t)?.ranges??[])}exports.findMatchRanges=findMatchRanges,exports.highlight=highlight,exports.highlightField=highlightField;
|
|
2
2
|
//# sourceMappingURL=highlight.cjs.map
|
package/dist/highlight.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"highlight.cjs","names":[],"sources":["../src/highlight.ts"],"sourcesContent":["import { tokenize } from './tokenize';\nimport type { HighlightPart, SearchResult } from './types';\n\n/**\n * Finds character ranges within `text` where `query` words appear.\n * Ranges are sorted and overlapping ranges are merged.\n *\n * Useful when you need to apply match ranges to a different string than the\n * indexed field value (e.g. a truncated preview or a differently formatted display string).\n *\n * @param text - The string to search within.\n * @param query - Raw query text. It is normalized with Scout's tokenizer before literal lookup.\n * @returns Sorted, non-overlapping `[start, end]` character ranges.\n */\nexport function findMatchRanges(text: string, query: string): [number, number][] {\n const lower = text.toLowerCase();\n const words = tokenize(query).split(' ').filter(Boolean);\n const ranges: [number, number][] = [];\n\n for (const word of words) {\n let pos = 0;\n\n while (pos < lower.length) {\n const idx = lower.indexOf(word, pos);\n\n if (idx === -1) break;\n\n ranges.push([idx, idx + word.length]);\n pos = idx + 1;\n }\n }\n\n ranges.sort((a, b) => a[0] - b[0]);\n\n const merged: [number, number][] = [];\n\n for (const range of ranges) {\n const last = merged[merged.length - 1];\n\n if (last && range[0] <= last[1]) {\n last[1] = Math.max(last[1], range[1]);\n } else {\n merged.push([range[0], range[1]]);\n }\n }\n\n return merged;\n}\n\n/**\n * Splits `text` into highlighted and unhighlighted fragments using match `ranges`.\n *\n * Ranges must be sorted and non-overlapping (as produced by `SearchResult.matches[n].ranges`).\n * Use the returned parts to render highlighted text in a UI component.\n *\n * **`part.text` is the original, unescaped field value** (e.g. a user's name, bio, or\n * product title)
|
|
1
|
+
{"version":3,"file":"highlight.cjs","names":[],"sources":["../src/highlight.ts"],"sourcesContent":["import { tokenize } from './tokenize';\nimport type { HighlightPart, SearchResult } from './types';\n\n/**\n * Finds character ranges within `text` where `query` words appear.\n * Ranges are sorted and overlapping ranges are merged.\n *\n * Useful when you need to apply match ranges to a different string than the\n * indexed field value (e.g. a truncated preview or a differently formatted display string).\n *\n * @param text - The string to search within.\n * @param query - Raw query text. It is normalized with Scout's tokenizer before literal lookup.\n * @returns Sorted, non-overlapping `[start, end]` character ranges.\n */\nexport function findMatchRanges(text: string, query: string): [number, number][] {\n const lower = text.toLowerCase();\n const words = tokenize(query).split(' ').filter(Boolean);\n const ranges: [number, number][] = [];\n\n for (const word of words) {\n let pos = 0;\n\n while (pos < lower.length) {\n const idx = lower.indexOf(word, pos);\n\n if (idx === -1) break;\n\n ranges.push([idx, idx + word.length]);\n pos = idx + 1;\n }\n }\n\n ranges.sort((a, b) => a[0] - b[0]);\n\n const merged: [number, number][] = [];\n\n for (const range of ranges) {\n const last = merged[merged.length - 1];\n\n if (last && range[0] <= last[1]) {\n last[1] = Math.max(last[1], range[1]);\n } else {\n merged.push([range[0], range[1]]);\n }\n }\n\n return merged;\n}\n\n/**\n * Splits `text` into highlighted and unhighlighted fragments using match `ranges`.\n *\n * Ranges must be sorted and non-overlapping (as produced by `SearchResult.matches[n].ranges`).\n * Use the returned parts to render highlighted text in a UI component.\n *\n * **`part.text` is the original, unescaped field value** (e.g. a user's name, bio, or\n * product title): this function does no HTML escaping. Render each part via safe DOM APIs\n * (`textContent`, a framework's text binding) and wrap `highlighted` parts in your own\n * element (e.g. `<mark>`); never concatenate `part.text` into an HTML string for\n * `innerHTML`, that reintroduces the XSS risk this structured return shape avoids.\n *\n * @example\n * ```ts\n * highlight('Hello World', [[0, 5]]);\n * // [{ text: 'Hello', highlighted: true }, { text: ' World', highlighted: false }]\n *\n * highlight('Hello World', [[0, 5], [6, 11]]);\n * // [\n * // { text: 'Hello', highlighted: true },\n * // { text: ' ', highlighted: false },\n * // { text: 'World', highlighted: true },\n * // ]\n * ```\n *\n * @param text - The original field value to split.\n * @param ranges - Sorted, non-overlapping `[start, end]` ranges from `FieldMatch.ranges`.\n * @returns An array of `HighlightPart` objects. Returns an empty array for an empty `text`.\n */\nexport function highlight(text: string, ranges: [number, number][]): HighlightPart[] {\n if (!text) return [];\n\n if (!ranges.length) return [{ highlighted: false, text }];\n\n const parts: HighlightPart[] = [];\n let cursor = 0;\n\n for (const [start, end] of ranges) {\n if (start > cursor) {\n parts.push({ highlighted: false, text: text.slice(cursor, start) });\n }\n\n if (end > start) {\n parts.push({ highlighted: true, text: text.slice(start, end) });\n }\n\n cursor = end;\n }\n\n if (cursor < text.length) {\n parts.push({ highlighted: false, text: text.slice(cursor) });\n }\n\n return parts;\n}\n\n/**\n * Finds the match ranges for `field` in `result` and splits `text` into\n * highlighted and unhighlighted fragments in one step.\n *\n * This is the ergonomic shorthand for the common pattern:\n * ```ts\n * const match = result.matches.find(m => m.field === 'name');\n * const parts = highlight(item.name, match?.ranges ?? []);\n * ```\n *\n * @example\n * ```ts\n * for (const result of index.search('alice')) {\n * const parts = highlightField(result, 'name', result.item.name);\n * console.log(parts.map(p => p.highlighted ? `[${p.text}]` : p.text).join(''));\n * }\n * ```\n *\n * @param result - A `SearchResult` from `ScoutIndex.search()`.\n * @param field - The field name to look up in `result.matches`.\n * @param text - The original field value string to split.\n * @returns An array of `HighlightPart` objects.\n */\nexport function highlightField<T>(result: SearchResult<T>, field: keyof T & string, text: string): HighlightPart[] {\n const match = result.matches.find((m) => m.field === field);\n\n return highlight(text, match?.ranges ?? []);\n}\n"],"mappings":"kCAcA,SAAgB,gBAAgB,EAAc,EAAmC,CAC/E,IAAM,EAAQ,EAAK,YAAY,EACzB,EAAQ,EAAA,SAAS,CAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,EACjD,EAA6B,CAAC,EAEpC,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAI,EAAM,EAEV,KAAO,EAAM,EAAM,QAAQ,CACzB,IAAM,EAAM,EAAM,QAAQ,EAAM,CAAG,EAEnC,GAAI,IAAQ,GAAI,MAEhB,EAAO,KAAK,CAAC,EAAK,EAAM,EAAK,MAAM,CAAC,EACpC,EAAM,EAAM,CACd,CACF,CAEA,EAAO,MAAM,EAAG,IAAM,EAAE,GAAK,EAAE,EAAE,EAEjC,IAAM,EAA6B,CAAC,EAEpC,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAO,EAAO,EAAO,OAAS,GAEhC,GAAQ,EAAM,IAAM,EAAK,GAC3B,EAAK,GAAK,KAAK,IAAI,EAAK,GAAI,EAAM,EAAE,EAEpC,EAAO,KAAK,CAAC,EAAM,GAAI,EAAM,EAAE,CAAC,CAEpC,CAEA,OAAO,CACT,CA+BA,SAAgB,UAAU,EAAc,EAA6C,CACnF,GAAI,CAAC,EAAM,MAAO,CAAC,EAEnB,GAAI,CAAC,EAAO,OAAQ,MAAO,CAAC,CAAE,YAAa,GAAO,MAAK,CAAC,EAExD,IAAM,EAAyB,CAAC,EAC5B,EAAS,EAEb,IAAK,GAAM,CAAC,EAAO,KAAQ,EACrB,EAAQ,GACV,EAAM,KAAK,CAAE,YAAa,GAAO,KAAM,EAAK,MAAM,EAAQ,CAAK,CAAE,CAAC,EAGhE,EAAM,GACR,EAAM,KAAK,CAAE,YAAa,GAAM,KAAM,EAAK,MAAM,EAAO,CAAG,CAAE,CAAC,EAGhE,EAAS,EAOX,OAJI,EAAS,EAAK,QAChB,EAAM,KAAK,CAAE,YAAa,GAAO,KAAM,EAAK,MAAM,CAAM,CAAE,CAAC,EAGtD,CACT,CAyBA,SAAgB,eAAkB,EAAyB,EAAyB,EAA+B,CAGjH,OAAO,UAAU,EAFH,EAAO,QAAQ,KAAM,GAAM,EAAE,QAAU,CAE9B,CAAA,EAAO,QAAU,CAAC,CAAC,CAC5C"}
|
package/dist/highlight.d.ts
CHANGED
|
@@ -18,10 +18,10 @@ export declare function findMatchRanges(text: string, query: string): [number, n
|
|
|
18
18
|
* Use the returned parts to render highlighted text in a UI component.
|
|
19
19
|
*
|
|
20
20
|
* **`part.text` is the original, unescaped field value** (e.g. a user's name, bio, or
|
|
21
|
-
* product title)
|
|
21
|
+
* product title): this function does no HTML escaping. Render each part via safe DOM APIs
|
|
22
22
|
* (`textContent`, a framework's text binding) and wrap `highlighted` parts in your own
|
|
23
23
|
* element (e.g. `<mark>`); never concatenate `part.text` into an HTML string for
|
|
24
|
-
* `innerHTML
|
|
24
|
+
* `innerHTML`, that reintroduces the XSS risk this structured return shape avoids.
|
|
25
25
|
*
|
|
26
26
|
* @example
|
|
27
27
|
* ```ts
|
package/dist/highlight.js
CHANGED
|
@@ -1,46 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
//#region src/highlight.ts
|
|
3
|
-
function t(t, n) {
|
|
4
|
-
let r = t.toLowerCase(), i = e(n).split(" ").filter(Boolean), a = [];
|
|
5
|
-
for (let e of i) {
|
|
6
|
-
let t = 0;
|
|
7
|
-
for (; t < r.length;) {
|
|
8
|
-
let n = r.indexOf(e, t);
|
|
9
|
-
if (n === -1) break;
|
|
10
|
-
a.push([n, n + e.length]), t = n + 1;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
a.sort((e, t) => e[0] - t[0]);
|
|
14
|
-
let o = [];
|
|
15
|
-
for (let e of a) {
|
|
16
|
-
let t = o[o.length - 1];
|
|
17
|
-
t && e[0] <= t[1] ? t[1] = Math.max(t[1], e[1]) : o.push([e[0], e[1]]);
|
|
18
|
-
}
|
|
19
|
-
return o;
|
|
20
|
-
}
|
|
21
|
-
function n(e, t) {
|
|
22
|
-
if (!e) return [];
|
|
23
|
-
if (!t.length) return [{
|
|
24
|
-
highlighted: !1,
|
|
25
|
-
text: e
|
|
26
|
-
}];
|
|
27
|
-
let n = [], r = 0;
|
|
28
|
-
for (let [i, a] of t) i > r && n.push({
|
|
29
|
-
highlighted: !1,
|
|
30
|
-
text: e.slice(r, i)
|
|
31
|
-
}), a > i && n.push({
|
|
32
|
-
highlighted: !0,
|
|
33
|
-
text: e.slice(i, a)
|
|
34
|
-
}), r = a;
|
|
35
|
-
return r < e.length && n.push({
|
|
36
|
-
highlighted: !1,
|
|
37
|
-
text: e.slice(r)
|
|
38
|
-
}), n;
|
|
39
|
-
}
|
|
40
|
-
function r(e, t, r) {
|
|
41
|
-
return n(r, e.matches.find((e) => e.field === t)?.ranges ?? []);
|
|
42
|
-
}
|
|
43
|
-
//#endregion
|
|
44
|
-
export { t as findMatchRanges, n as highlight, r as highlightField };
|
|
45
|
-
|
|
1
|
+
import{tokenize as e}from"./tokenize.js";function findMatchRanges(t,n){let r=t.toLowerCase(),i=e(n).split(` `).filter(Boolean),a=[];for(let e of i){let t=0;for(;t<r.length;){let n=r.indexOf(e,t);if(n===-1)break;a.push([n,n+e.length]),t=n+1}}a.sort((e,t)=>e[0]-t[0]);let o=[];for(let e of a){let t=o[o.length-1];t&&e[0]<=t[1]?t[1]=Math.max(t[1],e[1]):o.push([e[0],e[1]])}return o}function highlight(e,t){if(!e)return[];if(!t.length)return[{highlighted:!1,text:e}];let n=[],r=0;for(let[i,a]of t)i>r&&n.push({highlighted:!1,text:e.slice(r,i)}),a>i&&n.push({highlighted:!0,text:e.slice(i,a)}),r=a;return r<e.length&&n.push({highlighted:!1,text:e.slice(r)}),n}function highlightField(e,t,n){return highlight(n,e.matches.find(e=>e.field===t)?.ranges??[])}export{findMatchRanges,highlight,highlightField};
|
|
46
2
|
//# sourceMappingURL=highlight.js.map
|
package/dist/highlight.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"highlight.js","names":[],"sources":["../src/highlight.ts"],"sourcesContent":["import { tokenize } from './tokenize';\nimport type { HighlightPart, SearchResult } from './types';\n\n/**\n * Finds character ranges within `text` where `query` words appear.\n * Ranges are sorted and overlapping ranges are merged.\n *\n * Useful when you need to apply match ranges to a different string than the\n * indexed field value (e.g. a truncated preview or a differently formatted display string).\n *\n * @param text - The string to search within.\n * @param query - Raw query text. It is normalized with Scout's tokenizer before literal lookup.\n * @returns Sorted, non-overlapping `[start, end]` character ranges.\n */\nexport function findMatchRanges(text: string, query: string): [number, number][] {\n const lower = text.toLowerCase();\n const words = tokenize(query).split(' ').filter(Boolean);\n const ranges: [number, number][] = [];\n\n for (const word of words) {\n let pos = 0;\n\n while (pos < lower.length) {\n const idx = lower.indexOf(word, pos);\n\n if (idx === -1) break;\n\n ranges.push([idx, idx + word.length]);\n pos = idx + 1;\n }\n }\n\n ranges.sort((a, b) => a[0] - b[0]);\n\n const merged: [number, number][] = [];\n\n for (const range of ranges) {\n const last = merged[merged.length - 1];\n\n if (last && range[0] <= last[1]) {\n last[1] = Math.max(last[1], range[1]);\n } else {\n merged.push([range[0], range[1]]);\n }\n }\n\n return merged;\n}\n\n/**\n * Splits `text` into highlighted and unhighlighted fragments using match `ranges`.\n *\n * Ranges must be sorted and non-overlapping (as produced by `SearchResult.matches[n].ranges`).\n * Use the returned parts to render highlighted text in a UI component.\n *\n * **`part.text` is the original, unescaped field value** (e.g. a user's name, bio, or\n * product title)
|
|
1
|
+
{"version":3,"file":"highlight.js","names":[],"sources":["../src/highlight.ts"],"sourcesContent":["import { tokenize } from './tokenize';\nimport type { HighlightPart, SearchResult } from './types';\n\n/**\n * Finds character ranges within `text` where `query` words appear.\n * Ranges are sorted and overlapping ranges are merged.\n *\n * Useful when you need to apply match ranges to a different string than the\n * indexed field value (e.g. a truncated preview or a differently formatted display string).\n *\n * @param text - The string to search within.\n * @param query - Raw query text. It is normalized with Scout's tokenizer before literal lookup.\n * @returns Sorted, non-overlapping `[start, end]` character ranges.\n */\nexport function findMatchRanges(text: string, query: string): [number, number][] {\n const lower = text.toLowerCase();\n const words = tokenize(query).split(' ').filter(Boolean);\n const ranges: [number, number][] = [];\n\n for (const word of words) {\n let pos = 0;\n\n while (pos < lower.length) {\n const idx = lower.indexOf(word, pos);\n\n if (idx === -1) break;\n\n ranges.push([idx, idx + word.length]);\n pos = idx + 1;\n }\n }\n\n ranges.sort((a, b) => a[0] - b[0]);\n\n const merged: [number, number][] = [];\n\n for (const range of ranges) {\n const last = merged[merged.length - 1];\n\n if (last && range[0] <= last[1]) {\n last[1] = Math.max(last[1], range[1]);\n } else {\n merged.push([range[0], range[1]]);\n }\n }\n\n return merged;\n}\n\n/**\n * Splits `text` into highlighted and unhighlighted fragments using match `ranges`.\n *\n * Ranges must be sorted and non-overlapping (as produced by `SearchResult.matches[n].ranges`).\n * Use the returned parts to render highlighted text in a UI component.\n *\n * **`part.text` is the original, unescaped field value** (e.g. a user's name, bio, or\n * product title): this function does no HTML escaping. Render each part via safe DOM APIs\n * (`textContent`, a framework's text binding) and wrap `highlighted` parts in your own\n * element (e.g. `<mark>`); never concatenate `part.text` into an HTML string for\n * `innerHTML`, that reintroduces the XSS risk this structured return shape avoids.\n *\n * @example\n * ```ts\n * highlight('Hello World', [[0, 5]]);\n * // [{ text: 'Hello', highlighted: true }, { text: ' World', highlighted: false }]\n *\n * highlight('Hello World', [[0, 5], [6, 11]]);\n * // [\n * // { text: 'Hello', highlighted: true },\n * // { text: ' ', highlighted: false },\n * // { text: 'World', highlighted: true },\n * // ]\n * ```\n *\n * @param text - The original field value to split.\n * @param ranges - Sorted, non-overlapping `[start, end]` ranges from `FieldMatch.ranges`.\n * @returns An array of `HighlightPart` objects. Returns an empty array for an empty `text`.\n */\nexport function highlight(text: string, ranges: [number, number][]): HighlightPart[] {\n if (!text) return [];\n\n if (!ranges.length) return [{ highlighted: false, text }];\n\n const parts: HighlightPart[] = [];\n let cursor = 0;\n\n for (const [start, end] of ranges) {\n if (start > cursor) {\n parts.push({ highlighted: false, text: text.slice(cursor, start) });\n }\n\n if (end > start) {\n parts.push({ highlighted: true, text: text.slice(start, end) });\n }\n\n cursor = end;\n }\n\n if (cursor < text.length) {\n parts.push({ highlighted: false, text: text.slice(cursor) });\n }\n\n return parts;\n}\n\n/**\n * Finds the match ranges for `field` in `result` and splits `text` into\n * highlighted and unhighlighted fragments in one step.\n *\n * This is the ergonomic shorthand for the common pattern:\n * ```ts\n * const match = result.matches.find(m => m.field === 'name');\n * const parts = highlight(item.name, match?.ranges ?? []);\n * ```\n *\n * @example\n * ```ts\n * for (const result of index.search('alice')) {\n * const parts = highlightField(result, 'name', result.item.name);\n * console.log(parts.map(p => p.highlighted ? `[${p.text}]` : p.text).join(''));\n * }\n * ```\n *\n * @param result - A `SearchResult` from `ScoutIndex.search()`.\n * @param field - The field name to look up in `result.matches`.\n * @param text - The original field value string to split.\n * @returns An array of `HighlightPart` objects.\n */\nexport function highlightField<T>(result: SearchResult<T>, field: keyof T & string, text: string): HighlightPart[] {\n const match = result.matches.find((m) => m.field === field);\n\n return highlight(text, match?.ranges ?? []);\n}\n"],"mappings":"yCAcA,SAAgB,gBAAgB,EAAc,EAAmC,CAC/E,IAAM,EAAQ,EAAK,YAAY,EACzB,EAAQ,EAAS,CAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,EACjD,EAA6B,CAAC,EAEpC,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAI,EAAM,EAEV,KAAO,EAAM,EAAM,QAAQ,CACzB,IAAM,EAAM,EAAM,QAAQ,EAAM,CAAG,EAEnC,GAAI,IAAQ,GAAI,MAEhB,EAAO,KAAK,CAAC,EAAK,EAAM,EAAK,MAAM,CAAC,EACpC,EAAM,EAAM,CACd,CACF,CAEA,EAAO,MAAM,EAAG,IAAM,EAAE,GAAK,EAAE,EAAE,EAEjC,IAAM,EAA6B,CAAC,EAEpC,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAO,EAAO,EAAO,OAAS,GAEhC,GAAQ,EAAM,IAAM,EAAK,GAC3B,EAAK,GAAK,KAAK,IAAI,EAAK,GAAI,EAAM,EAAE,EAEpC,EAAO,KAAK,CAAC,EAAM,GAAI,EAAM,EAAE,CAAC,CAEpC,CAEA,OAAO,CACT,CA+BA,SAAgB,UAAU,EAAc,EAA6C,CACnF,GAAI,CAAC,EAAM,MAAO,CAAC,EAEnB,GAAI,CAAC,EAAO,OAAQ,MAAO,CAAC,CAAE,YAAa,GAAO,MAAK,CAAC,EAExD,IAAM,EAAyB,CAAC,EAC5B,EAAS,EAEb,IAAK,GAAM,CAAC,EAAO,KAAQ,EACrB,EAAQ,GACV,EAAM,KAAK,CAAE,YAAa,GAAO,KAAM,EAAK,MAAM,EAAQ,CAAK,CAAE,CAAC,EAGhE,EAAM,GACR,EAAM,KAAK,CAAE,YAAa,GAAM,KAAM,EAAK,MAAM,EAAO,CAAG,CAAE,CAAC,EAGhE,EAAS,EAOX,OAJI,EAAS,EAAK,QAChB,EAAM,KAAK,CAAE,YAAa,GAAO,KAAM,EAAK,MAAM,CAAM,CAAE,CAAC,EAGtD,CACT,CAyBA,SAAgB,eAAkB,EAAyB,EAAyB,EAA+B,CAGjH,OAAO,UAAU,EAFH,EAAO,QAAQ,KAAM,GAAM,EAAE,QAAU,CAE9B,CAAA,EAAO,QAAU,CAAC,CAAC,CAC5C"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters.cjs"),t=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters.cjs"),t=require("./entries.cjs"),n=require("./errors.cjs"),r=require("./highlight.cjs"),i=require("./scout-index.cjs"),a=require("./reactive.cjs"),o=require("./segment.cjs");exports.ScoutConfigError=n.ScoutConfigError,exports.ScoutDisposedError=n.ScoutDisposedError,exports.ScoutError=n.ScoutError,exports.createIndex=i.createIndex,exports.createReactiveSearch=a.createReactiveSearch,exports.createSearch=a.createSearch,exports.dedupeEntries=t.dedupeEntries,exports.escapeRegExp=t.escapeRegExp,exports.findMatchRanges=r.findMatchRanges,exports.highlight=r.highlight,exports.highlightField=r.highlightField,exports.rankEntries=t.rankEntries,exports.segmentWords=o.segmentWords,exports.splitPattern=t.splitPattern,exports.toFilterPredicate=e.toFilterPredicate,exports.toSearchMatcher=e.toSearchMatcher;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { toFilterPredicate, toSearchMatcher } from './adapters';
|
|
2
|
-
export {
|
|
2
|
+
export { dedupeEntries, escapeRegExp, type RankableEntry, rankEntries, type SplitSegment, splitPattern, } from './entries';
|
|
3
|
+
export { ScoutConfigError, ScoutDisposedError, ScoutError } from './errors';
|
|
3
4
|
export { findMatchRanges, highlight, highlightField } from './highlight';
|
|
4
5
|
export type { ReactiveSearch } from './reactive';
|
|
5
6
|
export { createReactiveSearch, createSearch } from './reactive';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EACL,aAAa,EACb,YAAY,EACZ,KAAK,aAAa,EAClB,WAAW,EACX,KAAK,YAAY,EACjB,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAChE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EACV,mBAAmB,EACnB,QAAQ,EACR,UAAU,EACV,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,WAAW,EACX,sBAAsB,GACvB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ScoutConfigurationError as n, ScoutDisposedError as r, ScoutError as i } from "./errors.js";
|
|
3
|
-
import { findMatchRanges as a, highlight as o, highlightField as s } from "./highlight.js";
|
|
4
|
-
import { createIndex as c } from "./scout-index.js";
|
|
5
|
-
import { createReactiveSearch as l, createSearch as u } from "./reactive.js";
|
|
6
|
-
import { segmentWords as d } from "./segment.js";
|
|
7
|
-
export { n as ScoutConfigurationError, r as ScoutDisposedError, i as ScoutError, c as createIndex, l as createReactiveSearch, u as createSearch, a as findMatchRanges, o as highlight, s as highlightField, d as segmentWords, e as toFilterPredicate, t as toSearchMatcher };
|
|
1
|
+
import{toFilterPredicate as e,toSearchMatcher as t}from"./adapters.js";import{dedupeEntries as n,escapeRegExp as r,rankEntries as i,splitPattern as a}from"./entries.js";import{ScoutConfigError as o,ScoutDisposedError as s,ScoutError as c}from"./errors.js";import{findMatchRanges as l,highlight as u,highlightField as d}from"./highlight.js";import{createIndex as f}from"./scout-index.js";import{createReactiveSearch as p,createSearch as m}from"./reactive.js";import{segmentWords as h}from"./segment.js";export{o as ScoutConfigError,s as ScoutDisposedError,c as ScoutError,f as createIndex,p as createReactiveSearch,m as createSearch,n as dedupeEntries,r as escapeRegExp,l as findMatchRanges,u as highlight,d as highlightField,i as rankEntries,h as segmentWords,a as splitPattern,e as toFilterPredicate,t as toSearchMatcher};
|
package/dist/reactive.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs"),t=require("./scout-index.cjs");
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./scout-index.cjs");let n=require("@vielzeug/arsenal");var r=200;function notify(e){for(let t of[...e])try{t()}catch(e){queueMicrotask(()=>{throw e})}}function subscribe(e,t,n,r,i){if(r||n?.signal?.aborted)return()=>{};e.add(t);let unsubscribe=()=>{e.delete(t),n?.signal?.removeEventListener(`abort`,unsubscribe)};return n?.signal?.addEventListener(`abort`,unsubscribe,{once:!0,signal:i}),unsubscribe}function createSearch(t,i={}){let{debounce:a=r,limit:o,minQueryLength:s,threshold:c}=i;if(!Number.isSafeInteger(a)||a<0)throw new e.ScoutConfigError(`debounce must be a finite non-negative integer.`);let search=e=>t.search(e,{limit:o,minQueryLength:s,threshold:c}),createSnapshot=(e,t,n)=>Object.freeze({isSearching:t,query:e,results:Object.freeze(n)}),l=new Set,u=(0,n.tapper)(),d=new AbortController,f=createSnapshot(``,!1,search(``)),p=``,m=!1,h,publish=e=>{f=e,notify(l),u.emit({snapshot:e,type:`state-change`})},commit=e=>{p=e,publish(createSnapshot(e,!1,search(e)))},g=t.onMutate(()=>{publish(createSnapshot(f.query,f.isSearching,search(p)))}),assertActive=t=>{if(m)throw new e.ScoutDisposedError(`SearchState.${t}() called after dispose()`)},dispose=()=>{m||(m=!0,h!==void 0&&clearTimeout(h),h=void 0,g(),l.clear(),u.clear(),d.abort())};return{clear(){assertActive(`clear`),(f.query!==``||f.isSearching)&&(h!==void 0&&clearTimeout(h),h=void 0,commit(``))},get disposalSignal(){return d.signal},dispose,get disposed(){return m},getSnapshot(){return f},setQuery(e){if(assertActive(`setQuery`),e!==f.query){if(h!==void 0&&clearTimeout(h),h=void 0,e===p){publish(createSnapshot(e,!1,f.results));return}if(a===0){commit(e);return}publish(createSnapshot(e,!0,f.results)),h=setTimeout(()=>{h=void 0,commit(e)},a)}},subscribe(e,t){return subscribe(l,e,t,m,d.signal)},tap(e,t){return m?()=>{}:u.tap(e,t)},[Symbol.dispose]:dispose}}function createReactiveSearch(e,n){let r=t.createIndex(e,n),i=createSearch(r,n);return{clear:i.clear,get disposalSignal(){return i.disposalSignal},dispose:i.dispose,get disposed(){return i.disposed},getSnapshot:i.getSnapshot,index:r,setQuery:i.setQuery,subscribe:i.subscribe,tap:i.tap,[Symbol.dispose]:i[Symbol.dispose]}}exports.createReactiveSearch=createReactiveSearch,exports.createSearch=createSearch;
|
|
2
2
|
//# sourceMappingURL=reactive.cjs.map
|
package/dist/reactive.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.cjs","names":[],"sources":["../src/reactive.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"reactive.cjs","names":[],"sources":["../src/reactive.ts"],"sourcesContent":["import { tapper } from '@vielzeug/arsenal';\n\nimport { ScoutConfigError, ScoutDisposedError } from './errors';\nimport { createIndex, type ScoutIndex } from './scout-index';\nimport type {\n CreateSearchOptions,\n ScoutEvent,\n ScoutIndexOptions,\n SearchResult,\n SearchSnapshot,\n SearchState,\n SearchSubscribeOptions,\n} from './types';\n\nexport type ReactiveSearch<T> = SearchState<T> & { readonly index: ScoutIndex<T> };\n\nconst DEFAULT_DEBOUNCE = 200;\n\nfunction notify(listeners: Set<() => void>): void {\n for (const listener of [...listeners]) {\n try {\n listener();\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n}\n\nfunction subscribe(\n listeners: Set<() => void>,\n listener: () => void,\n options: SearchSubscribeOptions | undefined,\n disposed: boolean,\n lifetimeSignal: AbortSignal,\n): () => void {\n if (disposed || options?.signal?.aborted) return () => {};\n\n listeners.add(listener);\n const unsubscribe = (): void => {\n listeners.delete(listener);\n options?.signal?.removeEventListener('abort', unsubscribe);\n };\n options?.signal?.addEventListener('abort', unsubscribe, { once: true, signal: lifetimeSignal });\n return unsubscribe;\n}\n\nexport function createSearch<T>(index: ScoutIndex<T>, options: CreateSearchOptions = {}): SearchState<T> {\n const { debounce: debounceMs = DEFAULT_DEBOUNCE, limit, minQueryLength, threshold } = options;\n\n if (!Number.isSafeInteger(debounceMs) || debounceMs < 0) {\n throw new ScoutConfigError('debounce must be a finite non-negative integer.');\n }\n\n const search = (query: string) => index.search(query, { limit, minQueryLength, threshold });\n const createSnapshot = (\n query: string,\n isSearching: boolean,\n results: ReadonlyArray<SearchResult<T>>,\n ): SearchSnapshot<T> => Object.freeze({ isSearching, query, results: Object.freeze(results) });\n const listeners = new Set<() => void>();\n const tappers = tapper<ScoutEvent<T>>();\n const controller = new AbortController();\n let snapshot = createSnapshot('', false, search(''));\n let committedQuery = '';\n let disposed = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const publish = (next: SearchSnapshot<T>): void => {\n snapshot = next;\n notify(listeners);\n tappers.emit({ snapshot: next, type: 'state-change' });\n };\n const commit = (query: string): void => {\n committedQuery = query;\n publish(createSnapshot(query, false, search(query)));\n };\n const unsubscribeIndex = index.onMutate(() => {\n publish(createSnapshot(snapshot.query, snapshot.isSearching, search(committedQuery)));\n });\n const assertActive = (method: string): void => {\n if (disposed) throw new ScoutDisposedError(`SearchState.${method}() called after dispose()`);\n };\n const dispose = (): void => {\n if (disposed) return;\n disposed = true;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n unsubscribeIndex();\n listeners.clear();\n tappers.clear();\n controller.abort();\n };\n\n return {\n clear() {\n assertActive('clear');\n if (snapshot.query === '' && !snapshot.isSearching) return;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n commit('');\n },\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return snapshot;\n },\n setQuery(query) {\n assertActive('setQuery');\n if (query === snapshot.query) return;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n\n if (query === committedQuery) {\n publish(createSnapshot(query, false, snapshot.results));\n return;\n }\n\n if (debounceMs === 0) {\n commit(query);\n return;\n }\n\n publish(createSnapshot(query, true, snapshot.results));\n timer = setTimeout(() => {\n timer = undefined;\n commit(query);\n }, debounceMs);\n },\n subscribe(listener, subscribeOptions) {\n return subscribe(listeners, listener, subscribeOptions, disposed, controller.signal);\n },\n tap(handler, tapOptions) {\n if (disposed) return () => {};\n\n return tappers.tap(handler, tapOptions);\n },\n [Symbol.dispose]: dispose,\n };\n}\n\nexport function createReactiveSearch<T>(\n items: T[],\n options: ScoutIndexOptions<T> & Pick<CreateSearchOptions, 'debounce'>,\n): ReactiveSearch<T> {\n const index = createIndex(items, options);\n const state = createSearch(index, options);\n\n return {\n clear: state.clear,\n get disposalSignal() {\n return state.disposalSignal;\n },\n dispose: state.dispose,\n get disposed() {\n return state.disposed;\n },\n getSnapshot: state.getSnapshot,\n index,\n setQuery: state.setQuery,\n subscribe: state.subscribe,\n tap: state.tap,\n [Symbol.dispose]: state[Symbol.dispose],\n };\n}\n"],"mappings":"kGAgBA,IAAM,EAAmB,IAEzB,SAAS,OAAO,EAAkC,CAChD,IAAK,IAAM,IAAY,CAAC,GAAG,CAAS,EAClC,GAAI,CACF,EAAS,CACX,OAAS,EAAO,CACd,mBAAqB,CACnB,MAAM,CACR,CAAC,CACH,CAEJ,CAEA,SAAS,UACP,EACA,EACA,EACA,EACA,EACY,CACZ,GAAI,GAAY,GAAS,QAAQ,QAAS,UAAa,CAAC,EAExD,EAAU,IAAI,CAAQ,EACtB,IAAM,gBAA0B,CAC9B,EAAU,OAAO,CAAQ,EACzB,GAAS,QAAQ,oBAAoB,QAAS,WAAW,CAC3D,EAEA,OADA,GAAS,QAAQ,iBAAiB,QAAS,YAAa,CAAE,KAAM,GAAM,OAAQ,CAAe,CAAC,EACvF,WACT,CAEA,SAAgB,aAAgB,EAAsB,EAA+B,CAAC,EAAmB,CACvG,GAAM,CAAE,SAAU,EAAa,EAAkB,QAAO,iBAAgB,aAAc,EAEtF,GAAI,CAAC,OAAO,cAAc,CAAU,GAAK,EAAa,EACpD,MAAM,IAAI,EAAA,iBAAiB,iDAAiD,EAG9E,IAAM,OAAU,GAAkB,EAAM,OAAO,EAAO,CAAE,QAAO,iBAAgB,WAAU,CAAC,EACpF,gBACJ,EACA,EACA,IACsB,OAAO,OAAO,CAAE,cAAa,QAAO,QAAS,OAAO,OAAO,CAAO,CAAE,CAAC,EACvF,EAAY,IAAI,IAChB,GAAA,EAAU,EAAA,OAAA,CAAsB,EAChC,EAAa,IAAI,gBACnB,EAAW,eAAe,GAAI,GAAO,OAAO,EAAE,CAAC,EAC/C,EAAiB,GACjB,EAAW,GACX,EAEE,QAAW,GAAkC,CACjD,EAAW,EACX,OAAO,CAAS,EAChB,EAAQ,KAAK,CAAE,SAAU,EAAM,KAAM,cAAe,CAAC,CACvD,EACM,OAAU,GAAwB,CACtC,EAAiB,EACjB,QAAQ,eAAe,EAAO,GAAO,OAAO,CAAK,CAAC,CAAC,CACrD,EACM,EAAmB,EAAM,aAAe,CAC5C,QAAQ,eAAe,EAAS,MAAO,EAAS,YAAa,OAAO,CAAc,CAAC,CAAC,CACtF,CAAC,EACK,aAAgB,GAAyB,CAC7C,GAAI,EAAU,MAAM,IAAI,EAAA,mBAAmB,eAAe,EAAO,0BAA0B,CAC7F,EACM,YAAsB,CACtB,IACJ,EAAW,GACP,IAAU,IAAA,IAAW,aAAa,CAAK,EAC3C,EAAQ,IAAA,GACR,EAAiB,EACjB,EAAU,MAAM,EAChB,EAAQ,MAAM,EACd,EAAW,MAAM,EACnB,EAEA,MAAO,CACL,OAAQ,CACN,aAAa,OAAO,GAChB,EAAS,QAAU,IAAO,EAAS,eACnC,IAAU,IAAA,IAAW,aAAa,CAAK,EAC3C,EAAQ,IAAA,GACR,OAAO,EAAE,EACX,EACA,IAAI,gBAAiB,CACnB,OAAO,EAAW,MACpB,EACA,QACA,IAAI,UAAW,CACb,OAAO,CACT,EACA,aAAc,CACZ,OAAO,CACT,EACA,SAAS,EAAO,CACd,gBAAa,UAAU,EACnB,IAAU,EAAS,MAIvB,IAHI,IAAU,IAAA,IAAW,aAAa,CAAK,EAC3C,EAAQ,IAAA,GAEJ,IAAU,EAAgB,CAC5B,QAAQ,eAAe,EAAO,GAAO,EAAS,OAAO,CAAC,EACtD,MACF,CAEA,GAAI,IAAe,EAAG,CACpB,OAAO,CAAK,EACZ,MACF,CAEA,QAAQ,eAAe,EAAO,GAAM,EAAS,OAAO,CAAC,EACrD,EAAQ,eAAiB,CACvB,EAAQ,IAAA,GACR,OAAO,CAAK,CACd,EAAG,CAAU,CAXb,CAYF,EACA,UAAU,EAAU,EAAkB,CACpC,OAAO,UAAU,EAAW,EAAU,EAAkB,EAAU,EAAW,MAAM,CACrF,EACA,IAAI,EAAS,EAAY,CAGvB,OAFI,MAAuB,CAAC,EAErB,EAAQ,IAAI,EAAS,CAAU,CACxC,GACC,OAAO,SAAU,OACpB,CACF,CAEA,SAAgB,qBACd,EACA,EACmB,CACnB,IAAM,EAAQ,EAAA,YAAY,EAAO,CAAO,EAClC,EAAQ,aAAa,EAAO,CAAO,EAEzC,MAAO,CACL,MAAO,EAAM,MACb,IAAI,gBAAiB,CACnB,OAAO,EAAM,cACf,EACA,QAAS,EAAM,QACf,IAAI,UAAW,CACb,OAAO,EAAM,QACf,EACA,YAAa,EAAM,YACnB,QACA,SAAU,EAAM,SAChB,UAAW,EAAM,UACjB,IAAK,EAAM,KACV,OAAO,SAAU,EAAM,OAAO,QACjC,CACF"}
|
package/dist/reactive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../src/reactive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../src/reactive.ts"],"names":[],"mappings":"AAGA,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EACV,mBAAmB,EAEnB,iBAAiB,EAGjB,WAAW,EAEZ,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC;AAkCnF,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,mBAAwB,GAAG,WAAW,CAAC,CAAC,CAAC,CAiGvG;AAED,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,KAAK,EAAE,CAAC,EAAE,EACV,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,GACpE,cAAc,CAAC,CAAC,CAAC,CAoBnB"}
|
package/dist/reactive.js
CHANGED
|
@@ -1,115 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { createIndex as n } from "./scout-index.js";
|
|
3
|
-
//#region src/reactive.ts
|
|
4
|
-
var r = 200;
|
|
5
|
-
function i(e) {
|
|
6
|
-
for (let t of [...e]) try {
|
|
7
|
-
t();
|
|
8
|
-
} catch (e) {
|
|
9
|
-
queueMicrotask(() => {
|
|
10
|
-
throw e;
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
function a(e, t) {
|
|
15
|
-
if (e.size !== 0) for (let n of [...e]) try {
|
|
16
|
-
n(t);
|
|
17
|
-
} catch {}
|
|
18
|
-
}
|
|
19
|
-
function o(e, t, n, r, i) {
|
|
20
|
-
if (r || n?.signal?.aborted) return () => {};
|
|
21
|
-
e.add(t);
|
|
22
|
-
let a = () => {
|
|
23
|
-
e.delete(t), n?.signal?.removeEventListener("abort", a);
|
|
24
|
-
};
|
|
25
|
-
return n?.signal?.addEventListener("abort", a, {
|
|
26
|
-
once: !0,
|
|
27
|
-
signal: i
|
|
28
|
-
}), a;
|
|
29
|
-
}
|
|
30
|
-
function s(n, s = {}) {
|
|
31
|
-
let { debounce: c = r, limit: l, minQueryLength: u, threshold: d } = s;
|
|
32
|
-
if (!Number.isSafeInteger(c) || c < 0) throw new e("debounce must be a finite non-negative integer.");
|
|
33
|
-
let f = (e) => n.search(e, {
|
|
34
|
-
limit: l,
|
|
35
|
-
minQueryLength: u,
|
|
36
|
-
threshold: d
|
|
37
|
-
}), p = (e, t, n) => Object.freeze({
|
|
38
|
-
isSearching: t,
|
|
39
|
-
query: e,
|
|
40
|
-
results: Object.freeze(n)
|
|
41
|
-
}), m = /* @__PURE__ */ new Set(), h = /* @__PURE__ */ new Set(), g = new AbortController(), _ = p("", !1, f("")), v = "", y = !1, b, x = (e) => {
|
|
42
|
-
_ = e, i(m), a(h, {
|
|
43
|
-
snapshot: e,
|
|
44
|
-
type: "state-change"
|
|
45
|
-
});
|
|
46
|
-
}, S = (e) => {
|
|
47
|
-
v = e, x(p(e, !1, f(e)));
|
|
48
|
-
}, C = n.onMutate(() => {
|
|
49
|
-
x(p(_.query, _.isSearching, f(v)));
|
|
50
|
-
}), w = (e) => {
|
|
51
|
-
if (y) throw new t(`SearchState.${e}() called after dispose()`);
|
|
52
|
-
}, T = () => {
|
|
53
|
-
y || (y = !0, b !== void 0 && clearTimeout(b), b = void 0, C(), a(h, { type: "dispose" }), m.clear(), h.clear(), g.abort());
|
|
54
|
-
};
|
|
55
|
-
return {
|
|
56
|
-
clear() {
|
|
57
|
-
w("clear"), (_.query !== "" || _.isSearching) && (b !== void 0 && clearTimeout(b), b = void 0, S(""));
|
|
58
|
-
},
|
|
59
|
-
get disposalSignal() {
|
|
60
|
-
return g.signal;
|
|
61
|
-
},
|
|
62
|
-
dispose: T,
|
|
63
|
-
get disposed() {
|
|
64
|
-
return y;
|
|
65
|
-
},
|
|
66
|
-
getSnapshot() {
|
|
67
|
-
return _;
|
|
68
|
-
},
|
|
69
|
-
setQuery(e) {
|
|
70
|
-
if (w("setQuery"), e !== _.query) {
|
|
71
|
-
if (b !== void 0 && clearTimeout(b), b = void 0, e === v) {
|
|
72
|
-
x(p(e, !1, _.results));
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
if (c === 0) {
|
|
76
|
-
S(e);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
x(p(e, !0, _.results)), b = setTimeout(() => {
|
|
80
|
-
b = void 0, S(e);
|
|
81
|
-
}, c);
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
subscribe(e, t) {
|
|
85
|
-
return o(m, e, t, y, g.signal);
|
|
86
|
-
},
|
|
87
|
-
tap(e, t) {
|
|
88
|
-
return o(h, e, t, y, g.signal);
|
|
89
|
-
},
|
|
90
|
-
[Symbol.dispose]: T
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
function c(e, t) {
|
|
94
|
-
let r = n(e, t), i = s(r, t);
|
|
95
|
-
return {
|
|
96
|
-
clear: i.clear,
|
|
97
|
-
get disposalSignal() {
|
|
98
|
-
return i.disposalSignal;
|
|
99
|
-
},
|
|
100
|
-
dispose: i.dispose,
|
|
101
|
-
get disposed() {
|
|
102
|
-
return i.disposed;
|
|
103
|
-
},
|
|
104
|
-
getSnapshot: i.getSnapshot,
|
|
105
|
-
index: r,
|
|
106
|
-
setQuery: i.setQuery,
|
|
107
|
-
subscribe: i.subscribe,
|
|
108
|
-
tap: i.tap,
|
|
109
|
-
[Symbol.dispose]: i[Symbol.dispose]
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
//#endregion
|
|
113
|
-
export { c as createReactiveSearch, s as createSearch };
|
|
114
|
-
|
|
1
|
+
import{ScoutConfigError as e,ScoutDisposedError as t}from"./errors.js";import{createIndex as n}from"./scout-index.js";import{tapper as r}from"@vielzeug/arsenal";var i=200;function notify(e){for(let t of[...e])try{t()}catch(e){queueMicrotask(()=>{throw e})}}function subscribe(e,t,n,r,i){if(r||n?.signal?.aborted)return()=>{};e.add(t);let unsubscribe=()=>{e.delete(t),n?.signal?.removeEventListener(`abort`,unsubscribe)};return n?.signal?.addEventListener(`abort`,unsubscribe,{once:!0,signal:i}),unsubscribe}function createSearch(n,a={}){let{debounce:o=i,limit:s,minQueryLength:c,threshold:l}=a;if(!Number.isSafeInteger(o)||o<0)throw new e(`debounce must be a finite non-negative integer.`);let search=e=>n.search(e,{limit:s,minQueryLength:c,threshold:l}),createSnapshot=(e,t,n)=>Object.freeze({isSearching:t,query:e,results:Object.freeze(n)}),u=new Set,d=r(),f=new AbortController,p=createSnapshot(``,!1,search(``)),m=``,h=!1,g,publish=e=>{p=e,notify(u),d.emit({snapshot:e,type:`state-change`})},commit=e=>{m=e,publish(createSnapshot(e,!1,search(e)))},_=n.onMutate(()=>{publish(createSnapshot(p.query,p.isSearching,search(m)))}),assertActive=e=>{if(h)throw new t(`SearchState.${e}() called after dispose()`)},dispose=()=>{h||(h=!0,g!==void 0&&clearTimeout(g),g=void 0,_(),u.clear(),d.clear(),f.abort())};return{clear(){assertActive(`clear`),(p.query!==``||p.isSearching)&&(g!==void 0&&clearTimeout(g),g=void 0,commit(``))},get disposalSignal(){return f.signal},dispose,get disposed(){return h},getSnapshot(){return p},setQuery(e){if(assertActive(`setQuery`),e!==p.query){if(g!==void 0&&clearTimeout(g),g=void 0,e===m){publish(createSnapshot(e,!1,p.results));return}if(o===0){commit(e);return}publish(createSnapshot(e,!0,p.results)),g=setTimeout(()=>{g=void 0,commit(e)},o)}},subscribe(e,t){return subscribe(u,e,t,h,f.signal)},tap(e,t){return h?()=>{}:d.tap(e,t)},[Symbol.dispose]:dispose}}function createReactiveSearch(e,t){let r=n(e,t),i=createSearch(r,t);return{clear:i.clear,get disposalSignal(){return i.disposalSignal},dispose:i.dispose,get disposed(){return i.disposed},getSnapshot:i.getSnapshot,index:r,setQuery:i.setQuery,subscribe:i.subscribe,tap:i.tap,[Symbol.dispose]:i[Symbol.dispose]}}export{createReactiveSearch,createSearch};
|
|
115
2
|
//# sourceMappingURL=reactive.js.map
|