@vielzeug/scout 1.1.7 → 2.0.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 +3 -3
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js +2 -5
- package/dist/_dev.js.map +1 -1
- package/dist/adapters.cjs +1 -1
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.ts +3 -26
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +3 -2
- package/dist/adapters.js.map +1 -1
- package/dist/devtools.cjs +1 -1
- package/dist/devtools.cjs.map +1 -1
- package/dist/devtools.js +1 -1
- package/dist/devtools.js.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.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.map +1 -1
- package/dist/reactive.js +3 -6
- package/dist/reactive.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/tokenize.cjs +1 -1
- package/dist/tokenize.cjs.map +1 -1
- package/dist/tokenize.js +5 -5
- package/dist/tokenize.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Fast fuzzy-search. Builds a trigram inverted index at construction — O(candida
|
|
|
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
|
-
- **Framework adapters** — `
|
|
11
|
+
- **Framework adapters** — `toSearchMatcher()` for sourcerer, `toFilterPredicate()` for filter pipelines
|
|
12
12
|
- **Incremental updates** — `add()`, `remove()`, `reindex()` patch the index in O(field_length)
|
|
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
|
|
@@ -54,10 +54,10 @@ search.query.value = 'alice';
|
|
|
54
54
|
## Sourcerer integration
|
|
55
55
|
|
|
56
56
|
```ts
|
|
57
|
-
import { createIndex,
|
|
57
|
+
import { createIndex, toSearchMatcher } from '@vielzeug/scout';
|
|
58
58
|
|
|
59
59
|
const index = createIndex(users, { fields: ['name', 'email'] });
|
|
60
|
-
const source = createLocalSource(users, {
|
|
60
|
+
const source = createLocalSource(users, { match: toSearchMatcher(index) });
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
## Highlighting
|
package/dist/_dev.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
function e(e){}exports.warn=e;
|
|
2
2
|
//# sourceMappingURL=_dev.cjs.map
|
package/dist/_dev.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __SCOUT_PROD__?: boolean }).__SCOUT_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/scout] ${msg}`);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __SCOUT_PROD__?: boolean }).__SCOUT_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/scout] ${msg}`);\n}\n"],"mappings":"AAGA,SAAgB,EAAK,EAAmB,CAExC"}
|
package/dist/_dev.js
CHANGED
package/dist/_dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __SCOUT_PROD__?: boolean }).__SCOUT_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/scout] ${msg}`);\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __SCOUT_PROD__?: boolean }).__SCOUT_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/scout] ${msg}`);\n}\n"],"mappings":";AAGA,SAAgB,EAAK,GAAmB,CAExC"}
|
package/dist/adapters.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e,t){return(n,r
|
|
1
|
+
function e(e,t){let n,r=new Set;return(i,a)=>(a!==n&&(n=a,r=new Set(e.search(a,t).map(e=>e.item))),r.has(i))}function t(e,t,n){let r=new Set(e.search(t,n).map(e=>e.item));return e=>r.has(e)}exports.toFilterPredicate=t,exports.toSearchMatcher=e;
|
|
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 *
|
|
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":"AAOA,SAAgB,EACd,EACA,EACqC,CACrC,IAAI,EACA,EAAU,IAAI,IAElB,OAAQ,EAAM,KACR,IAAU,IACZ,EAAY,EACZ,EAAU,IAAI,IAAI,EAAM,OAAO,EAAO,CAAO,CAAC,CAAC,IAAK,GAAW,EAAO,IAAI,CAAC,GAGtE,EAAQ,IAAI,CAAI,EAE3B,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
CHANGED
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
import type { ScoutIndex } from './scout-index';
|
|
2
2
|
import type { SearchConstraints } from './types';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* The returned function ignores the `items` argument (the index is the source of truth)
|
|
7
|
-
* and delegates entirely to `index.search()`, returning plain items in score order.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```ts
|
|
11
|
-
* const index = createIndex(users, { fields: ['name', 'email'] });
|
|
12
|
-
*
|
|
13
|
-
* // Pass directly to sourcerer's searchFn option
|
|
14
|
-
* const source = createLocalSource(users, { searchFn: toSearchFn(index) });
|
|
15
|
-
* ```
|
|
4
|
+
* Adapts a `ScoutIndex` to sourcerer's explicit local `match` callback.
|
|
5
|
+
* Caches one match set per query so local filtering does not repeat index work per item.
|
|
16
6
|
*/
|
|
17
|
-
export declare function
|
|
7
|
+
export declare function toSearchMatcher<T>(index: ScoutIndex<T>, options?: SearchConstraints): (item: T, query: string) => boolean;
|
|
18
8
|
/**
|
|
19
9
|
* Returns a predicate that returns `true` for items matching `query` in the given index.
|
|
20
10
|
*
|
|
@@ -22,19 +12,6 @@ export declare function toSearchFn<T>(index: ScoutIndex<T>, options?: SearchCons
|
|
|
22
12
|
* query or corpus changes.
|
|
23
13
|
*
|
|
24
14
|
* Compatible with `Array.filter`, `vault`'s `query.filter()`, or any predicate pipeline.
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* const index = createIndex(products, { fields: ['title', 'sku'] });
|
|
29
|
-
*
|
|
30
|
-
* // Array filter
|
|
31
|
-
* const results = products.filter(toFilterPredicate(index, 'widget'));
|
|
32
|
-
*
|
|
33
|
-
* // vault query builder
|
|
34
|
-
* const rows = await db.query('products')
|
|
35
|
-
* .filter(toFilterPredicate(index, searchTerm))
|
|
36
|
-
* .toArray();
|
|
37
|
-
* ```
|
|
38
15
|
*/
|
|
39
16
|
export declare function toFilterPredicate<T>(index: ScoutIndex<T>, query: string, options?: SearchConstraints): (item: T) => boolean;
|
|
40
17
|
//# sourceMappingURL=adapters.d.ts.map
|
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;;;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,CAYrC;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,12 +1,13 @@
|
|
|
1
1
|
//#region src/adapters.ts
|
|
2
2
|
function e(e, t) {
|
|
3
|
-
|
|
3
|
+
let n, r = /* @__PURE__ */ new Set();
|
|
4
|
+
return (i, a) => (a !== n && (n = a, r = new Set(e.search(a, t).map((e) => e.item))), r.has(i));
|
|
4
5
|
}
|
|
5
6
|
function t(e, t, n) {
|
|
6
7
|
let r = new Set(e.search(t, n).map((e) => e.item));
|
|
7
8
|
return (e) => r.has(e);
|
|
8
9
|
}
|
|
9
10
|
//#endregion
|
|
10
|
-
export { t as toFilterPredicate, e as
|
|
11
|
+
export { t as toFilterPredicate, e as toSearchMatcher };
|
|
11
12
|
|
|
12
13
|
//# 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 *
|
|
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":";AAOA,SAAgB,EACd,GACA,GACqC;CACrC,IAAI,GACA,oBAAU,IAAI,IAAO;CAEzB,QAAQ,GAAM,OACR,MAAU,MACZ,IAAY,GACZ,IAAU,IAAI,IAAI,EAAM,OAAO,GAAO,CAAO,CAAC,CAAC,KAAK,MAAW,EAAO,IAAI,CAAC,IAGtE,EAAQ,IAAI,CAAI;AAE3B;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/devtools.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){let t=`scout:search`,n=[e.query.subscribe(()=>{console.debug(`[${t}] query -> ${JSON.stringify(e.query.peek())}`)}),e.isSearching.subscribe(()=>{console.debug(`[${t}] isSearching -> ${e.isSearching.peek()}`)}),e.results.subscribe(()=>{console.debug(`[${t}] results -> ${e.results.peek().length} item(s)`)})];return()=>{for(let e of n)e
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){let t=`scout:search`,n=[e.query.subscribe(()=>{console.debug(`[${t}] query -> ${JSON.stringify(e.query.peek())}`)}),e.isSearching.subscribe(()=>{console.debug(`[${t}] isSearching -> ${e.isSearching.peek()}`)}),e.results.subscribe(()=>{console.debug(`[${t}] results -> ${e.results.peek().length} item(s)`)})];return()=>{for(let e of n)e()}}exports.debugSearch=e;
|
|
2
2
|
//# sourceMappingURL=devtools.cjs.map
|
package/dist/devtools.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devtools.cjs","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/scout — debug utilities for the reactive search layer.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugSearch } from '@vielzeug/scout/devtools';\n * ```\n */\n\nimport type { SearchState } from './types';\n\n/**\n * Logs `query` → `isSearching` → `results` transitions of a `SearchState` to\n * `console.debug`. Development only — helps visualize debounce timing and result\n * churn without stepping through `createSearch()` internals.\n *\n * **Logs the full, literal search query string.** If your queries may carry PII (names,\n * emails, medical/financial terms typed by end users), don't enable this in production —\n * it's opt-in dev tooling imported from a dedicated sub-path for exactly that reason.\n *\n * Returns a function that unsubscribes all listeners installed by this call.\n *\n * @example\n * ```ts\n * import { debugSearch } from '@vielzeug/scout/devtools';\n *\n * const search = createSearch(index);\n * const stopDebugging = debugSearch(search);\n *\n * search.query.value = 'alice';\n * // [scout:search] query -> \"alice\"\n * // [scout:search] isSearching -> true\n * // [scout:search] isSearching -> false\n * // [scout:search] results -> 1 item(s)\n *\n * stopDebugging();\n * ```\n */\nexport function debugSearch<T>(search: SearchState<T>): () => void {\n const label = 'scout:search';\n\n const subscriptions = [\n search.query.subscribe(() => {\n console.debug(`[${label}] query -> ${JSON.stringify(search.query.peek())}`);\n }),\n search.isSearching.subscribe(() => {\n console.debug(`[${label}] isSearching -> ${search.isSearching.peek()}`);\n }),\n search.results.subscribe(() => {\n console.debug(`[${label}] results -> ${search.results.peek().length} item(s)`);\n }),\n ];\n\n return () => {\n for (const
|
|
1
|
+
{"version":3,"file":"devtools.cjs","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/scout — debug utilities for the reactive search layer.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugSearch } from '@vielzeug/scout/devtools';\n * ```\n */\n\nimport type { SearchState } from './types';\n\n/**\n * Logs `query` → `isSearching` → `results` transitions of a `SearchState` to\n * `console.debug`. Development only — helps visualize debounce timing and result\n * churn without stepping through `createSearch()` internals.\n *\n * **Logs the full, literal search query string.** If your queries may carry PII (names,\n * emails, medical/financial terms typed by end users), don't enable this in production —\n * it's opt-in dev tooling imported from a dedicated sub-path for exactly that reason.\n *\n * Returns a function that unsubscribes all listeners installed by this call.\n *\n * @example\n * ```ts\n * import { debugSearch } from '@vielzeug/scout/devtools';\n *\n * const search = createSearch(index);\n * const stopDebugging = debugSearch(search);\n *\n * search.query.value = 'alice';\n * // [scout:search] query -> \"alice\"\n * // [scout:search] isSearching -> true\n * // [scout:search] isSearching -> false\n * // [scout:search] results -> 1 item(s)\n *\n * stopDebugging();\n * ```\n */\nexport function debugSearch<T>(search: SearchState<T>): () => void {\n const label = 'scout:search';\n\n const subscriptions = [\n search.query.subscribe(() => {\n console.debug(`[${label}] query -> ${JSON.stringify(search.query.peek())}`);\n }),\n search.isSearching.subscribe(() => {\n console.debug(`[${label}] isSearching -> ${search.isSearching.peek()}`);\n }),\n search.results.subscribe(() => {\n console.debug(`[${label}] results -> ${search.results.peek().length} item(s)`);\n }),\n ];\n\n return () => {\n for (const unsubscribe of subscriptions) unsubscribe();\n };\n}\n"],"mappings":"mEAsCA,SAAgB,EAAe,EAAoC,CACjE,IAAM,EAAQ,eAER,EAAgB,CACpB,EAAO,MAAM,cAAgB,CAC3B,QAAQ,MAAM,IAAI,EAAM,aAAa,KAAK,UAAU,EAAO,MAAM,KAAK,CAAC,GAAG,CAC5E,CAAC,EACD,EAAO,YAAY,cAAgB,CACjC,QAAQ,MAAM,IAAI,EAAM,mBAAmB,EAAO,YAAY,KAAK,GAAG,CACxE,CAAC,EACD,EAAO,QAAQ,cAAgB,CAC7B,QAAQ,MAAM,IAAI,EAAM,eAAe,EAAO,QAAQ,KAAK,CAAC,CAAC,OAAO,SAAS,CAC/E,CAAC,CACH,EAEA,UAAa,CACX,IAAK,IAAM,KAAe,EAAe,EAAY,CACvD,CACF"}
|
package/dist/devtools.js
CHANGED
package/dist/devtools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devtools.js","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/scout — debug utilities for the reactive search layer.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugSearch } from '@vielzeug/scout/devtools';\n * ```\n */\n\nimport type { SearchState } from './types';\n\n/**\n * Logs `query` → `isSearching` → `results` transitions of a `SearchState` to\n * `console.debug`. Development only — helps visualize debounce timing and result\n * churn without stepping through `createSearch()` internals.\n *\n * **Logs the full, literal search query string.** If your queries may carry PII (names,\n * emails, medical/financial terms typed by end users), don't enable this in production —\n * it's opt-in dev tooling imported from a dedicated sub-path for exactly that reason.\n *\n * Returns a function that unsubscribes all listeners installed by this call.\n *\n * @example\n * ```ts\n * import { debugSearch } from '@vielzeug/scout/devtools';\n *\n * const search = createSearch(index);\n * const stopDebugging = debugSearch(search);\n *\n * search.query.value = 'alice';\n * // [scout:search] query -> \"alice\"\n * // [scout:search] isSearching -> true\n * // [scout:search] isSearching -> false\n * // [scout:search] results -> 1 item(s)\n *\n * stopDebugging();\n * ```\n */\nexport function debugSearch<T>(search: SearchState<T>): () => void {\n const label = 'scout:search';\n\n const subscriptions = [\n search.query.subscribe(() => {\n console.debug(`[${label}] query -> ${JSON.stringify(search.query.peek())}`);\n }),\n search.isSearching.subscribe(() => {\n console.debug(`[${label}] isSearching -> ${search.isSearching.peek()}`);\n }),\n search.results.subscribe(() => {\n console.debug(`[${label}] results -> ${search.results.peek().length} item(s)`);\n }),\n ];\n\n return () => {\n for (const
|
|
1
|
+
{"version":3,"file":"devtools.js","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/scout — debug utilities for the reactive search layer.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugSearch } from '@vielzeug/scout/devtools';\n * ```\n */\n\nimport type { SearchState } from './types';\n\n/**\n * Logs `query` → `isSearching` → `results` transitions of a `SearchState` to\n * `console.debug`. Development only — helps visualize debounce timing and result\n * churn without stepping through `createSearch()` internals.\n *\n * **Logs the full, literal search query string.** If your queries may carry PII (names,\n * emails, medical/financial terms typed by end users), don't enable this in production —\n * it's opt-in dev tooling imported from a dedicated sub-path for exactly that reason.\n *\n * Returns a function that unsubscribes all listeners installed by this call.\n *\n * @example\n * ```ts\n * import { debugSearch } from '@vielzeug/scout/devtools';\n *\n * const search = createSearch(index);\n * const stopDebugging = debugSearch(search);\n *\n * search.query.value = 'alice';\n * // [scout:search] query -> \"alice\"\n * // [scout:search] isSearching -> true\n * // [scout:search] isSearching -> false\n * // [scout:search] results -> 1 item(s)\n *\n * stopDebugging();\n * ```\n */\nexport function debugSearch<T>(search: SearchState<T>): () => void {\n const label = 'scout:search';\n\n const subscriptions = [\n search.query.subscribe(() => {\n console.debug(`[${label}] query -> ${JSON.stringify(search.query.peek())}`);\n }),\n search.isSearching.subscribe(() => {\n console.debug(`[${label}] isSearching -> ${search.isSearching.peek()}`);\n }),\n search.results.subscribe(() => {\n console.debug(`[${label}] results -> ${search.results.peek().length} item(s)`);\n }),\n ];\n\n return () => {\n for (const unsubscribe of subscriptions) unsubscribe();\n };\n}\n"],"mappings":";AAsCA,SAAgB,EAAe,GAAoC;CACjE,IAAM,IAAQ,gBAER,IAAgB;EACpB,EAAO,MAAM,gBAAgB;GAC3B,QAAQ,MAAM,IAAI,EAAM,aAAa,KAAK,UAAU,EAAO,MAAM,KAAK,CAAC,GAAG;EAC5E,CAAC;EACD,EAAO,YAAY,gBAAgB;GACjC,QAAQ,MAAM,IAAI,EAAM,mBAAmB,EAAO,YAAY,KAAK,GAAG;EACxE,CAAC;EACD,EAAO,QAAQ,gBAAgB;GAC7B,QAAQ,MAAM,IAAI,EAAM,eAAe,EAAO,QAAQ,KAAK,CAAC,CAAC,OAAO,SAAS;EAC/E,CAAC;CACH;CAEA,aAAa;EACX,KAAK,IAAM,KAAe,GAAe,EAAY;CACvD;AACF"}
|
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 is built or queried with an invalid configuration (e.g. zero fields defined). */\nexport class ScoutIndexError extends ScoutError {}\n"],"mappings":"AACA,IAAa,EAAb,MAAa,UAAmB,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,
|
|
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 is built or queried with an invalid configuration (e.g. zero fields defined). */\nexport class ScoutIndexError 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,cAAqC,CAAW,CAAC"}
|
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 is built or queried with an invalid configuration (e.g. zero fields defined). */\nexport class ScoutIndexError extends ScoutError {}\n"],"mappings":";AACA,IAAa,IAAb,MAAa,UAAmB,MAAM;CACpC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,
|
|
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 is built or queried with an invalid configuration (e.g. zero fields defined). */\nexport class ScoutIndexError 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,cAAqC,EAAW,CAAC"}
|
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.ScoutDisposedError=t.ScoutDisposedError,exports.ScoutError=t.ScoutError,exports.ScoutIndexError=t.ScoutIndexError,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.
|
|
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.ScoutDisposedError=t.ScoutDisposedError,exports.ScoutError=t.ScoutError,exports.ScoutIndexError=t.ScoutIndexError,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,4 +1,4 @@
|
|
|
1
|
-
export { toFilterPredicate,
|
|
1
|
+
export { toFilterPredicate, toSearchMatcher } from './adapters';
|
|
2
2
|
export { ScoutDisposedError, ScoutError, ScoutIndexError } from './errors';
|
|
3
3
|
export { findMatchRanges, highlight, highlightField } from './highlight';
|
|
4
4
|
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,
|
|
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,kBAAkB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3E,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
|
-
import { toFilterPredicate as e,
|
|
1
|
+
import { toFilterPredicate as e, toSearchMatcher as t } from "./adapters.js";
|
|
2
2
|
import { ScoutDisposedError as n, ScoutError as r, ScoutIndexError 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 ScoutDisposedError, r as ScoutError, i as ScoutIndexError, 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
|
|
7
|
+
export { n as ScoutDisposedError, r as ScoutError, i as ScoutIndexError, 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,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(),_
|
|
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,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
|
|
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,EAEhF,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.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,CAyFvG;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
|
@@ -34,7 +34,7 @@ function o(t, o = {}) {
|
|
|
34
34
|
}
|
|
35
35
|
let x = !1, S = new AbortController();
|
|
36
36
|
function C() {
|
|
37
|
-
x = !0, S.abort(), v(), y
|
|
37
|
+
x = !0, S.abort(), v(), y(), m();
|
|
38
38
|
}
|
|
39
39
|
return {
|
|
40
40
|
clear: b,
|
|
@@ -59,11 +59,8 @@ function s(e, n) {
|
|
|
59
59
|
limit: n.limit,
|
|
60
60
|
minQueryLength: n.minQueryLength,
|
|
61
61
|
threshold: n.threshold
|
|
62
|
-
});
|
|
63
|
-
return {
|
|
64
|
-
...o(r, { debounce: n.debounce }),
|
|
65
|
-
index: r
|
|
66
|
-
};
|
|
62
|
+
}), i = o(r, { debounce: n.debounce });
|
|
63
|
+
return Object.assign(Object.create(i), { index: r });
|
|
67
64
|
}
|
|
68
65
|
//#endregion
|
|
69
66
|
export { s as createReactiveSearch, o as createSearch };
|
package/dist/reactive.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.js","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
|
|
1
|
+
{"version":3,"file":"reactive.js","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":";;;;AAeA,IAAM,IAAmB;AAsCzB,SAAgB,EAAgB,GAAsB,IAA+B,CAAC,GAAmB;CACvG,IAAM,EAAE,UAAU,IAAa,GAAkB,UAAO,mBAAgB,iBAAc,GAEhF,IAAQ,EAAe,IAAI,EAAE,MAAM,cAAc,CAAC,GAClD,IAAiB,EAAe,IAAI,EAAE,MAAM,uBAAuB,CAAC,GACpE,IAAe,EAAO,GAAG,EAAE,MAAM,qBAAqB,CAAC,GAEvD,IAAuB,EAAM,eAAe;EAChD,EAAa;CACf,CAAC,GAEK,IAAc,QAAe,EAAM,UAAU,EAAe,OAAO,EAAE,MAAM,oBAAoB,CAAC,GAEhG,IAAU,SAGZ,EAAkB,OAEX,EAAM,OAAO,EAAe,OAAO;EAAE;EAAO;EAAgB;CAAU,CAAC,IAEhF,EAAE,MAAM,gBAAgB,CAC1B,GAEI,IAA8C;CAElD,SAAS,IAAoB;EAC3B,AAAI,MAAU,SACZ,aAAa,CAAK,GAClB,IAAQ;CAEZ;CAEA,IAAM,IAAe,EAAM,gBAAgB;EACzC,IAAM,IAAI,EAAM,KAAK;EAErB,MAAY,GAER,MAAM,EAAe,KAAK,GAE9B;OAAI,MAAe,GAAG;IACpB,EAAe,QAAQ;IAEvB;GACF;GAEA,IAAQ,iBAAiB;IAEvB,AADA,EAAe,QAAQ,GACvB,IAAQ;GACV,GAAG,CAAU;EALb;CAMF,CAAC;CAED,SAAS,IAAc;EACrB,IAAI,GAAY,MAAM,IAAI,EAAmB,4CAA4C;EAIzF,AAFA,EAAY,GAEZ,QAAY;GAEV,AADA,EAAM,QAAQ,IACd,EAAe,QAAQ;EACzB,CAAC;CACH;CAEA,IAAI,IAAa,IACX,IAAK,IAAI,gBAAgB;CAE/B,SAAS,IAAgB;EAKvB,AAJA,IAAa,IACb,EAAG,MAAM,GACT,EAAY,GACZ,EAAa,GACb,EAAqB;CACvB;CAEA,OAAO;EACL;EACA,IAAI,iBAA8B;GAChC,OAAO,EAAG;EACZ;EACA;EACA,IAAI,WAAoB;GACtB,OAAO;EACT;EACA;EACA;EACA;EACA,CAAC,OAAO,WAAiB;GACvB,EAAQ;EACV;CACF;AACF;AA4BA,SAAgB,EACd,GACA,GACmB;CACnB,IAAM,IAAQ,EAAY,GAAO;EAC/B,QAAQ,EAAQ;EAChB,OAAO,EAAQ;EACf,gBAAgB,EAAQ;EACxB,WAAW,EAAQ;CACrB,CAAC,GACK,IAAQ,EAAa,GAAO,EAAE,UAAU,EAAQ,SAAS,CAAC;CAEhE,OAAO,OAAO,OAAO,OAAO,OAAO,CAAK,GAAG,EAAE,SAAM,CAAC;AACtD"}
|
package/dist/scout.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e,t){return(n,r)=>e.search(r,t).map(e=>e.item)}function t(e,t,n){let r=new Set(e.search(t,n).map(e=>e.item));return e=>r.has(e)}var n=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}},r=class extends n{},i=class extends n{};function a(e,t){let n=e.toLowerCase(),r=t.split(` `).filter(Boolean),i=[];for(let e of r){let t=0;for(;t<n.length;){let r=n.indexOf(e,t);if(r===-1)break;i.push([r,r+e.length]),t=r+1}}i.sort((e,t)=>e[0]-t[0]);let a=[];for(let e of i){let t=a[a.length-1];t&&e[0]<=t[1]?t[1]=Math.max(t[1],e[1]):a.push([e[0],e[1]])}return a}function o(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 s(e,t,n){return o(n,e.matches.find(e=>e.field===t)?.ranges??[])}var c=null,l=()=>c,u=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}},d=class extends u{},f=class extends u{},p=e=>e instanceof Error?e:Error(`Non-Error thrown: ${String(e)}`),m=e=>{let t=[];for(let n of e)try{n()}catch(e){t.push(p(e))}return t},h=(e,t)=>{let n=m(e);if(n.length===1)throw n[0];if(n.length>0)throw AggregateError(n,t)},g={scheduling:{activeDirty:`a`,batchDepth:0,dirtyWithEffectSubsA:new Set,dirtyWithEffectSubsB:new Set,pendingSubscribers:new Set},scopeCleanups:null,tracking:null},_=null,v=()=>_===null?g:_.get(),y=(e,t)=>{let n={...v(),...e};if(_!==null)return _.run(n,t);let r=g;g=n;try{return t()}finally{g=r}},b=()=>v().scheduling,x=()=>_!==null,S=()=>v().scopeCleanups,C=!globalThis.__RIPPLE_PROD__;function ee(e){C&&console.warn(`[@vielzeug/ripple] ${e}`)}var w=!1,T=0,E=[],D=e=>e.activeDirty===`a`?e.dirtyWithEffectSubsA:e.dirtyWithEffectSubsB,te=(e,t)=>{let n=++T;for(let n of t.effectSubs())e.pendingSubscribers.add(n);for(let e of t.computedSubs())E.push(e);try{for(;E.length>0;){let t=E.pop();if(t.lastPropEpoch_!==n&&(t.lastPropEpoch_=n,t.markDirty())){t.effectSubs().size>0&&D(e).add(t);for(let e of t.computedSubs())E.push(e)}}}finally{E.length=0}},ne=e=>{for(;D(e).size>0;){let t=D(e);e.activeDirty=e.activeDirty===`a`?`b`:`a`,D(e).clear();for(let n of t)if(n.hasSubscribers()&&n.refreshIfDirty()){for(let t of n.effectSubs())e.pendingSubscribers.add(t);for(let t of n.computedSubs())t.markDirty()&&t.effectSubs().size>0&&D(e).add(t)}t.clear()}},O=e=>{let t=0;for(;e.pendingSubscribers.size>0||D(e).size>0;){if(++t>100)throw new f(`infinite flush loop (> 100 iterations)`);if(D(e).size>0&&ne(e),e.pendingSubscribers.size===0)continue;let n=[...e.pendingSubscribers];e.pendingSubscribers.clear(),h(n,`subscriber errors`)}},k=globalThis.process?.versions!=null,A=e=>{if(!e.hasSubscribers())return;!w&&k&&!x()&&(w=!0,ee(`Signal updated in a Node.js-like environment. The module-level flush queue is shared across concurrent requests — use per-request worker isolation or the @vielzeug/ripple/ssr sub-path for request-isolated scheduling.`));let t=b();te(t,e),t.batchDepth===0&&O(t)},j=e=>{let t=b();t.batchDepth++;let n;try{n=e()}catch(e){throw t.batchDepth--,t.batchDepth===0&&(t.pendingSubscribers.clear(),t.dirtyWithEffectSubsA.clear(),t.dirtyWithEffectSubsB.clear()),e}return t.batchDepth--,t.batchDepth===0&&O(t),n},M=0,re=()=>++M,N=()=>M,P=()=>v().tracking,F=(e,t)=>y({tracking:e},t),I=e=>{let t=P();t?.kind===`effect`?t.cleanups.push(e):S()?.push(e)},L=e=>{let t=P();if(t!==null){if(t.sourceObserver?.(e),t.kind===`computed`)t.depCollector.push({source:e,version:e.version});else if(t.kind===`effect`){let n=t.effect;e.addEffectSub(n),t.subscriptions.add(()=>e.removeEffectSub(n)),t.deps.set(e,e.version)}}},R=class{fn_;disposed_=!1;constructor(e){this.fn_=e}get disposed(){return this.disposed_}dispose(){if(this.disposed_)return;this.disposed_=!0;let e=this.fn_;this.fn_=null,e()}[Symbol.dispose](){this.dispose()}},ie=Symbol(`ripple.is-signal`),ae=Symbol(`ripple.is-computed`),z=Symbol(`ripple.uninitialized`),B=new FinalizationRegistry(({key:e,map:t})=>{t.delete(e)}),V=class{version=0;name;[ie]=!0;computedSubs_=new Map;effectSubs_=new Set;constructor(e){this.name=e}addComputedSub(e){let t=new WeakRef(e);this.computedSubs_.set(e,t),B.register(e,{key:e,map:this.computedSubs_},t)}removeComputedSub(e){let t=this.computedSubs_.get(e);t!==void 0&&(this.computedSubs_.delete(e),B.unregister(t))}addEffectSub(e){this.effectSubs_.add(e)}removeEffectSub(e){this.effectSubs_.delete(e)}clearSubscribers(){for(let e of this.computedSubs_.values())B.unregister(e);this.computedSubs_.clear(),this.effectSubs_.clear()}hasSubscribers(){if(this.effectSubs_.size>0)return!0;for(let e of this.computedSubs_.values())if(e.deref()!==void 0)return!0;return!1}*computedSubs(){for(let[e,t]of this.computedSubs_){let n=t.deref();n===void 0?(this.computedSubs_.delete(e),B.unregister(t)):yield n}}effectSubs(){return this.effectSubs_}},H=class extends V{[ae]=!0;lastPropEpoch_=0},U=class extends V{value_;equals_;disposed_;constructor(e,t,n){super(n),this.value_=e,this.equals_=t??Object.is,this.disposed_=!1}get value(){return this.disposed_||L(this),this.value_}set value(e){if(this.disposed_||this.equals_(this.value_,e))return;let t=this.value_;this.value_=e,this.version=re(),l()?.write?.({name:this.name,newValue:e,oldValue:t}),A(this)}peek(){return this.value_}subscribe=e=>{if(this.disposed_){let e=new R(()=>{});return e.dispose(),e}return this.addEffectSub(e),new R(()=>{this.removeEffectSub(e)})};get disposed(){return this.disposed_}dispose(){this.disposed_||(this.disposed_=!0,this.clearSubscribers(),l()?.dispose?.({kind:`signal`,name:this.name}))}[Symbol.dispose](){this.dispose()}},W=(e,t)=>new U(e,t?.equals,t?.name),G=class extends H{value_;dirty_;computing_;disposed_;deps_;compute_;equals_;maxRevision_;constructor(e,t){let{equals:n,name:r}=t??{};super(r),this.value_=z,this.dirty_=!0,this.computing_=!1,this.disposed_=!1,this.deps_=[],this.maxRevision_=-1,this.compute_=e,this.equals_=n===void 0?Object.is:(e,t)=>n(e,t)}markDirty(){return this.disposed_||this.dirty_?!1:(this.dirty_=!0,!0)}refreshIfDirty(){if(!this.dirty_)return!1;if(N()<=this.maxRevision_)return this.dirty_=!1,!1;if(this.deps_.length>0){let e=!0;for(let t of this.deps_){let n=t.source;if(`refreshIfDirty`in n&&n.refreshIfDirty(),n.version!==t.version){e=!1;break}}if(e)return this.dirty_=!1,this.maxRevision_=N(),!1}return this.recompute()}runCompute(e){try{return F({computed:this,depCollector:e,kind:`computed`},this.compute_)}catch(e){throw p(e)}}recompute(){if(this.computing_)throw new d(`computed cycle detected${this.name?` "${this.name}"`:``}`);this.computing_=!0;try{let e=[];l()?.compute?.({name:this.name});let t=this.runCompute(e);return this.dirty_=!1,this.maxRevision_=N(),this.updateDeps(e),this.value_===z||!this.equals_(this.value_,t)?(this.value_=t,this.version++,!0):!1}finally{this.computing_=!1}}updateDeps(e){let t=this.deps_;if(t.length===e.length&&t.every((t,n)=>t.source===e[n].source)){for(let n=0;n<e.length;n++)t[n].version=e[n].version;return}let n=new Set(t.map(e=>e.source)),r=new Set(e.map(e=>e.source));for(let e of t)r.has(e.source)||e.source.removeComputedSub(this);for(let t of e)n.has(t.source)||t.source.addComputedSub(this);this.deps_=e}get value(){return this.disposed_?this.value_===z?void 0:this.value_:(this.refreshIfDirty(),L(this),this.value_)}peek(){return this.disposed_?this.value_===z?void 0:this.value_:(this.refreshIfDirty(),this.value_)}subscribe=e=>{if(this.disposed_){let e=new R(()=>{});return e.dispose(),e}return this.refreshIfDirty(),this.addEffectSub(e),new R(()=>{this.removeEffectSub(e)})};get disposed(){return this.disposed_}dispose(){if(!this.disposed_){this.disposed_=!0;for(let e of this.deps_)e.source.removeComputedSub(this);this.deps_=[],this.clearSubscribers(),l()?.dispose?.({kind:`computed`,name:this.name})}}[Symbol.dispose](){this.dispose()}},K=(e,t)=>{let n=new G(e,t);return I(()=>n.dispose()),n},q=!globalThis.__SCOUT_PROD__;function oe(e){q&&console.warn(`[@vielzeug/scout] ${e}`)}function J(e){return e.toLowerCase().replace(/[^\p{L}\p{N}\s']/gu,` `).trim().replace(/\s+/g,` `)}function Y(e){return e==null?``:typeof e==`string`?e:typeof e==`number`||typeof e==`boolean`?String(e):Array.isArray(e)?(oe("defaultStringify: received an array value — provide a custom `stringify` on the FieldDef to control array indexing.\nFalling back to joining string/number/boolean elements with a space."),e.filter(e=>typeof e==`string`||typeof e==`number`||typeof e==`boolean`).join(` `)):``}function X(e){let t=new Set,n=` ${e} `;for(let e=0;e<n.length-2;e++)t.add(n.slice(e,e+3));return t}function se(e,t){if(e.size===0||t.size===0)return 0;let n=0;for(let r of e)t.has(r)&&n++;return n<2&&n<e.size?0:n/Math.min(e.size,t.size)}function ce(e){return e.map(e=>typeof e==`string`?{field:e,stringify:Y,weight:1}:{field:e.field,stringify:e.stringify??Y,weight:e.weight??1})}function Z(e,t){if(t.fields.length===0)throw new i(`createIndex: at least one field is required.`);let n=ce(t.fields),r=n.reduce((e,t)=>Math.max(e,t.weight),1),o=t.threshold??.2,s=t.limit??50,c=t.minQueryLength??3,l=new Map,u=new Map,d=new Set;function f(){for(let e of d)e()}let p=null,m=null;function h(e){return e===p&&m!==null?m:(p=e,m=X(e),m)}function g(e,t){for(let n of t){let t=u.get(n);t||(t=new Set,u.set(n,t)),t.add(e)}}function _(e,t){for(let n of t){let t=u.get(n);t&&(t.delete(e),t.size===0&&u.delete(n))}}function v(e){let t=new Map,r=new Map;for(let{field:i,stringify:a}of n){let n=e[i],o=a(n),s=J(o),c=s.length>=1?X(s):new Set;t.set(i,c),r.set(i,o),g(e,c)}l.set(e,{trigrams:t,values:r})}function y(e){let t=new Set;for(let[n,r]of l)for(let i of r.values.values())if(i.toLowerCase().includes(e)){t.add(n);break}return t}function b(e){let t=new Set;for(let n of e){let e=u.get(n);if(e)for(let n of e)t.add(n)}return t}function x(e,t,i){let a=0;for(let{field:o,weight:s}of n){let n;if(t===null)n=+!!(i.values.get(o)??``).toLowerCase().includes(e);else{let e=i.trigrams.get(o);if(!e||e.size===0)continue;n=se(t,e)}let c=s/r*n;c>a&&(a=c)}return a}function S(e,t){let r=[];for(let{field:i}of n){let n=t.get(i);if(!n)continue;let o=a(n,e);o.length>0&&r.push({field:i,ranges:o})}return r}for(let t of e)v(t);return{add(e){l.has(e)||(v(e),f())},get items(){return[...l.keys()]},onMutate(e){return d.add(e),()=>{d.delete(e)}},reindex(e){let t=l.get(e);if(!t)return;let r=!1;for(let{field:i,stringify:a}of n){let n=e[i],o=a(n);if(o===t.values.get(i))continue;r=!0;let s=t.trigrams.get(i);s&&_(e,s);let c=J(o),l=c.length>=1?X(c):new Set;t.trigrams.set(i,l),t.values.set(i,o),g(e,l)}r&&f()},remove(e){let t=l.get(e);if(t){for(let n of t.trigrams.values())_(e,n);l.delete(e),f()}},search(e,t){let n=t?.threshold??o,r=Math.max(0,t?.limit??s),i=t?.minQueryLength??c;if(!e.trim())return[...l.keys()].slice(0,r).map(e=>({item:e,matches:[],score:1}));let a=J(e);if(!a)return[];let u=a.length<i?null:h(a),d=u===null?y(a):b(u),f=[];for(let e of d){let t=l.get(e);if(!t)continue;let r=x(a,u,t);if(r>=n){let n=S(a,t.values);f.push({item:e,matches:n,score:r})}}return f.sort((e,t)=>t.score-e.score).slice(0,r)},get size(){return l.size}}}var le=200;function Q(e,t={}){let{debounce:n=le,limit:i,minQueryLength:a,threshold:o}=t,s=W(``,{name:`scout:query`}),c=W(``,{name:`scout:committedQuery`}),l=W(0,{name:`scout:indexVersion`}),u=e.onMutate(()=>{l.value++}),d=K(()=>s.value!==c.value,{name:`scout:isSearching`}),f=K(()=>(l.value,e.search(c.value,{limit:i,minQueryLength:a,threshold:o})),{name:`scout:results`}),p=null;function m(){p!==null&&(clearTimeout(p),p=null)}let h=s.subscribe(()=>{let e=s.peek();if(m(),e!==c.peek()){if(n===0){c.value=e;return}p=setTimeout(()=>{c.value=e,p=null},n)}});function g(){if(_)throw new r(`SearchState.clear() called after dispose()`);m(),j(()=>{s.value=``,c.value=``})}let _=!1,v=new AbortController;function y(){_=!0,v.abort(),m(),h.dispose(),u(),s.dispose(),c.dispose(),d.dispose(),f.dispose(),l.dispose()}return{clear:g,get disposalSignal(){return v.signal},dispose:y,get disposed(){return _},isSearching:d,query:s,results:f,[Symbol.dispose](){y()}}}function ue(e,t){let n=Z(e,{fields:t.fields,limit:t.limit,minQueryLength:t.minQueryLength,threshold:t.threshold});return{...Q(n,{debounce:t.debounce}),index:n}}function de(e){let t=fe();return t?[...t.segment(e)].filter(e=>e.isWordLike).map(e=>e.segment).join(` `):e}var $;function fe(){return $===void 0&&($=typeof Intl>`u`||typeof Intl.Segmenter!=`function`?null:new Intl.Segmenter(void 0,{granularity:`word`})),$}exports.ScoutDisposedError=r,exports.ScoutError=n,exports.ScoutIndexError=i,exports.createIndex=Z,exports.createReactiveSearch=ue,exports.createSearch=Q,exports.findMatchRanges=a,exports.highlight=o,exports.highlightField=s,exports.segmentWords=de,exports.toFilterPredicate=t,exports.toSearchFn=e;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e,t){let n,r=new Set;return(i,a)=>(a!==n&&(n=a,r=new Set(e.search(a,t).map(e=>e.item))),r.has(i))}function t(e,t,n){let r=new Set(e.search(t,n).map(e=>e.item));return e=>r.has(e)}var n=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}},r=class extends n{},i=class extends n{};function a(e,t){let n=e.toLowerCase(),r=t.split(` `).filter(Boolean),i=[];for(let e of r){let t=0;for(;t<n.length;){let r=n.indexOf(e,t);if(r===-1)break;i.push([r,r+e.length]),t=r+1}}i.sort((e,t)=>e[0]-t[0]);let a=[];for(let e of i){let t=a[a.length-1];t&&e[0]<=t[1]?t[1]=Math.max(t[1],e[1]):a.push([e[0],e[1]])}return a}function o(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 s(e,t,n){return o(n,e.matches.find(e=>e.field===t)?.ranges??[])}var c=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}},l=class extends c{},u=class extends c{},d=class extends c{},f=Symbol(`ripple.reactive`),p=Symbol(`ripple.signal`),m=Symbol(`ripple.computed`),h=100,g=Symbol(`ripple.unset`),_=class{[f]=!0;dependents=new Set;name;runtime;constructor(e,t){this.runtime=e,this.name=t}subscribe(e){this.peek();let t={dependencies:new Set,onDependencyChanged:()=>this.runtime.enqueueListener(e)};return this.dependents.add(t),()=>this.dependents.delete(t)}notify(){for(let e of[...this.dependents])e.onDependencyChanged()}},v=class extends _{[p]=!0;current;equals;constructor(e,t,n){super(e,n?.name),this.current=t,this.equals=n?.equals??Object.is}get value(){return this.runtime.track(this),this.current}set value(e){if(this.equals(this.current,e))return;let t=this.current;this.current=e,this.runtime.emit({kind:`write`,name:this.name,next:e,previous:t}),this.runtime.propagate(()=>this.notify())}peek(){return this.current}},y=class extends _{[m]=!0;dependencies=new Set;computing=!1;disposed=!1;dirty=!0;current=g;derive;equals;constructor(e,t,n){super(e,n?.name),this.derive=t,this.equals=n?.equals??Object.is}get value(){return this.refresh(),this.runtime.track(this),this.current}peek(){return this.refresh(),this.current}onDependencyChanged(){this.disposed||(this.dirty||=!0,this.dependents.size>0&&this.refresh()&&this.notify())}dispose(){this.disposed||(this.disposed=!0,this.runtime.clearDependencies(this),this.dependents.clear())}refresh(){if(!this.dirty||this.disposed)return!1;if(this.computing)throw new l(`computed cycle detected${this.name===void 0?``:` "${this.name}"`}`);this.computing=!0,this.runtime.emit({kind:`compute`,name:this.name});try{let e=this.runtime.collect(this,this.derive),t=this.current===g||!this.equals(this.current,e);return this.current=e,this.dirty=!1,t}finally{this.computing=!1}}},b=class{disposalController=new AbortController;owned=new Set;name;isDisposed=!1;runtime;constructor(e,t){this.runtime=e,this.name=t}get disposed(){return this.isDisposed}get disposalSignal(){return this.disposalController.signal}run(e){if(this.isDisposed)throw new u(`Cannot run a disposed scope.`);return this.runtime.withScope(this,e)}dispose(){if(!this.isDisposed){this.isDisposed=!0;for(let e of[...this.owned].reverse())e.dispose();this.owned.clear(),this.disposalController.abort(),this.runtime.emit({kind:`dispose`,name:this.name,node:`scope`})}}[Symbol.dispose](){this.dispose()}},x=class{dependencies=new Set;disposalController=new AbortController;cleanup;isDisposed=!1;owner;scheduled=!1;callback;options;runtime;constructor(e,t,n){this.runtime=e,this.callback=t,this.options=n}get disposed(){return this.isDisposed}get disposalSignal(){return this.disposalController.signal}onDependencyChanged(){if(!this.isDisposed){if(this.options?.scheduler===`microtask`){if(this.scheduled)return;this.scheduled=!0,queueMicrotask(()=>{this.scheduled=!1,this.isDisposed||this.runtime.enqueue(this)});return}this.runtime.enqueue(this)}}run(){if(this.isDisposed)return;this.owner?.dispose(),this.owner=void 0,this.runCleanup(),this.runtime.emit({kind:`effect`,name:this.options?.name});let e=new b(this.runtime);try{let t=this.runtime.withEffectScope(e,()=>this.runtime.collectEffect(this,this.callback));this.owner=e,this.cleanup=typeof t==`function`?t:void 0}catch(t){e.dispose(),this.runtime.report(t,{kind:`effect`,name:this.options?.name})}}dispose(){this.isDisposed||(this.isDisposed=!0,this.owner?.dispose(),this.owner=void 0,this.runtime.clearDependencies(this),this.runCleanup(),this.disposalController.abort(),this.runtime.emit({kind:`dispose`,name:this.options?.name,node:`effect`}))}[Symbol.dispose](){this.dispose()}runCleanup(){let e=this.cleanup;if(this.cleanup=void 0,e!==void 0)try{e()}catch(e){this.runtime.report(e,{kind:`cleanup`,name:this.options?.name})}}},S=class{activeEffectScope;activeObserver;activeScope;flushDepth=0;flushing=!1;pending=new Set;listeners=new Set;rootScope;observer;onError;constructor(e){this.observer=e?.observer,this.onError=e?.onError??(e=>{queueMicrotask(()=>{throw e})}),this.rootScope=new b(this,`runtime`),this.activeScope=this.rootScope}signal=(e,t)=>new v(this,e,t);computed=(e,t)=>{let n=new y(this,e,t);return(this.activeEffectScope??this.activeScope).owned.add(n),n};effect=(e,t)=>{let n=new x(this,e,t);return(this.activeEffectScope??this.activeScope).owned.add(n),n.run(),n};createScope=e=>{let t=new b(this,e);return this.activeScope.owned.add(t),t};batch=e=>this.propagate(e);untrack=e=>this.withObserver(void 0,e);dispose(){this.rootScope.dispose()}track(e){let t=this.activeObserver;t?.collecting!==void 0&&t.collecting.add(e)}clearDependencies(e){for(let t of e.dependencies)t.dependents.delete(e);e.dependencies.clear()}collect(e,t){return this.collectWith(e,t,!1)}collectEffect(e,t){return this.collectWith(e,t,!0)}withEffectScope(e,t){let n=this.activeEffectScope;this.activeEffectScope=e;try{return t()}finally{this.activeEffectScope=n}}withObserver(e,t){let n=this.activeObserver;this.activeObserver=e;try{return t()}finally{this.activeObserver=n}}withScope(e,t){let n=this.activeEffectScope,r=this.activeScope;this.activeEffectScope=void 0,this.activeScope=e;try{return t()}finally{this.activeEffectScope=n,this.activeScope=r}}enqueue(e){this.pending.add(e),this.flushDepth===0&&this.flush()}enqueueListener(e){this.listeners.add(e),this.flushDepth===0&&this.flush()}propagate(e){this.flushDepth++;try{return e()}finally{this.flushDepth--,this.flushDepth===0&&this.flush()}}emit(e){try{this.observer?.(e)}catch(t){this.report(t,{kind:`observer`,name:e.name})}}report(e,t){try{this.onError(e,t)}catch(e){queueMicrotask(()=>{throw e})}}collectWith(e,t,n){let r=this.activeObserver,i=new Set;e.collecting=i,this.activeObserver=e;try{let n=t();return this.commitDependencies(e,i),n}catch(t){throw n&&this.commitDependencies(e,i),t}finally{e.collecting=void 0,this.activeObserver=r}}commitDependencies(e,t){for(let n of e.dependencies)t.has(n)||n.dependents.delete(e);for(let n of t)e.dependencies.has(n)||n.dependents.add(e);e.dependencies.clear();for(let n of t)e.dependencies.add(n)}flush(){if(this.flushing)return;this.flushing=!0;let e=0;try{for(;this.pending.size>0||this.listeners.size>0;){if(++e>h)throw new d(`infinite reactive flush (>${h} iterations)`);let t=[...this.pending],n=[...this.listeners];this.pending.clear(),this.listeners.clear();for(let e of t)e.run();for(let e of n)try{e()}catch(e){this.report(e,{kind:`listener`})}}}finally{this.flushing=!1}}},C=e=>(t,n,r)=>{let i=e.signal({status:`pending`},{name:r?.name}),a=e.signal(0),o=new AbortController,s,c=!1,l=()=>{a.value,s?.abort();let e=i.peek(),r=e.status===`success`?e.value:`previous`in e?e.previous:void 0,o;try{o=t()}catch(e){i.value=r===void 0?{error:e,status:`error`}:{error:e,previous:r,status:`error`};return}let l=new AbortController;s=l,i.value=r===void 0?{status:`pending`}:{previous:r,status:`pending`};let u;try{u=Promise.resolve(n(o,{signal:l.signal}))}catch(e){u=Promise.reject(e)}u.then(e=>{!c&&!l.signal.aborted&&(i.value={status:`success`,value:e})},e=>{!c&&!l.signal.aborted&&(i.value=r===void 0?{error:e,status:`error`}:{error:e,previous:r,status:`error`})})},u=e.effect(()=>(l(),()=>s?.abort()),{name:r?.name});return u.disposalSignal.addEventListener(`abort`,()=>{c=!0,o.abort()},{once:!0}),{get disposalSignal(){return o.signal},dispose:()=>u.dispose(),get disposed(){return c},get name(){return i.name},peek:()=>i.peek(),reload:()=>{c||(a.value=a.peek()+1)},subscribe:e=>i.subscribe(e),[Symbol.dispose](){this.dispose()},get value(){return i.value}}},w=e=>(t,n)=>{let r=e.signal(t,{name:n?.name});return{get name(){return r.name},peek:()=>r.peek(),set:e=>{r.value=e},subscribe:e=>r.subscribe(e),update:e=>{r.value=e(r.peek())},get value(){return r.value}}},T=e=>(t,n,r)=>{let i=typeof t==`function`?t:()=>t.value,a=r?.equals??Object.is,o=!0,s,c=!1,l=e.effect(()=>{let e=i();if(o){o=!1,s=e,r?.immediate&&n(e,void 0),c=r?.once===!0&&r.immediate===!0;return}if(a(s,e))return;let t=s;s=e,n(e,t),r?.once&&l.dispose()},{name:r?.name});return c&&l.dispose(),l},E=(e=>{let t=new S(e),n=C(t),r=w(t);return{batch:t.batch,computed:t.computed,createScope:t.createScope,createStore:r,dispose:()=>t.dispose(),effect:t.effect,resource:n,signal:t.signal,untrack:t.untrack,watch:T(t)}})(),D=E.signal,O=E.computed;E.effect;var k=E.batch;E.createScope,E.createStore,E.resource,E.untrack,E.watch;function A(e){return e.toLowerCase().replace(/[^\p{L}\p{N}\s']/gu,` `).trim().replace(/\s+/g,` `)}function j(e){return e==null?``:typeof e==`string`?e:typeof e==`number`||typeof e==`boolean`?String(e):Array.isArray(e)?e.filter(e=>typeof e==`string`||typeof e==`number`||typeof e==`boolean`).join(` `):``}function M(e){let t=new Set,n=` ${e} `;for(let e=0;e<n.length-2;e++)t.add(n.slice(e,e+3));return t}function N(e,t){if(e.size===0||t.size===0)return 0;let n=0;for(let r of e)t.has(r)&&n++;return n<2&&n<e.size?0:n/Math.min(e.size,t.size)}function P(e){return e.map(e=>typeof e==`string`?{field:e,stringify:j,weight:1}:{field:e.field,stringify:e.stringify??j,weight:e.weight??1})}function F(e,t){if(t.fields.length===0)throw new i(`createIndex: at least one field is required.`);let n=P(t.fields),r=n.reduce((e,t)=>Math.max(e,t.weight),1),o=t.threshold??.2,s=t.limit??50,c=t.minQueryLength??3,l=new Map,u=new Map,d=new Set;function f(){for(let e of d)e()}let p=null,m=null;function h(e){return e===p&&m!==null?m:(p=e,m=M(e),m)}function g(e,t){for(let n of t){let t=u.get(n);t||(t=new Set,u.set(n,t)),t.add(e)}}function _(e,t){for(let n of t){let t=u.get(n);t&&(t.delete(e),t.size===0&&u.delete(n))}}function v(e){let t=new Map,r=new Map;for(let{field:i,stringify:a}of n){let n=e[i],o=a(n),s=A(o),c=s.length>=1?M(s):new Set;t.set(i,c),r.set(i,o),g(e,c)}l.set(e,{trigrams:t,values:r})}function y(e){let t=new Set;for(let[n,r]of l)for(let i of r.values.values())if(i.toLowerCase().includes(e)){t.add(n);break}return t}function b(e){let t=new Set;for(let n of e){let e=u.get(n);if(e)for(let n of e)t.add(n)}return t}function x(e,t,i){let a=0;for(let{field:o,weight:s}of n){let n;if(t===null)n=+!!(i.values.get(o)??``).toLowerCase().includes(e);else{let e=i.trigrams.get(o);if(!e||e.size===0)continue;n=N(t,e)}let c=s/r*n;c>a&&(a=c)}return a}function S(e,t){let r=[];for(let{field:i}of n){let n=t.get(i);if(!n)continue;let o=a(n,e);o.length>0&&r.push({field:i,ranges:o})}return r}for(let t of e)v(t);return{add(e){l.has(e)||(v(e),f())},get items(){return[...l.keys()]},onMutate(e){return d.add(e),()=>{d.delete(e)}},reindex(e){let t=l.get(e);if(!t)return;let r=!1;for(let{field:i,stringify:a}of n){let n=e[i],o=a(n);if(o===t.values.get(i))continue;r=!0;let s=t.trigrams.get(i);s&&_(e,s);let c=A(o),l=c.length>=1?M(c):new Set;t.trigrams.set(i,l),t.values.set(i,o),g(e,l)}r&&f()},remove(e){let t=l.get(e);if(t){for(let n of t.trigrams.values())_(e,n);l.delete(e),f()}},search(e,t){let n=t?.threshold??o,r=Math.max(0,t?.limit??s),i=t?.minQueryLength??c;if(!e.trim())return[...l.keys()].slice(0,r).map(e=>({item:e,matches:[],score:1}));let a=A(e);if(!a)return[];let u=a.length<i?null:h(a),d=u===null?y(a):b(u),f=[];for(let e of d){let t=l.get(e);if(!t)continue;let r=x(a,u,t);if(r>=n){let n=S(a,t.values);f.push({item:e,matches:n,score:r})}}return f.sort((e,t)=>t.score-e.score).slice(0,r)},get size(){return l.size}}}var I=200;function L(e,t={}){let{debounce:n=I,limit:i,minQueryLength:a,threshold:o}=t,s=D(``,{name:`scout:query`}),c=D(``,{name:`scout:committedQuery`}),l=D(0,{name:`scout:indexVersion`}),u=e.onMutate(()=>{l.value++}),d=O(()=>s.value!==c.value,{name:`scout:isSearching`}),f=O(()=>(l.value,e.search(c.value,{limit:i,minQueryLength:a,threshold:o})),{name:`scout:results`}),p=null;function m(){p!==null&&(clearTimeout(p),p=null)}let h=s.subscribe(()=>{let e=s.peek();if(m(),e!==c.peek()){if(n===0){c.value=e;return}p=setTimeout(()=>{c.value=e,p=null},n)}});function g(){if(_)throw new r(`SearchState.clear() called after dispose()`);m(),k(()=>{s.value=``,c.value=``})}let _=!1,v=new AbortController;function y(){_=!0,v.abort(),m(),h(),u()}return{clear:g,get disposalSignal(){return v.signal},dispose:y,get disposed(){return _},isSearching:d,query:s,results:f,[Symbol.dispose](){y()}}}function R(e,t){let n=F(e,{fields:t.fields,limit:t.limit,minQueryLength:t.minQueryLength,threshold:t.threshold}),r=L(n,{debounce:t.debounce});return Object.assign(Object.create(r),{index:n})}function z(e){let t=V();return t?[...t.segment(e)].filter(e=>e.isWordLike).map(e=>e.segment).join(` `):e}var B;function V(){return B===void 0&&(B=typeof Intl>`u`||typeof Intl.Segmenter!=`function`?null:new Intl.Segmenter(void 0,{granularity:`word`})),B}exports.ScoutDisposedError=r,exports.ScoutError=n,exports.ScoutIndexError=i,exports.createIndex=F,exports.createReactiveSearch=R,exports.createSearch=L,exports.findMatchRanges=a,exports.highlight=o,exports.highlightField=s,exports.segmentWords=z,exports.toFilterPredicate=t,exports.toSearchMatcher=e;
|
|
2
2
|
//# sourceMappingURL=scout.cjs.map
|