@vielzeug/scout 1.1.7 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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** — `toSearchFn()` for sourcerer, `toFilterPredicate()` for filter pipelines
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, toSearchFn } from '@vielzeug/scout';
57
+ import { createIndex, toSearchMatcher } from '@vielzeug/scout';
58
58
 
59
59
  const index = createIndex(users, { fields: ['name', 'email'] });
60
- const source = createLocalSource(users, { searchFn: toSearchFn(index) });
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
- var e=!globalThis.__SCOUT_PROD__;function t(t){e&&console.warn(`[@vielzeug/scout] ${t}`)}exports.warn=t;
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":"AAAA,IAAM,EAAQ,CAAE,WAA4C,eAG5D,SAAgB,EAAK,EAAmB,CAClC,GAAO,QAAQ,KAAK,qBAAqB,GAAK,CACpD"}
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
@@ -1,9 +1,6 @@
1
1
  //#region src/_dev.ts
2
- var e = !globalThis.__SCOUT_PROD__;
3
- function t(t) {
4
- e && console.warn(`[@vielzeug/scout] ${t}`);
5
- }
2
+ function e(e) {}
6
3
  //#endregion
7
- export { t as warn };
4
+ export { e as warn };
8
5
 
9
6
  //# sourceMappingURL=_dev.js.map
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":";AAAA,IAAM,IAAQ,CAAE,WAA4C;AAG5D,SAAgB,EAAK,GAAmB;CACtC,AAAI,KAAO,QAAQ,KAAK,qBAAqB,GAAK;AACpD"}
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)=>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)}exports.toFilterPredicate=t,exports.toSearchFn=e;
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
@@ -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 * Wraps a `ScoutIndex` as a `searchFn` compatible with `sourcerer`'s `LocalSourceConfig`.\n *\n * The returned function ignores the `items` argument (the index is the source of truth)\n * and delegates entirely to `index.search()`, returning plain items in score order.\n *\n * @example\n * ```ts\n * const index = createIndex(users, { fields: ['name', 'email'] });\n *\n * // Pass directly to sourcerer's searchFn option\n * const source = createLocalSource(users, { searchFn: toSearchFn(index) });\n * ```\n */\nexport function toSearchFn<T>(\n index: ScoutIndex<T>,\n options?: SearchConstraints,\n): (items: readonly T[], query: string) => readonly T[] {\n return (_items, query) => index.search(query, options).map((r) => r.item);\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 *\n * @example\n * ```ts\n * const index = createIndex(products, { fields: ['title', 'sku'] });\n *\n * // Array filter\n * const results = products.filter(toFilterPredicate(index, 'widget'));\n *\n * // vault query builder\n * const rows = await db.query('products')\n * .filter(toFilterPredicate(index, searchTerm))\n * .toArray();\n * ```\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((r) => r.item));\n\n return (item) => matchSet.has(item);\n}\n"],"mappings":"AAiBA,SAAgB,EACd,EACA,EACsD,CACtD,OAAQ,EAAQ,IAAU,EAAM,OAAO,EAAO,CAAO,CAAC,CAAC,IAAK,GAAM,EAAE,IAAI,CAC1E,CAuBA,SAAgB,EACd,EACA,EACA,EACsB,CACtB,IAAM,EAAW,IAAI,IAAI,EAAM,OAAO,EAAO,CAAO,CAAC,CAAC,IAAK,GAAM,EAAE,IAAI,CAAC,EAExE,MAAQ,IAAS,EAAS,IAAI,CAAI,CACpC"}
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"}
@@ -1,20 +1,10 @@
1
1
  import type { ScoutIndex } from './scout-index';
2
2
  import type { SearchConstraints } from './types';
3
3
  /**
4
- * Wraps a `ScoutIndex` as a `searchFn` compatible with `sourcerer`'s `LocalSourceConfig`.
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 toSearchFn<T>(index: ScoutIndex<T>, options?: SearchConstraints): (items: readonly T[], query: string) => readonly T[];
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
@@ -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;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAC1B,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EACpB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC,EAAE,CAEtD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;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"}
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
- return (n, r) => e.search(r, t).map((e) => e.item);
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 toSearchFn };
11
+ export { t as toFilterPredicate, e as toSearchMatcher };
11
12
 
12
13
  //# sourceMappingURL=adapters.js.map
@@ -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 * Wraps a `ScoutIndex` as a `searchFn` compatible with `sourcerer`'s `LocalSourceConfig`.\n *\n * The returned function ignores the `items` argument (the index is the source of truth)\n * and delegates entirely to `index.search()`, returning plain items in score order.\n *\n * @example\n * ```ts\n * const index = createIndex(users, { fields: ['name', 'email'] });\n *\n * // Pass directly to sourcerer's searchFn option\n * const source = createLocalSource(users, { searchFn: toSearchFn(index) });\n * ```\n */\nexport function toSearchFn<T>(\n index: ScoutIndex<T>,\n options?: SearchConstraints,\n): (items: readonly T[], query: string) => readonly T[] {\n return (_items, query) => index.search(query, options).map((r) => r.item);\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 *\n * @example\n * ```ts\n * const index = createIndex(products, { fields: ['title', 'sku'] });\n *\n * // Array filter\n * const results = products.filter(toFilterPredicate(index, 'widget'));\n *\n * // vault query builder\n * const rows = await db.query('products')\n * .filter(toFilterPredicate(index, searchTerm))\n * .toArray();\n * ```\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((r) => r.item));\n\n return (item) => matchSet.has(item);\n}\n"],"mappings":";AAiBA,SAAgB,EACd,GACA,GACsD;CACtD,QAAQ,GAAQ,MAAU,EAAM,OAAO,GAAO,CAAO,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI;AAC1E;AAuBA,SAAgB,EACd,GACA,GACA,GACsB;CACtB,IAAM,IAAW,IAAI,IAAI,EAAM,OAAO,GAAO,CAAO,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI,CAAC;CAExE,QAAQ,MAAS,EAAS,IAAI,CAAI;AACpC"}
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.dispose()}}exports.debugSearch=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
@@ -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 subscription of subscriptions) subscription.dispose();\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,KAAgB,EAAe,EAAa,QAAQ,CACjE,CACF"}
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
@@ -12,7 +12,7 @@ function e(e) {
12
12
  })
