@vielzeug/scout 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -4
- package/dist/_index-state.cjs +2 -0
- package/dist/_index-state.cjs.map +1 -0
- package/dist/_index-state.d.ts +4 -0
- package/dist/_index-state.d.ts.map +1 -0
- package/dist/_index-state.js +12 -0
- package/dist/_index-state.js.map +1 -0
- package/dist/adapters.cjs +1 -1
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +9 -5
- package/dist/adapters.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -1
- 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 +1 -1
- package/dist/highlight.d.ts.map +1 -1
- package/dist/highlight.js +18 -17
- package/dist/highlight.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/reactive.cjs +1 -1
- package/dist/reactive.cjs.map +1 -1
- package/dist/reactive.d.ts +2 -2
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +47 -45
- 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 +11 -4
- package/dist/scout-index.d.ts.map +1 -1
- package/dist/scout-index.js +104 -75
- 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/types.d.ts +10 -8
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -4,12 +4,12 @@ Fast fuzzy-search. Builds a trigram inverted index at construction — O(candida
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Trigram index** — fast candidate lookup;
|
|
7
|
+
- **Trigram index** — fast candidate lookup; overlap-coefficient scoring
|
|
8
8
|
- **Multi-field weighted ranking** — per-field weights, custom stringifiers
|
|
9
9
|
- **Match highlighting** — character-range offsets for UI rendering
|
|
10
10
|
- **Reactive layer** — `createSearch()` wraps any index in `ripple` signals with debounce
|
|
11
11
|
- **Framework adapters** — `toSearchMatcher()` for sourcerer, `toFilterPredicate()` for filter pipelines
|
|
12
|
-
- **
|
|
12
|
+
- **Corpus reconciliation** — `setItems()` reconciles reference-based additions, removals, reindexes, and order in one notification
|
|
13
13
|
- **Unsegmented-script helper** — `segmentWords()` pre-splits CJK/Thai text into words via `Intl.Segmenter`
|
|
14
14
|
- **Devtools** — `@vielzeug/scout/devtools`'s `debugSearch()` logs query/results transitions
|
|
15
15
|
|
|
@@ -24,6 +24,11 @@ pnpm add @vielzeug/scout
|
|
|
24
24
|
```ts
|
|
25
25
|
import { createIndex } from '@vielzeug/scout';
|
|
26
26
|
|
|
27
|
+
const users = [
|
|
28
|
+
{ email: 'ada@example.com', name: 'Ada Lovelace' },
|
|
29
|
+
{ email: 'grace@example.com', name: 'Grace Hopper' },
|
|
30
|
+
];
|
|
31
|
+
|
|
27
32
|
const index = createIndex(users, {
|
|
28
33
|
fields: [
|
|
29
34
|
{ field: 'name', weight: 2 },
|
|
@@ -31,8 +36,7 @@ const index = createIndex(users, {
|
|
|
31
36
|
],
|
|
32
37
|
});
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
// [{ item: { name: 'Alice', email: '...' }, score: 0.85, matches: [...] }]
|
|
39
|
+
console.log(index.search('ada')[0]?.item.name); // Ada Lovelace
|
|
36
40
|
```
|
|
37
41
|
|
|
38
42
|
## Reactive search
|
|
@@ -41,6 +45,7 @@ const results = index.search('alice');
|
|
|
41
45
|
import { createIndex, createSearch } from '@vielzeug/scout';
|
|
42
46
|
import { effect } from '@vielzeug/ripple';
|
|
43
47
|
|
|
48
|
+
const users = [{ name: 'Ada Lovelace' }, { name: 'Grace Hopper' }];
|
|
44
49
|
const index = createIndex(users, { fields: ['name'] });
|
|
45
50
|
const search = createSearch(index, { debounce: 150 });
|
|
46
51
|
|
|
@@ -55,7 +60,9 @@ search.query.value = 'alice';
|
|
|
55
60
|
|
|
56
61
|
```ts
|
|
57
62
|
import { createIndex, toSearchMatcher } from '@vielzeug/scout';
|
|
63
|
+
import { createLocalSource } from '@vielzeug/sourcerer';
|
|
58
64
|
|
|
65
|
+
const users = [{ email: 'ada@example.com', name: 'Ada Lovelace' }];
|
|
59
66
|
const index = createIndex(users, { fields: ['name', 'email'] });
|
|
60
67
|
const source = createLocalSource(users, { match: toSearchMatcher(index) });
|
|
61
68
|
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_index-state.cjs","names":[],"sources":["../src/_index-state.ts"],"sourcesContent":["import type { ScoutIndex } from './scout-index';\n\nconst revisions = new WeakMap<object, () => number>();\n\nexport function getIndexRevision<T>(index: ScoutIndex<T>): number {\n return revisions.get(index)?.() ?? 0;\n}\n\nexport function registerIndexRevision<T>(index: ScoutIndex<T>, getRevision: () => number): void {\n revisions.set(index, getRevision);\n}\n"],"mappings":"AAEA,IAAM,EAAY,IAAI,QAEtB,SAAgB,EAAoB,EAA8B,CAChE,OAAO,EAAU,IAAI,CAAK,CAAC,GAAG,GAAK,CACrC,CAEA,SAAgB,EAAyB,EAAsB,EAAiC,CAC9F,EAAU,IAAI,EAAO,CAAW,CAClC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ScoutIndex } from './scout-index';
|
|
2
|
+
export declare function getIndexRevision<T>(index: ScoutIndex<T>): number;
|
|
3
|
+
export declare function registerIndexRevision<T>(index: ScoutIndex<T>, getRevision: () => number): void;
|
|
4
|
+
//# sourceMappingURL=_index-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_index-state.d.ts","sourceRoot":"","sources":["../src/_index-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAIhD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,CAEhE;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,MAAM,GAAG,IAAI,CAE9F"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/_index-state.ts
|
|
2
|
+
var e = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
function t(t) {
|
|
4
|
+
return e.get(t)?.() ?? 0;
|
|
5
|
+
}
|
|
6
|
+
function n(t, n) {
|
|
7
|
+
e.set(t, n);
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { t as getIndexRevision, n as registerIndexRevision };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=_index-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_index-state.js","names":[],"sources":["../src/_index-state.ts"],"sourcesContent":["import type { ScoutIndex } from './scout-index';\n\nconst revisions = new WeakMap<object, () => number>();\n\nexport function getIndexRevision<T>(index: ScoutIndex<T>): number {\n return revisions.get(index)?.() ?? 0;\n}\n\nexport function registerIndexRevision<T>(index: ScoutIndex<T>, getRevision: () => number): void {\n revisions.set(index, getRevision);\n}\n"],"mappings":";AAEA,IAAM,oBAAY,IAAI,QAA8B;AAEpD,SAAgB,EAAoB,GAA8B;CAChE,OAAO,EAAU,IAAI,CAAK,CAAC,GAAG,KAAK;AACrC;AAEA,SAAgB,EAAyB,GAAsB,GAAiC;CAC9F,EAAU,IAAI,GAAO,CAAW;AAClC"}
|
package/dist/adapters.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
const e=require("./_index-state.cjs");function t(t,n){let r,i=-1,a=new Set;return(o,s)=>{let c=e.getIndexRevision(t);return(s!==r||c!==i)&&(r=s,i=c,a=new Set(t.search(s,n).map(e=>e.item))),a.has(o)}}function n(e,t,n){let r=new Set(e.search(t,n).map(e=>e.item));return e=>r.has(e)}exports.toFilterPredicate=n,exports.toSearchMatcher=t;
|
|
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 explicit local `match` callback.\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 matches = new Set<T>();\n\n return (item, query) => {\n if (query !== lastQuery) {\n lastQuery = query;\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":"
|
|
1
|
+
{"version":3,"file":"adapters.cjs","names":[],"sources":["../src/adapters.ts"],"sourcesContent":["import type { ScoutIndex } from './scout-index';\nimport type { SearchConstraints } from './types';\n\nimport { getIndexRevision } from './_index-state';\n\n/**\n * Adapts a `ScoutIndex` to sourcerer's explicit local `match` callback.\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 const revision = getIndexRevision(index);\n\n if (query !== lastQuery || revision !== lastRevision) {\n lastQuery = query;\n lastRevision = 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":"sCASA,SAAgB,EACd,EACA,EACqC,CACrC,IAAI,EACA,EAAe,GACf,EAAU,IAAI,IAElB,OAAQ,EAAM,IAAU,CACtB,IAAM,EAAW,EAAA,iBAAiB,CAAK,EAQvC,OANI,IAAU,GAAa,IAAa,KACtC,EAAY,EACZ,EAAe,EACf,EAAU,IAAI,IAAI,EAAM,OAAO,EAAO,CAAO,CAAC,CAAC,IAAK,GAAW,EAAO,IAAI,CAAC,GAGtE,EAAQ,IAAI,CAAI,CACzB,CACF,CAUA,SAAgB,EACd,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.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;
|
|
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;AAIjD;;;GAGG;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,CAgBrC;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,17 @@
|
|
|
1
|
+
import { getIndexRevision as e } from "./_index-state.js";
|
|
1
2
|
//#region src/adapters.ts
|
|
2
|
-
function
|
|
3
|
-
let
|
|
4
|
-
return (
|
|
3
|
+
function t(t, n) {
|
|
4
|
+
let r, i = -1, a = /* @__PURE__ */ new Set();
|
|
5
|
+
return (o, s) => {
|
|
6
|
+
let c = e(t);
|
|
7
|
+
return (s !== r || c !== i) && (r = s, i = c, a = new Set(t.search(s, n).map((e) => e.item))), a.has(o);
|
|
8
|
+
};
|
|
5
9
|
}
|
|
6
|
-
function
|
|
10
|
+
function n(e, t, n) {
|
|
7
11
|
let r = new Set(e.search(t, n).map((e) => e.item));
|
|
8
12
|
return (e) => r.has(e);
|
|
9
13
|
}
|
|
10
14
|
//#endregion
|
|
11
|
-
export {
|
|
15
|
+
export { n as toFilterPredicate, t as toSearchMatcher };
|
|
12
16
|
|
|
13
17
|
//# 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 explicit local `match` callback.\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 matches = new Set<T>();\n\n return (item, query) => {\n if (query !== lastQuery) {\n lastQuery = query;\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":"
|
|
1
|
+
{"version":3,"file":"adapters.js","names":[],"sources":["../src/adapters.ts"],"sourcesContent":["import type { ScoutIndex } from './scout-index';\nimport type { SearchConstraints } from './types';\n\nimport { getIndexRevision } from './_index-state';\n\n/**\n * Adapts a `ScoutIndex` to sourcerer's explicit local `match` callback.\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 const revision = getIndexRevision(index);\n\n if (query !== lastQuery || revision !== lastRevision) {\n lastQuery = query;\n lastRevision = 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":";;AASA,SAAgB,EACd,GACA,GACqC;CACrC,IAAI,GACA,IAAe,IACf,oBAAU,IAAI,IAAO;CAEzB,QAAQ,GAAM,MAAU;EACtB,IAAM,IAAW,EAAiB,CAAK;EAQvC,QANI,MAAU,KAAa,MAAa,OACtC,IAAY,GACZ,IAAe,GACf,IAAU,IAAI,IAAI,EAAM,OAAO,GAAO,CAAO,CAAC,CAAC,KAAK,MAAW,EAAO,IAAI,CAAC,IAGtE,EAAQ,IAAI,CAAI;CACzB;AACF;AAUA,SAAgB,EACd,GACA,GACA,GACsB;CACtB,IAAM,IAAW,IAAI,IAAI,EAAM,OAAO,GAAO,CAAO,CAAC,CAAC,KAAK,MAAW,EAAO,IAAI,CAAC;CAElF,QAAQ,MAAS,EAAS,IAAI,CAAI;AACpC"}
|
package/dist/errors.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{},n=class extends e{};exports.ScoutDisposedError=t,exports.ScoutError=e
|
|
1
|
+
var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{},n=class extends e{};exports.ScoutConfigurationError=n,exports.ScoutDisposedError=t,exports.ScoutError=e;
|
|
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 static is(err: unknown): err is ScoutError {\n return err instanceof ScoutError;\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
|
|
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 static is(err: unknown): err is ScoutError {\n return err instanceof ScoutError;\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 ScoutConfigurationError extends ScoutError {}\n"],"mappings":"AACA,IAAa,EAAb,MAAa,UAAmB,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CAEA,OAAO,GAAG,EAAiC,CACzC,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAAwC,CAAW,CAAC,EAGvC,EAAb,cAA6C,CAAW,CAAC"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare class ScoutError extends Error {
|
|
|
6
6
|
/** Thrown when a method is called on a disposed search state instance. */
|
|
7
7
|
export declare class ScoutDisposedError extends ScoutError {
|
|
8
8
|
}
|
|
9
|
-
/** Thrown when an index
|
|
10
|
-
export declare class
|
|
9
|
+
/** Thrown when an index, search, or reactive search receives an invalid configuration. */
|
|
10
|
+
export declare class ScoutConfigurationError extends ScoutError {
|
|
11
11
|
}
|
|
12
12
|
//# 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;IAMhD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU;CAG3C;AAED,0EAA0E;AAC1E,qBAAa,kBAAmB,SAAQ,UAAU;CAAG;AAErD,
|
|
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;IAMhD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU;CAG3C;AAED,0EAA0E;AAC1E,qBAAa,kBAAmB,SAAQ,UAAU;CAAG;AAErD,0FAA0F;AAC1F,qBAAa,uBAAwB,SAAQ,UAAU;CAAG"}
|
package/dist/errors.js
CHANGED
|
@@ -8,6 +8,6 @@ var e = class e extends Error {
|
|
|
8
8
|
}
|
|
9
9
|
}, t = class extends e {}, n = class extends e {};
|
|
10
10
|
//#endregion
|
|
11
|
-
export {
|
|
11
|
+
export { n as ScoutConfigurationError, t as ScoutDisposedError, e as ScoutError };
|
|
12
12
|
|
|
13
13
|
//# 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 static is(err: unknown): err is ScoutError {\n return err instanceof ScoutError;\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
|
|
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 static is(err: unknown): err is ScoutError {\n return err instanceof ScoutError;\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 ScoutConfigurationError extends ScoutError {}\n"],"mappings":";AACA,IAAa,IAAb,MAAa,UAAmB,MAAM;CACpC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;CAEA,OAAO,GAAG,GAAiC;EACzC,OAAO,aAAe;CACxB;AACF,GAGa,IAAb,cAAwC,EAAW,CAAC,GAGvC,IAAb,cAA6C,EAAW,CAAC"}
|
package/dist/highlight.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
const e=require("./tokenize.cjs");function t(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 n(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 r(e,t,r){return n(r,e.matches.find(e=>e.field===t)?.ranges??[])}exports.findMatchRanges=t,exports.highlight=n,exports.highlightField=r;
|
|
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 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 -
|
|
1
|
+
{"version":3,"file":"highlight.cjs","names":[],"sources":["../src/highlight.ts"],"sourcesContent":["import type { HighlightPart, SearchResult } from './types';\n\nimport { tokenize } from './tokenize';\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":"kCAeA,SAAgB,EAAgB,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,EAAU,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,EAAU,EAFH,EAAO,QAAQ,KAAM,GAAM,EAAE,QAAU,CAE9B,CAAA,EAAO,QAAU,CAAC,CAAC,CAC5C"}
|
package/dist/highlight.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { HighlightPart, SearchResult } from './types';
|
|
|
7
7
|
* indexed field value (e.g. a truncated preview or a differently formatted display string).
|
|
8
8
|
*
|
|
9
9
|
* @param text - The string to search within.
|
|
10
|
-
* @param query -
|
|
10
|
+
* @param query - Raw query text. It is normalized with Scout's tokenizer before literal lookup.
|
|
11
11
|
* @returns Sorted, non-overlapping `[start, end]` character ranges.
|
|
12
12
|
*/
|
|
13
13
|
export declare function findMatchRanges(text: string, query: string): [number, number][];
|
package/dist/highlight.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"highlight.d.ts","sourceRoot":"","sources":["../src/highlight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"highlight.d.ts","sourceRoot":"","sources":["../src/highlight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI3D;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAiC/E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,aAAa,EAAE,CAyBnF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAIjH"}
|
package/dist/highlight.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
|
+
import { tokenize as e } from "./tokenize.js";
|
|
1
2
|
//#region src/highlight.ts
|
|
2
|
-
function
|
|
3
|
-
let
|
|
4
|
-
for (let e of
|
|
3
|
+
function t(t, n) {
|
|
4
|
+
let r = t.toLowerCase(), i = e(n).split(" ").filter(Boolean), a = [];
|
|
5
|
+
for (let e of i) {
|
|
5
6
|
let t = 0;
|
|
6
|
-
for (; t <
|
|
7
|
-
let
|
|
8
|
-
if (
|
|
9
|
-
|
|
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;
|
|
10
11
|
}
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
-
let
|
|
14
|
-
for (let e of
|
|
15
|
-
let t =
|
|
16
|
-
t && e[0] <= t[1] ? t[1] = Math.max(t[1], e[1]) :
|
|
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]]);
|
|
17
18
|
}
|
|
18
|
-
return
|
|
19
|
+
return o;
|
|
19
20
|
}
|
|
20
|
-
function
|
|
21
|
+
function n(e, t) {
|
|
21
22
|
if (!e) return [];
|
|
22
23
|
if (!t.length) return [{
|
|
23
24
|
highlighted: !1,
|
|
@@ -36,10 +37,10 @@ function t(e, t) {
|
|
|
36
37
|
text: e.slice(r)
|
|
37
38
|
}), n;
|
|
38
39
|
}
|
|
39
|
-
function
|
|
40
|
-
return
|
|
40
|
+
function r(e, t, r) {
|
|
41
|
+
return n(r, e.matches.find((e) => e.field === t)?.ranges ?? []);
|
|
41
42
|
}
|
|
42
43
|
//#endregion
|
|
43
|
-
export {
|
|
44
|
+
export { t as findMatchRanges, n as highlight, r as highlightField };
|
|
44
45
|
|
|
45
46
|
//# 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 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 -
|
|
1
|
+
{"version":3,"file":"highlight.js","names":[],"sources":["../src/highlight.ts"],"sourcesContent":["import type { HighlightPart, SearchResult } from './types';\n\nimport { tokenize } from './tokenize';\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":";;AAeA,SAAgB,EAAgB,GAAc,GAAmC;CAC/E,IAAM,IAAQ,EAAK,YAAY,GACzB,IAAQ,EAAS,CAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,GACjD,IAA6B,CAAC;CAEpC,KAAK,IAAM,KAAQ,GAAO;EACxB,IAAI,IAAM;EAEV,OAAO,IAAM,EAAM,SAAQ;GACzB,IAAM,IAAM,EAAM,QAAQ,GAAM,CAAG;GAEnC,IAAI,MAAQ,IAAI;GAGhB,AADA,EAAO,KAAK,CAAC,GAAK,IAAM,EAAK,MAAM,CAAC,GACpC,IAAM,IAAM;EACd;CACF;CAEA,EAAO,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE;CAEjC,IAAM,IAA6B,CAAC;CAEpC,KAAK,IAAM,KAAS,GAAQ;EAC1B,IAAM,IAAO,EAAO,EAAO,SAAS;EAEpC,AAAI,KAAQ,EAAM,MAAM,EAAK,KAC3B,EAAK,KAAK,KAAK,IAAI,EAAK,IAAI,EAAM,EAAE,IAEpC,EAAO,KAAK,CAAC,EAAM,IAAI,EAAM,EAAE,CAAC;CAEpC;CAEA,OAAO;AACT;AA+BA,SAAgB,EAAU,GAAc,GAA6C;CACnF,IAAI,CAAC,GAAM,OAAO,CAAC;CAEnB,IAAI,CAAC,EAAO,QAAQ,OAAO,CAAC;EAAE,aAAa;EAAO;CAAK,CAAC;CAExD,IAAM,IAAyB,CAAC,GAC5B,IAAS;CAEb,KAAK,IAAM,CAAC,GAAO,MAAQ,GASzB,AARI,IAAQ,KACV,EAAM,KAAK;EAAE,aAAa;EAAO,MAAM,EAAK,MAAM,GAAQ,CAAK;CAAE,CAAC,GAGhE,IAAM,KACR,EAAM,KAAK;EAAE,aAAa;EAAM,MAAM,EAAK,MAAM,GAAO,CAAG;CAAE,CAAC,GAGhE,IAAS;CAOX,OAJI,IAAS,EAAK,UAChB,EAAM,KAAK;EAAE,aAAa;EAAO,MAAM,EAAK,MAAM,CAAM;CAAE,CAAC,GAGtD;AACT;AAyBA,SAAgB,EAAkB,GAAyB,GAAyB,GAA+B;CAGjH,OAAO,EAAU,GAFH,EAAO,QAAQ,MAAM,MAAM,EAAE,UAAU,CAE9B,CAAA,EAAO,UAAU,CAAC,CAAC;AAC5C"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters.cjs"),t=require("./errors.cjs"),n=require("./highlight.cjs"),r=require("./scout-index.cjs"),i=require("./reactive.cjs"),a=require("./segment.cjs");exports.
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters.cjs"),t=require("./errors.cjs"),n=require("./highlight.cjs"),r=require("./scout-index.cjs"),i=require("./reactive.cjs"),a=require("./segment.cjs");exports.ScoutConfigurationError=t.ScoutConfigurationError,exports.ScoutDisposedError=t.ScoutDisposedError,exports.ScoutError=t.ScoutError,exports.createIndex=r.createIndex,exports.createReactiveSearch=i.createReactiveSearch,exports.createSearch=i.createSearch,exports.findMatchRanges=n.findMatchRanges,exports.highlight=n.highlight,exports.highlightField=n.highlightField,exports.segmentWords=a.segmentWords,exports.toFilterPredicate=e.toFilterPredicate,exports.toSearchMatcher=e.toSearchMatcher;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { toFilterPredicate, toSearchMatcher } from './adapters';
|
|
2
|
-
export { ScoutDisposedError, ScoutError
|
|
2
|
+
export { ScoutConfigurationError, ScoutDisposedError, ScoutError } from './errors';
|
|
3
3
|
export { findMatchRanges, highlight, highlightField } from './highlight';
|
|
4
4
|
export { createReactiveSearch, createSearch } from './reactive';
|
|
5
5
|
export type { ReactiveSearch } 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,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAChE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,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,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,WAAW,GACZ,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { toFilterPredicate as e, toSearchMatcher as t } from "./adapters.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ScoutConfigurationError as n, ScoutDisposedError as r, ScoutError as i } from "./errors.js";
|
|
3
3
|
import { findMatchRanges as a, highlight as o, highlightField as s } from "./highlight.js";
|
|
4
4
|
import { createIndex as c } from "./scout-index.js";
|
|
5
5
|
import { createReactiveSearch as l, createSearch as u } from "./reactive.js";
|
|
6
6
|
import { segmentWords as d } from "./segment.js";
|
|
7
|
-
export { n as
|
|
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 };
|
package/dist/reactive.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs"),t=require("./scout-index.cjs");let n=require("@vielzeug/ripple");var r=200;function i(t,i={}){let{debounce:a=r,limit:o,minQueryLength:s,threshold:c}=i
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./scout-index.cjs");let n=require("@vielzeug/ripple");var r=200;function i(t,i={}){let{debounce:a=r,limit:o,minQueryLength:s,threshold:c}=i;if(!Number.isFinite(a)||!Number.isInteger(a)||a<0)throw new e.ScoutConfigurationError(`debounce must be a finite non-negative integer.`);let l=(0,n.signal)(``,{name:`scout:query`}),u=(0,n.signal)(``,{name:`scout:committedQuery`}),d=(0,n.signal)(0,{name:`scout:indexVersion`}),f=t.onMutate(()=>{d.value++}),p=(0,n.computed)(()=>l.value!==u.value,{name:`scout:isSearching`}),m=(0,n.computed)(()=>(d.value,t.search(u.value,{limit:o,minQueryLength:s,threshold:c})),{name:`scout:results`}),h=null;function g(){h!==null&&(clearTimeout(h),h=null)}let _=l.subscribe(()=>{let e=l.peek();if(g(),e!==u.peek()){if(a===0){u.value=e;return}h=setTimeout(()=>{u.value=e,h=null},a)}});function v(){if(y)throw new e.ScoutDisposedError(`SearchState.clear() called after dispose()`);g(),(0,n.batch)(()=>{l.value=``,u.value=``})}let y=!1,b=new AbortController;function x(){y=!0,b.abort(),g(),_(),f()}return{clear:v,get disposalSignal(){return b.signal},dispose:x,get disposed(){return y},isSearching:p,query:l,results:m,[Symbol.dispose](){x()}}}function a(e,n){let r=t.createIndex(e,{fields:n.fields,limit:n.limit,minQueryLength:n.minQueryLength,threshold:n.threshold}),a=i(r,{debounce:n.debounce});return Object.assign(Object.create(a),{index:r})}exports.createReactiveSearch=a,exports.createSearch=i;
|
|
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 { batch, computed, signal } from '@vielzeug/ripple';\n\nimport type { CreateSearchOptions, ScoutIndexOptions, SearchResult, SearchState } from './types';\n\nimport { ScoutDisposedError } from './errors';\nimport { createIndex, type ScoutIndex } from './scout-index';\n\n/**\n * Combined index + reactive search state returned by `createReactiveSearch()`.\n * Exposes the underlying `ScoutIndex` for incremental mutations (`add`, `remove`, `reindex`).\n */\nexport type ReactiveSearch<T> = SearchState<T> & {\n readonly index: ScoutIndex<T>;\n};\n\nconst DEFAULT_DEBOUNCE = 200;\n\n/**\n * Creates a reactive search state backed by a `ScoutIndex`.\n *\n * - Set `state.query.value` to trigger a (debounced) search.\n * - Read `state.results.value` inside an `effect` or `computed` to consume results reactively.\n * - `state.isSearching.value` is `true` while debouncing, `false` otherwise.\n * - Call `state.dispose()` (or `using state = createSearch(...)`) to release subscriptions.\n *\n * @example\n * ```ts\n * const index = createIndex(users, { fields: ['name', 'email'] });\n * const search = createSearch(index, { debounce: 150 });\n *\n * effect(() => {\n * const results = search.results.value;\n * renderList(results.map(r => r.item));\n * });\n *\n * // Wire to an input\n * input.addEventListener('input', e => {\n * search.query.value = e.currentTarget.value;\n * });\n *\n * // Clean up\n * search.dispose();\n * ```\n *\n * `results` also updates when the index is mutated directly via `index.add()` / `.remove()`\n * / `.reindex()` (not just when `query` changes), by subscribing to `index.onMutate()`.\n *\n * @param index - A `ScoutIndex` built with `createIndex()`.\n * @param options.debounce - Milliseconds to wait before committing query changes. Default: `200`.\n * @param options.limit - Override the index-level result limit.\n * @param options.minQueryLength - Override the index-level minimum query length.\n * @param options.threshold - Override the index-level score threshold.\n */\nexport function createSearch<T>(index: ScoutIndex<T>, options: CreateSearchOptions = {}): SearchState<T> {\n const { debounce: debounceMs = DEFAULT_DEBOUNCE, limit, minQueryLength, threshold } = options;\n\n const query = signal<string>('', { name: 'scout:query' });\n const committedQuery = signal<string>('', { name: 'scout:committedQuery' });\n const indexVersion = signal(0, { name: 'scout:indexVersion' });\n\n const unsubscribeMutations = index.onMutate(() => {\n indexVersion.value++;\n });\n\n const isSearching = computed(() => query.value !== committedQuery.value, { name: 'scout:isSearching' });\n\n const results = computed<SearchResult<T>[]>(\n () => {\n // Reading .value establishes a dependency so index mutations trigger a recompute.\n void indexVersion.value;\n\n return index.search(committedQuery.value, { limit, minQueryLength, threshold });\n },\n { name: 'scout:results' },\n );\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function cancelTimer(): void {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n const subscription = query.subscribe(() => {\n const q = query.peek();\n\n cancelTimer();\n\n if (q === committedQuery.peek()) return;\n\n if (debounceMs === 0) {\n committedQuery.value = q;\n\n return;\n }\n\n timer = setTimeout(() => {\n committedQuery.value = q;\n timer = null;\n }, debounceMs);\n });\n\n function clear(): void {\n if (isDisposed) throw new ScoutDisposedError('SearchState.clear() called after dispose()');\n\n cancelTimer();\n\n batch(() => {\n query.value = '';\n committedQuery.value = '';\n });\n }\n\n let isDisposed = false;\n const ac = new AbortController();\n\n function dispose(): void {\n isDisposed = true;\n ac.abort();\n cancelTimer();\n subscription();\n unsubscribeMutations();\n }\n\n return {\n clear,\n get disposalSignal(): AbortSignal {\n return ac.signal;\n },\n dispose,\n get disposed(): boolean {\n return isDisposed;\n },\n isSearching,\n query,\n results,\n [Symbol.dispose](): void {\n dispose();\n },\n };\n}\n\n/**\n * Creates a `ScoutIndex` and a reactive search state in one call — the shorthand\n * for the common pattern of `createIndex` + `createSearch`.\n *\n * The returned `ReactiveSearch` exposes the underlying index via `.index` for\n * incremental mutations (`add`, `remove`, `reindex`) after construction.\n *\n * @example\n * ```ts\n * const search = createReactiveSearch(users, {\n * fields: [{ field: 'name', weight: 2 }, 'email'],\n * debounce: 150,\n * });\n *\n * effect(() => renderList(search.results.value.map(r => r.item)));\n *\n * // Wire to an input\n * input.addEventListener('input', e => { search.query.value = e.currentTarget.value; });\n *\n * // Add a new item at runtime\n * search.index.add(newUser);\n * ```\n *\n * @param items - Initial corpus to index.\n * @param options - Index options (`fields`, `limit`, `minQueryLength`, `threshold`) plus optional `debounce`.\n */\nexport function createReactiveSearch<T>(\n items: T[],\n options: ScoutIndexOptions<T> & Pick<CreateSearchOptions, 'debounce'>,\n): ReactiveSearch<T> {\n const index = createIndex(items, {\n fields: options.fields,\n limit: options.limit,\n minQueryLength: options.minQueryLength,\n threshold: options.threshold,\n });\n const state = createSearch(index, { debounce: options.debounce });\n\n return Object.assign(Object.create(state), { index }) as ReactiveSearch<T>;\n}\n"],"mappings":"iGAeA,IAAM,EAAmB,IAsCzB,SAAgB,EAAgB,EAAsB,EAA+B,CAAC,EAAmB,CACvG,GAAM,CAAE,SAAU,EAAa,EAAkB,QAAO,iBAAgB,aAAc,
|
|
1
|
+
{"version":3,"file":"reactive.cjs","names":[],"sources":["../src/reactive.ts"],"sourcesContent":["import { batch, computed, signal } from '@vielzeug/ripple';\n\nimport type { CreateSearchOptions, ScoutIndexOptions, SearchResult, SearchState } from './types';\n\nimport { ScoutConfigurationError, ScoutDisposedError } from './errors';\nimport { createIndex, type ScoutIndex } from './scout-index';\n\n/**\n * Combined index + reactive search state returned by `createReactiveSearch()`.\n * Exposes the underlying `ScoutIndex` for incremental mutations (`add`, `remove`, `reindex`).\n */\nexport type ReactiveSearch<T> = SearchState<T> & {\n readonly index: ScoutIndex<T>;\n};\n\nconst DEFAULT_DEBOUNCE = 200;\n\n/**\n * Creates a reactive search state backed by a `ScoutIndex`.\n *\n * - Set `state.query.value` to trigger a (debounced) search.\n * - Read `state.results.value` inside an `effect` or `computed` to consume results reactively.\n * - `state.isSearching.value` is `true` while debouncing, `false` otherwise.\n * - Call `state.dispose()` (or `using state = createSearch(...)`) to release subscriptions.\n *\n * @example\n * ```ts\n * const index = createIndex(users, { fields: ['name', 'email'] });\n * const search = createSearch(index, { debounce: 150 });\n *\n * effect(() => {\n * const results = search.results.value;\n * renderList(results.map(r => r.item));\n * });\n *\n * // Wire to an input\n * input.addEventListener('input', e => {\n * search.query.value = e.currentTarget.value;\n * });\n *\n * // Clean up\n * search.dispose();\n * ```\n *\n * `results` also updates when the index is mutated directly via `index.add()` / `.remove()`\n * / `.reindex()` / `.setItems()` (not just when `query` changes), by subscribing to `index.onMutate()`.\n *\n * @param index - A `ScoutIndex` built with `createIndex()`.\n * @param options.debounce - Milliseconds to wait before committing query changes. Default: `200`.\n * @param options.limit - Override the index-level result limit.\n * @param options.minQueryLength - Override the index-level minimum query length.\n * @param options.threshold - Override the index-level score threshold.\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.isFinite(debounceMs) || !Number.isInteger(debounceMs) || debounceMs < 0) {\n throw new ScoutConfigurationError('debounce must be a finite non-negative integer.');\n }\n\n const query = signal<string>('', { name: 'scout:query' });\n const committedQuery = signal<string>('', { name: 'scout:committedQuery' });\n const indexVersion = signal(0, { name: 'scout:indexVersion' });\n\n const unsubscribeMutations = index.onMutate(() => {\n indexVersion.value++;\n });\n\n const isSearching = computed(() => query.value !== committedQuery.value, { name: 'scout:isSearching' });\n\n const results = computed<SearchResult<T>[]>(\n () => {\n // Reading .value establishes a dependency so index mutations trigger a recompute.\n void indexVersion.value;\n\n return index.search(committedQuery.value, { limit, minQueryLength, threshold });\n },\n { name: 'scout:results' },\n );\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function cancelTimer(): void {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n const subscription = query.subscribe(() => {\n const q = query.peek();\n\n cancelTimer();\n\n if (q === committedQuery.peek()) return;\n\n if (debounceMs === 0) {\n committedQuery.value = q;\n\n return;\n }\n\n timer = setTimeout(() => {\n committedQuery.value = q;\n timer = null;\n }, debounceMs);\n });\n\n function clear(): void {\n if (isDisposed) throw new ScoutDisposedError('SearchState.clear() called after dispose()');\n\n cancelTimer();\n\n batch(() => {\n query.value = '';\n committedQuery.value = '';\n });\n }\n\n let isDisposed = false;\n const ac = new AbortController();\n\n function dispose(): void {\n isDisposed = true;\n ac.abort();\n cancelTimer();\n subscription();\n unsubscribeMutations();\n }\n\n return {\n clear,\n get disposalSignal(): AbortSignal {\n return ac.signal;\n },\n dispose,\n get disposed(): boolean {\n return isDisposed;\n },\n isSearching,\n query,\n results,\n [Symbol.dispose](): void {\n dispose();\n },\n };\n}\n\n/**\n * Creates a `ScoutIndex` and a reactive search state in one call — the shorthand\n * for the common pattern of `createIndex` + `createSearch`.\n *\n * The returned `ReactiveSearch` exposes the underlying index via `.index` for\n * incremental mutations (`add`, `remove`, `reindex`, `setItems`) after construction.\n *\n * @example\n * ```ts\n * const search = createReactiveSearch(users, {\n * fields: [{ field: 'name', weight: 2 }, 'email'],\n * debounce: 150,\n * });\n *\n * effect(() => renderList(search.results.value.map(r => r.item)));\n *\n * // Wire to an input\n * input.addEventListener('input', e => { search.query.value = e.currentTarget.value; });\n *\n * // Add a new item at runtime\n * search.index.add(newUser);\n * ```\n *\n * @param items - Initial corpus to index.\n * @param options - Index options (`fields`, `limit`, `minQueryLength`, `threshold`) plus optional `debounce`.\n */\nexport function createReactiveSearch<T>(\n items: T[],\n options: ScoutIndexOptions<T> & Pick<CreateSearchOptions, 'debounce'>,\n): ReactiveSearch<T> {\n const index = createIndex(items, {\n fields: options.fields,\n limit: options.limit,\n minQueryLength: options.minQueryLength,\n threshold: options.threshold,\n });\n const state = createSearch(index, { debounce: options.debounce });\n\n return Object.assign(Object.create(state), { index }) as ReactiveSearch<T>;\n}\n"],"mappings":"iGAeA,IAAM,EAAmB,IAsCzB,SAAgB,EAAgB,EAAsB,EAA+B,CAAC,EAAmB,CACvG,GAAM,CAAE,SAAU,EAAa,EAAkB,QAAO,iBAAgB,aAAc,EAEtF,GAAI,CAAC,OAAO,SAAS,CAAU,GAAK,CAAC,OAAO,UAAU,CAAU,GAAK,EAAa,EAChF,MAAM,IAAI,EAAA,wBAAwB,iDAAiD,EAGrF,IAAM,GAAA,EAAQ,EAAA,OAAA,CAAe,GAAI,CAAE,KAAM,aAAc,CAAC,EAClD,GAAA,EAAiB,EAAA,OAAA,CAAe,GAAI,CAAE,KAAM,sBAAuB,CAAC,EACpE,GAAA,EAAe,EAAA,OAAA,CAAO,EAAG,CAAE,KAAM,oBAAqB,CAAC,EAEvD,EAAuB,EAAM,aAAe,CAChD,EAAa,OACf,CAAC,EAEK,GAAA,EAAc,EAAA,SAAA,KAAe,EAAM,QAAU,EAAe,MAAO,CAAE,KAAM,mBAAoB,CAAC,EAEhG,GAAA,EAAU,EAAA,SAAA,MAGZ,EAAkB,MAEX,EAAM,OAAO,EAAe,MAAO,CAAE,QAAO,iBAAgB,WAAU,CAAC,GAEhF,CAAE,KAAM,eAAgB,CAC1B,EAEI,EAA8C,KAElD,SAAS,GAAoB,CACvB,IAAU,OACZ,aAAa,CAAK,EAClB,EAAQ,KAEZ,CAEA,IAAM,EAAe,EAAM,cAAgB,CACzC,IAAM,EAAI,EAAM,KAAK,EAErB,KAAY,EAER,IAAM,EAAe,KAAK,EAE9B,IAAI,IAAe,EAAG,CACpB,EAAe,MAAQ,EAEvB,MACF,CAEA,EAAQ,eAAiB,CACvB,EAAe,MAAQ,EACvB,EAAQ,IACV,EAAG,CAAU,CALb,CAMF,CAAC,EAED,SAAS,GAAc,CACrB,GAAI,EAAY,MAAM,IAAI,EAAA,mBAAmB,4CAA4C,EAEzF,EAAY,GAEZ,EAAA,EAAA,MAAA,KAAY,CACV,EAAM,MAAQ,GACd,EAAe,MAAQ,EACzB,CAAC,CACH,CAEA,IAAI,EAAa,GACX,EAAK,IAAI,gBAEf,SAAS,GAAgB,CACvB,EAAa,GACb,EAAG,MAAM,EACT,EAAY,EACZ,EAAa,EACb,EAAqB,CACvB,CAEA,MAAO,CACL,QACA,IAAI,gBAA8B,CAChC,OAAO,EAAG,MACZ,EACA,UACA,IAAI,UAAoB,CACtB,OAAO,CACT,EACA,cACA,QACA,UACA,CAAC,OAAO,UAAiB,CACvB,EAAQ,CACV,CACF,CACF,CA4BA,SAAgB,EACd,EACA,EACmB,CACnB,IAAM,EAAQ,EAAA,YAAY,EAAO,CAC/B,OAAQ,EAAQ,OAChB,MAAO,EAAQ,MACf,eAAgB,EAAQ,eACxB,UAAW,EAAQ,SACrB,CAAC,EACK,EAAQ,EAAa,EAAO,CAAE,SAAU,EAAQ,QAAS,CAAC,EAEhE,OAAO,OAAO,OAAO,OAAO,OAAO,CAAK,EAAG,CAAE,OAAM,CAAC,CACtD"}
|
package/dist/reactive.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export type ReactiveSearch<T> = SearchState<T> & {
|
|
|
35
35
|
* ```
|
|
36
36
|
*
|
|
37
37
|
* `results` also updates when the index is mutated directly via `index.add()` / `.remove()`
|
|
38
|
-
* / `.reindex()` (not just when `query` changes), by subscribing to `index.onMutate()`.
|
|
38
|
+
* / `.reindex()` / `.setItems()` (not just when `query` changes), by subscribing to `index.onMutate()`.
|
|
39
39
|
*
|
|
40
40
|
* @param index - A `ScoutIndex` built with `createIndex()`.
|
|
41
41
|
* @param options.debounce - Milliseconds to wait before committing query changes. Default: `200`.
|
|
@@ -49,7 +49,7 @@ export declare function createSearch<T>(index: ScoutIndex<T>, options?: CreateSe
|
|
|
49
49
|
* for the common pattern of `createIndex` + `createSearch`.
|
|
50
50
|
*
|
|
51
51
|
* The returned `ReactiveSearch` exposes the underlying index via `.index` for
|
|
52
|
-
* incremental mutations (`add`, `remove`, `reindex`) after construction.
|
|
52
|
+
* incremental mutations (`add`, `remove`, `reindex`, `setItems`) after construction.
|
|
53
53
|
*
|
|
54
54
|
* @example
|
|
55
55
|
* ```ts
|
package/dist/reactive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../src/reactive.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAgB,WAAW,EAAE,MAAM,SAAS,CAAC;AAGjG,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAE7D;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAC/C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CAC/B,CAAC;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,mBAAwB,GAAG,WAAW,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../src/reactive.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAgB,WAAW,EAAE,MAAM,SAAS,CAAC;AAGjG,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAE7D;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAC/C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CAC/B,CAAC;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,mBAAwB,GAAG,WAAW,CAAC,CAAC,CAAC,CA6FvG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,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,CAUnB"}
|
package/dist/reactive.js
CHANGED
|
@@ -1,68 +1,70 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createIndex as
|
|
3
|
-
import { batch as
|
|
1
|
+
import { ScoutConfigurationError as e, ScoutDisposedError as t } from "./errors.js";
|
|
2
|
+
import { createIndex as n } from "./scout-index.js";
|
|
3
|
+
import { batch as r, computed as i, signal as a } from "@vielzeug/ripple";
|
|
4
4
|
//#region src/reactive.ts
|
|
5
|
-
var
|
|
6
|
-
function
|
|
7
|
-
let { debounce:
|
|
8
|
-
|
|
9
|
-
}),
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
var o = 200;
|
|
6
|
+
function s(n, s = {}) {
|
|
7
|
+
let { debounce: c = o, limit: l, minQueryLength: u, threshold: d } = s;
|
|
8
|
+
if (!Number.isFinite(c) || !Number.isInteger(c) || c < 0) throw new e("debounce must be a finite non-negative integer.");
|
|
9
|
+
let f = a("", { name: "scout:query" }), p = a("", { name: "scout:committedQuery" }), m = a(0, { name: "scout:indexVersion" }), h = n.onMutate(() => {
|
|
10
|
+
m.value++;
|
|
11
|
+
}), g = i(() => f.value !== p.value, { name: "scout:isSearching" }), _ = i(() => (m.value, n.search(p.value, {
|
|
12
|
+
limit: l,
|
|
13
|
+
minQueryLength: u,
|
|
14
|
+
threshold: d
|
|
15
|
+
})), { name: "scout:results" }), v = null;
|
|
16
|
+
function y() {
|
|
17
|
+
v !== null && (clearTimeout(v), v = null);
|
|
16
18
|
}
|
|
17
|
-
let
|
|
18
|
-
let e =
|
|
19
|
-
if (
|
|
20
|
-
if (
|
|
21
|
-
|
|
19
|
+
let b = f.subscribe(() => {
|
|
20
|
+
let e = f.peek();
|
|
21
|
+
if (y(), e !== p.peek()) {
|
|
22
|
+
if (c === 0) {
|
|
23
|
+
p.value = e;
|
|
22
24
|
return;
|
|
23
25
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
26
|
+
v = setTimeout(() => {
|
|
27
|
+
p.value = e, v = null;
|
|
28
|
+
}, c);
|
|
27
29
|
}
|
|
28
30
|
});
|
|
29
|
-
function
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
function x() {
|
|
32
|
+
if (S) throw new t("SearchState.clear() called after dispose()");
|
|
33
|
+
y(), r(() => {
|
|
34
|
+
f.value = "", p.value = "";
|
|
33
35
|
});
|
|
34
36
|
}
|
|
35
|
-
let
|
|
36
|
-
function
|
|
37
|
-
|
|
37
|
+
let S = !1, C = new AbortController();
|
|
38
|
+
function w() {
|
|
39
|
+
S = !0, C.abort(), y(), b(), h();
|
|
38
40
|
}
|
|
39
41
|
return {
|
|
40
|
-
clear:
|
|
42
|
+
clear: x,
|
|
41
43
|
get disposalSignal() {
|
|
42
|
-
return
|
|
44
|
+
return C.signal;
|
|
43
45
|
},
|
|
44
|
-
dispose:
|
|
46
|
+
dispose: w,
|
|
45
47
|
get disposed() {
|
|
46
|
-
return
|
|
48
|
+
return S;
|
|
47
49
|
},
|
|
48
|
-
isSearching:
|
|
49
|
-
query:
|
|
50
|
-
results:
|
|
50
|
+
isSearching: g,
|
|
51
|
+
query: f,
|
|
52
|
+
results: _,
|
|
51
53
|
[Symbol.dispose]() {
|
|
52
|
-
|
|
54
|
+
w();
|
|
53
55
|
}
|
|
54
56
|
};
|
|
55
57
|
}
|
|
56
|
-
function
|
|
57
|
-
let r =
|
|
58
|
-
fields:
|
|
59
|
-
limit:
|
|
60
|
-
minQueryLength:
|
|
61
|
-
threshold:
|
|
62
|
-
}), i =
|
|
58
|
+
function c(e, t) {
|
|
59
|
+
let r = n(e, {
|
|
60
|
+
fields: t.fields,
|
|
61
|
+
limit: t.limit,
|
|
62
|
+
minQueryLength: t.minQueryLength,
|
|
63
|
+
threshold: t.threshold
|
|
64
|
+
}), i = s(r, { debounce: t.debounce });
|
|
63
65
|
return Object.assign(Object.create(i), { index: r });
|
|
64
66
|
}
|
|
65
67
|
//#endregion
|
|
66
|
-
export {
|
|
68
|
+
export { c as createReactiveSearch, s as createSearch };
|
|
67
69
|
|
|
68
70
|
//# sourceMappingURL=reactive.js.map
|