13
13
  ];
14
14
  return () => {
15
- for (let e of n) e.dispose();
15
+ for (let e of n) e();
16
16
  };
17
17
  }
18
18
  //#endregion
@@ -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 subscription of subscriptions) subscription.dispose();\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,KAAgB,GAAe,EAAa,QAAQ;CACjE;AACF"}
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"}
@@ -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,IAAI,OAAO,KACvB,OAAO,eAAe,KAAM,IAAI,OAAO,SAAS,CAClD,CAEA,OAAO,GAAG,EAAiC,CACzC,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAAwC,CAAW,CAAC,EAGvC,EAAb,cAAqC,CAAW,CAAC"}
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"}
@@ -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,IAAI,OAAO,MACvB,OAAO,eAAe,MAAM,IAAI,OAAO,SAAS;CAClD;CAEA,OAAO,GAAG,GAAiC;EACzC,OAAO,aAAe;CACxB;AACF,GAGa,IAAb,cAAwC,EAAW,CAAC,GAGvC,IAAb,cAAqC,EAAW,CAAC"}
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.toSearchFn=e.toSearchFn;
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, toSearchFn } from './adapters';
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';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC3D,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"}
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, toSearchFn as t } from "./adapters.js";
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 toSearchFn };
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(),_.dispose(),f(),l.dispose(),u.dispose(),p.dispose(),m.dispose(),d.dispose()}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});return{...i(r,{debounce:n.debounce}),index:r}}exports.createReactiveSearch=a,exports.createSearch=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,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
@@ -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.dispose();\n unsubscribeMutations();\n query.dispose();\n committedQuery.dispose();\n isSearching.dispose();\n results.dispose();\n indexVersion.dispose();\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 { ...state, index };\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,EAAA,EAAA,OAAA,CAAuB,GAAI,CAAE,KAAM,aAAc,CAAC,EAClD,GAAA,EAAA,EAAA,OAAA,CAAgC,GAAI,CAAE,KAAM,sBAAuB,CAAC,EACpE,GAAA,EAAA,EAAA,OAAA,CAAsB,EAAG,CAAE,KAAM,oBAAqB,CAAC,EAEvD,EAAuB,EAAM,aAAe,CAChD,EAAa,OACf,CAAC,EAEK,GAAA,EAAA,EAAA,SAAA,KAA6B,EAAM,QAAU,EAAe,MAAO,CAAE,KAAM,mBAAoB,CAAC,EAEhG,GAAA,EAAA,EAAA,SAAA,MAGF,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,QAAQ,EACrB,EAAqB,EACrB,EAAM,QAAQ,EACd,EAAe,QAAQ,EACvB,EAAY,QAAQ,EACpB,EAAQ,QAAQ,EAChB,EAAa,QAAQ,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,EAGD,MAAO,CAAE,GAFK,EAAa,EAAO,CAAE,SAAU,EAAQ,QAAS,CAEnD,EAAO,OAAM,CAC3B"}
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"}
@@ -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,CA8FvG;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"}
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.dispose(), m(), d.dispose(), f.dispose(), h.dispose(), g.dispose(), p.dispose();
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 };
@@ -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.dispose();\n unsubscribeMutations();\n query.dispose();\n committedQuery.dispose();\n isSearching.dispose();\n results.dispose();\n indexVersion.dispose();\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 { ...state, index };\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;EAUvB,AATA,IAAa,IACb,EAAG,MAAM,GACT,EAAY,GACZ,EAAa,QAAQ,GACrB,EAAqB,GACrB,EAAM,QAAQ,GACd,EAAe,QAAQ,GACvB,EAAY,QAAQ,GACpB,EAAQ,QAAQ,GAChB,EAAa,QAAQ;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;CAGD,OAAO;EAAE,GAFK,EAAa,GAAO,EAAE,UAAU,EAAQ,SAAS,CAEnD;EAAO;CAAM;AAC3B"}
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