@topgunbuild/react 0.7.0 → 0.8.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/dist/index.d.mts +199 -3
- package/dist/index.d.ts +199 -3
- package/dist/index.js +177 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +175 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- package/LICENSE +0 -97
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
2
|
import * as _topgunbuild_client from '@topgunbuild/client';
|
|
3
|
-
import { TopGunClient, ChangeEvent, QueryResultItem, QueryFilter, TopicCallback, RegisterResult, ResolverInfo } from '@topgunbuild/client';
|
|
4
|
-
import { LWWMap, ORMap, EntryProcessorResult, EntryProcessorDef, JournalEventType, JournalEvent, MergeRejection, ConflictResolverDef } from '@topgunbuild/core';
|
|
3
|
+
import { TopGunClient, ChangeEvent, QueryResultItem, QueryFilter, TopicCallback, RegisterResult, ResolverInfo, SearchResult, HybridResultItem, HybridQueryFilter } from '@topgunbuild/client';
|
|
4
|
+
import { LWWMap, ORMap, EntryProcessorResult, EntryProcessorDef, JournalEventType, JournalEvent, MergeRejection, ConflictResolverDef, SearchOptions } from '@topgunbuild/core';
|
|
5
5
|
|
|
6
6
|
interface TopGunProviderProps {
|
|
7
7
|
client: TopGunClient;
|
|
@@ -607,4 +607,200 @@ interface UseConflictResolverResult {
|
|
|
607
607
|
*/
|
|
608
608
|
declare function useConflictResolver(mapName: string, options?: UseConflictResolverOptions): UseConflictResolverResult;
|
|
609
609
|
|
|
610
|
-
|
|
610
|
+
/**
|
|
611
|
+
* Extended search options for useSearch hook.
|
|
612
|
+
*/
|
|
613
|
+
interface UseSearchOptions extends SearchOptions {
|
|
614
|
+
/**
|
|
615
|
+
* Debounce delay in milliseconds.
|
|
616
|
+
* If specified, query changes will be debounced before sending to the server.
|
|
617
|
+
* Useful for search-as-you-type interfaces.
|
|
618
|
+
*/
|
|
619
|
+
debounceMs?: number;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Result type for the useSearch hook.
|
|
623
|
+
*/
|
|
624
|
+
interface UseSearchResult<T> {
|
|
625
|
+
/** Current search results sorted by relevance */
|
|
626
|
+
results: SearchResult<T>[];
|
|
627
|
+
/** True while waiting for initial results */
|
|
628
|
+
loading: boolean;
|
|
629
|
+
/** Error if search failed */
|
|
630
|
+
error: Error | null;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* React hook for live full-text search with real-time updates.
|
|
634
|
+
*
|
|
635
|
+
* Creates a search subscription that receives delta updates when documents
|
|
636
|
+
* matching the query are added, updated, or removed. Results are automatically
|
|
637
|
+
* sorted by BM25 relevance score.
|
|
638
|
+
*
|
|
639
|
+
* @param mapName - Name of the map to search
|
|
640
|
+
* @param query - Search query text
|
|
641
|
+
* @param options - Search options (limit, minScore, boost, debounceMs)
|
|
642
|
+
* @returns Object containing results, loading state, and error
|
|
643
|
+
*
|
|
644
|
+
* @example Basic usage
|
|
645
|
+
* ```tsx
|
|
646
|
+
* function SearchResults() {
|
|
647
|
+
* const [searchTerm, setSearchTerm] = useState('');
|
|
648
|
+
* const { results, loading } = useSearch<Article>('articles', searchTerm, {
|
|
649
|
+
* limit: 20,
|
|
650
|
+
* boost: { title: 2.0 }
|
|
651
|
+
* });
|
|
652
|
+
*
|
|
653
|
+
* if (loading) return <Spinner />;
|
|
654
|
+
*
|
|
655
|
+
* return (
|
|
656
|
+
* <ul>
|
|
657
|
+
* {results.map(r => (
|
|
658
|
+
* <li key={r.key}>
|
|
659
|
+
* [{r.score.toFixed(2)}] {r.value.title}
|
|
660
|
+
* </li>
|
|
661
|
+
* ))}
|
|
662
|
+
* </ul>
|
|
663
|
+
* );
|
|
664
|
+
* }
|
|
665
|
+
* ```
|
|
666
|
+
*
|
|
667
|
+
* @example With debounce for search-as-you-type
|
|
668
|
+
* ```tsx
|
|
669
|
+
* function SearchInput() {
|
|
670
|
+
* const [input, setInput] = useState('');
|
|
671
|
+
* const { results, loading, error } = useSearch<Product>('products', input, {
|
|
672
|
+
* debounceMs: 300,
|
|
673
|
+
* limit: 10
|
|
674
|
+
* });
|
|
675
|
+
*
|
|
676
|
+
* return (
|
|
677
|
+
* <div>
|
|
678
|
+
* <input
|
|
679
|
+
* value={input}
|
|
680
|
+
* onChange={(e) => setInput(e.target.value)}
|
|
681
|
+
* placeholder="Search products..."
|
|
682
|
+
* />
|
|
683
|
+
* {loading && <span>Searching...</span>}
|
|
684
|
+
* {error && <span className="error">{error.message}</span>}
|
|
685
|
+
* <ul>
|
|
686
|
+
* {results.map(r => (
|
|
687
|
+
* <li key={r.key}>{r.value.name}</li>
|
|
688
|
+
* ))}
|
|
689
|
+
* </ul>
|
|
690
|
+
* </div>
|
|
691
|
+
* );
|
|
692
|
+
* }
|
|
693
|
+
* ```
|
|
694
|
+
*/
|
|
695
|
+
declare function useSearch<T = unknown>(mapName: string, query: string, options?: UseSearchOptions): UseSearchResult<T>;
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Extended options for useHybridQuery hook.
|
|
699
|
+
*/
|
|
700
|
+
interface UseHybridQueryOptions {
|
|
701
|
+
/**
|
|
702
|
+
* Whether to skip the query (don't execute).
|
|
703
|
+
* Useful for conditional queries.
|
|
704
|
+
*/
|
|
705
|
+
skip?: boolean;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Result type for the useHybridQuery hook.
|
|
709
|
+
*/
|
|
710
|
+
interface UseHybridQueryResult<T> {
|
|
711
|
+
/** Current query results with _key, value, _score, _matchedTerms */
|
|
712
|
+
results: HybridResultItem<T>[];
|
|
713
|
+
/** True while waiting for initial results */
|
|
714
|
+
loading: boolean;
|
|
715
|
+
/** Error if query failed */
|
|
716
|
+
error: Error | null;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* React hook for hybrid queries combining FTS with traditional filters.
|
|
720
|
+
*
|
|
721
|
+
* Creates a subscription that receives live updates when documents
|
|
722
|
+
* matching the query change. Results include relevance scores for FTS predicates.
|
|
723
|
+
*
|
|
724
|
+
* @param mapName - Name of the map to query
|
|
725
|
+
* @param filter - Hybrid query filter with predicate, where, sort, limit, offset
|
|
726
|
+
* @param options - Hook options (skip)
|
|
727
|
+
* @returns Object containing results, loading state, and error
|
|
728
|
+
*
|
|
729
|
+
* @example Basic hybrid query (FTS + filter)
|
|
730
|
+
* ```tsx
|
|
731
|
+
* import { Predicates } from '@topgunbuild/core';
|
|
732
|
+
*
|
|
733
|
+
* function TechArticles() {
|
|
734
|
+
* const { results, loading } = useHybridQuery<Article>('articles', {
|
|
735
|
+
* predicate: Predicates.and(
|
|
736
|
+
* Predicates.match('body', 'machine learning'),
|
|
737
|
+
* Predicates.equal('category', 'tech')
|
|
738
|
+
* ),
|
|
739
|
+
* sort: { _score: 'desc' },
|
|
740
|
+
* limit: 20
|
|
741
|
+
* });
|
|
742
|
+
*
|
|
743
|
+
* if (loading) return <Spinner />;
|
|
744
|
+
*
|
|
745
|
+
* return (
|
|
746
|
+
* <ul>
|
|
747
|
+
* {results.map(r => (
|
|
748
|
+
* <li key={r._key}>
|
|
749
|
+
* [{r._score?.toFixed(2)}] {r.value.title}
|
|
750
|
+
* </li>
|
|
751
|
+
* ))}
|
|
752
|
+
* </ul>
|
|
753
|
+
* );
|
|
754
|
+
* }
|
|
755
|
+
* ```
|
|
756
|
+
*
|
|
757
|
+
* @example With dynamic filter
|
|
758
|
+
* ```tsx
|
|
759
|
+
* function SearchWithFilters() {
|
|
760
|
+
* const [searchTerm, setSearchTerm] = useState('');
|
|
761
|
+
* const [category, setCategory] = useState('all');
|
|
762
|
+
*
|
|
763
|
+
* const filter = useMemo(() => ({
|
|
764
|
+
* predicate: searchTerm
|
|
765
|
+
* ? category !== 'all'
|
|
766
|
+
* ? Predicates.and(
|
|
767
|
+
* Predicates.match('body', searchTerm),
|
|
768
|
+
* Predicates.equal('category', category)
|
|
769
|
+
* )
|
|
770
|
+
* : Predicates.match('body', searchTerm)
|
|
771
|
+
* : category !== 'all'
|
|
772
|
+
* ? Predicates.equal('category', category)
|
|
773
|
+
* : undefined,
|
|
774
|
+
* sort: searchTerm ? { _score: 'desc' } : { createdAt: 'desc' },
|
|
775
|
+
* limit: 20
|
|
776
|
+
* }), [searchTerm, category]);
|
|
777
|
+
*
|
|
778
|
+
* const { results, loading, error } = useHybridQuery<Article>('articles', filter);
|
|
779
|
+
*
|
|
780
|
+
* return (
|
|
781
|
+
* <div>
|
|
782
|
+
* <input
|
|
783
|
+
* value={searchTerm}
|
|
784
|
+
* onChange={(e) => setSearchTerm(e.target.value)}
|
|
785
|
+
* placeholder="Search..."
|
|
786
|
+
* />
|
|
787
|
+
* <select value={category} onChange={(e) => setCategory(e.target.value)}>
|
|
788
|
+
* <option value="all">All</option>
|
|
789
|
+
* <option value="tech">Tech</option>
|
|
790
|
+
* <option value="science">Science</option>
|
|
791
|
+
* </select>
|
|
792
|
+
* {loading && <span>Loading...</span>}
|
|
793
|
+
* {error && <span className="error">{error.message}</span>}
|
|
794
|
+
* <ul>
|
|
795
|
+
* {results.map(r => (
|
|
796
|
+
* <li key={r._key}>{r.value.title}</li>
|
|
797
|
+
* ))}
|
|
798
|
+
* </ul>
|
|
799
|
+
* </div>
|
|
800
|
+
* );
|
|
801
|
+
* }
|
|
802
|
+
* ```
|
|
803
|
+
*/
|
|
804
|
+
declare function useHybridQuery<T = unknown>(mapName: string, filter?: HybridQueryFilter, options?: UseHybridQueryOptions): UseHybridQueryResult<T>;
|
|
805
|
+
|
|
806
|
+
export { TopGunProvider, type TopGunProviderProps, type UseConflictResolverOptions, type UseConflictResolverResult, type UseEntryProcessorOptions, type UseEntryProcessorResult, type UseEventJournalOptions, type UseEventJournalResult, type UseHybridQueryOptions, type UseHybridQueryResult, type UseMergeRejectionsOptions, type UseMergeRejectionsResult, type UseMutationResult, type UsePNCounterResult, type UseQueryOptions, type UseQueryResult, type UseSearchOptions, type UseSearchResult, useClient, useConflictResolver, useEntryProcessor, useEventJournal, useHybridQuery, useMap, useMergeRejections, useMutation, useORMap, usePNCounter, useQuery, useSearch, useTopic };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
2
|
import * as _topgunbuild_client from '@topgunbuild/client';
|
|
3
|
-
import { TopGunClient, ChangeEvent, QueryResultItem, QueryFilter, TopicCallback, RegisterResult, ResolverInfo } from '@topgunbuild/client';
|
|
4
|
-
import { LWWMap, ORMap, EntryProcessorResult, EntryProcessorDef, JournalEventType, JournalEvent, MergeRejection, ConflictResolverDef } from '@topgunbuild/core';
|
|
3
|
+
import { TopGunClient, ChangeEvent, QueryResultItem, QueryFilter, TopicCallback, RegisterResult, ResolverInfo, SearchResult, HybridResultItem, HybridQueryFilter } from '@topgunbuild/client';
|
|
4
|
+
import { LWWMap, ORMap, EntryProcessorResult, EntryProcessorDef, JournalEventType, JournalEvent, MergeRejection, ConflictResolverDef, SearchOptions } from '@topgunbuild/core';
|
|
5
5
|
|
|
6
6
|
interface TopGunProviderProps {
|
|
7
7
|
client: TopGunClient;
|
|
@@ -607,4 +607,200 @@ interface UseConflictResolverResult {
|
|
|
607
607
|
*/
|
|
608
608
|
declare function useConflictResolver(mapName: string, options?: UseConflictResolverOptions): UseConflictResolverResult;
|
|
609
609
|
|
|
610
|
-
|
|
610
|
+
/**
|
|
611
|
+
* Extended search options for useSearch hook.
|
|
612
|
+
*/
|
|
613
|
+
interface UseSearchOptions extends SearchOptions {
|
|
614
|
+
/**
|
|
615
|
+
* Debounce delay in milliseconds.
|
|
616
|
+
* If specified, query changes will be debounced before sending to the server.
|
|
617
|
+
* Useful for search-as-you-type interfaces.
|
|
618
|
+
*/
|
|
619
|
+
debounceMs?: number;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Result type for the useSearch hook.
|
|
623
|
+
*/
|
|
624
|
+
interface UseSearchResult<T> {
|
|
625
|
+
/** Current search results sorted by relevance */
|
|
626
|
+
results: SearchResult<T>[];
|
|
627
|
+
/** True while waiting for initial results */
|
|
628
|
+
loading: boolean;
|
|
629
|
+
/** Error if search failed */
|
|
630
|
+
error: Error | null;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* React hook for live full-text search with real-time updates.
|
|
634
|
+
*
|
|
635
|
+
* Creates a search subscription that receives delta updates when documents
|
|
636
|
+
* matching the query are added, updated, or removed. Results are automatically
|
|
637
|
+
* sorted by BM25 relevance score.
|
|
638
|
+
*
|
|
639
|
+
* @param mapName - Name of the map to search
|
|
640
|
+
* @param query - Search query text
|
|
641
|
+
* @param options - Search options (limit, minScore, boost, debounceMs)
|
|
642
|
+
* @returns Object containing results, loading state, and error
|
|
643
|
+
*
|
|
644
|
+
* @example Basic usage
|
|
645
|
+
* ```tsx
|
|
646
|
+
* function SearchResults() {
|
|
647
|
+
* const [searchTerm, setSearchTerm] = useState('');
|
|
648
|
+
* const { results, loading } = useSearch<Article>('articles', searchTerm, {
|
|
649
|
+
* limit: 20,
|
|
650
|
+
* boost: { title: 2.0 }
|
|
651
|
+
* });
|
|
652
|
+
*
|
|
653
|
+
* if (loading) return <Spinner />;
|
|
654
|
+
*
|
|
655
|
+
* return (
|
|
656
|
+
* <ul>
|
|
657
|
+
* {results.map(r => (
|
|
658
|
+
* <li key={r.key}>
|
|
659
|
+
* [{r.score.toFixed(2)}] {r.value.title}
|
|
660
|
+
* </li>
|
|
661
|
+
* ))}
|
|
662
|
+
* </ul>
|
|
663
|
+
* );
|
|
664
|
+
* }
|
|
665
|
+
* ```
|
|
666
|
+
*
|
|
667
|
+
* @example With debounce for search-as-you-type
|
|
668
|
+
* ```tsx
|
|
669
|
+
* function SearchInput() {
|
|
670
|
+
* const [input, setInput] = useState('');
|
|
671
|
+
* const { results, loading, error } = useSearch<Product>('products', input, {
|
|
672
|
+
* debounceMs: 300,
|
|
673
|
+
* limit: 10
|
|
674
|
+
* });
|
|
675
|
+
*
|
|
676
|
+
* return (
|
|
677
|
+
* <div>
|
|
678
|
+
* <input
|
|
679
|
+
* value={input}
|
|
680
|
+
* onChange={(e) => setInput(e.target.value)}
|
|
681
|
+
* placeholder="Search products..."
|
|
682
|
+
* />
|
|
683
|
+
* {loading && <span>Searching...</span>}
|
|
684
|
+
* {error && <span className="error">{error.message}</span>}
|
|
685
|
+
* <ul>
|
|
686
|
+
* {results.map(r => (
|
|
687
|
+
* <li key={r.key}>{r.value.name}</li>
|
|
688
|
+
* ))}
|
|
689
|
+
* </ul>
|
|
690
|
+
* </div>
|
|
691
|
+
* );
|
|
692
|
+
* }
|
|
693
|
+
* ```
|
|
694
|
+
*/
|
|
695
|
+
declare function useSearch<T = unknown>(mapName: string, query: string, options?: UseSearchOptions): UseSearchResult<T>;
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Extended options for useHybridQuery hook.
|
|
699
|
+
*/
|
|
700
|
+
interface UseHybridQueryOptions {
|
|
701
|
+
/**
|
|
702
|
+
* Whether to skip the query (don't execute).
|
|
703
|
+
* Useful for conditional queries.
|
|
704
|
+
*/
|
|
705
|
+
skip?: boolean;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Result type for the useHybridQuery hook.
|
|
709
|
+
*/
|
|
710
|
+
interface UseHybridQueryResult<T> {
|
|
711
|
+
/** Current query results with _key, value, _score, _matchedTerms */
|
|
712
|
+
results: HybridResultItem<T>[];
|
|
713
|
+
/** True while waiting for initial results */
|
|
714
|
+
loading: boolean;
|
|
715
|
+
/** Error if query failed */
|
|
716
|
+
error: Error | null;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* React hook for hybrid queries combining FTS with traditional filters.
|
|
720
|
+
*
|
|
721
|
+
* Creates a subscription that receives live updates when documents
|
|
722
|
+
* matching the query change. Results include relevance scores for FTS predicates.
|
|
723
|
+
*
|
|
724
|
+
* @param mapName - Name of the map to query
|
|
725
|
+
* @param filter - Hybrid query filter with predicate, where, sort, limit, offset
|
|
726
|
+
* @param options - Hook options (skip)
|
|
727
|
+
* @returns Object containing results, loading state, and error
|
|
728
|
+
*
|
|
729
|
+
* @example Basic hybrid query (FTS + filter)
|
|
730
|
+
* ```tsx
|
|
731
|
+
* import { Predicates } from '@topgunbuild/core';
|
|
732
|
+
*
|
|
733
|
+
* function TechArticles() {
|
|
734
|
+
* const { results, loading } = useHybridQuery<Article>('articles', {
|
|
735
|
+
* predicate: Predicates.and(
|
|
736
|
+
* Predicates.match('body', 'machine learning'),
|
|
737
|
+
* Predicates.equal('category', 'tech')
|
|
738
|
+
* ),
|
|
739
|
+
* sort: { _score: 'desc' },
|
|
740
|
+
* limit: 20
|
|
741
|
+
* });
|
|
742
|
+
*
|
|
743
|
+
* if (loading) return <Spinner />;
|
|
744
|
+
*
|
|
745
|
+
* return (
|
|
746
|
+
* <ul>
|
|
747
|
+
* {results.map(r => (
|
|
748
|
+
* <li key={r._key}>
|
|
749
|
+
* [{r._score?.toFixed(2)}] {r.value.title}
|
|
750
|
+
* </li>
|
|
751
|
+
* ))}
|
|
752
|
+
* </ul>
|
|
753
|
+
* );
|
|
754
|
+
* }
|
|
755
|
+
* ```
|
|
756
|
+
*
|
|
757
|
+
* @example With dynamic filter
|
|
758
|
+
* ```tsx
|
|
759
|
+
* function SearchWithFilters() {
|
|
760
|
+
* const [searchTerm, setSearchTerm] = useState('');
|
|
761
|
+
* const [category, setCategory] = useState('all');
|
|
762
|
+
*
|
|
763
|
+
* const filter = useMemo(() => ({
|
|
764
|
+
* predicate: searchTerm
|
|
765
|
+
* ? category !== 'all'
|
|
766
|
+
* ? Predicates.and(
|
|
767
|
+
* Predicates.match('body', searchTerm),
|
|
768
|
+
* Predicates.equal('category', category)
|
|
769
|
+
* )
|
|
770
|
+
* : Predicates.match('body', searchTerm)
|
|
771
|
+
* : category !== 'all'
|
|
772
|
+
* ? Predicates.equal('category', category)
|
|
773
|
+
* : undefined,
|
|
774
|
+
* sort: searchTerm ? { _score: 'desc' } : { createdAt: 'desc' },
|
|
775
|
+
* limit: 20
|
|
776
|
+
* }), [searchTerm, category]);
|
|
777
|
+
*
|
|
778
|
+
* const { results, loading, error } = useHybridQuery<Article>('articles', filter);
|
|
779
|
+
*
|
|
780
|
+
* return (
|
|
781
|
+
* <div>
|
|
782
|
+
* <input
|
|
783
|
+
* value={searchTerm}
|
|
784
|
+
* onChange={(e) => setSearchTerm(e.target.value)}
|
|
785
|
+
* placeholder="Search..."
|
|
786
|
+
* />
|
|
787
|
+
* <select value={category} onChange={(e) => setCategory(e.target.value)}>
|
|
788
|
+
* <option value="all">All</option>
|
|
789
|
+
* <option value="tech">Tech</option>
|
|
790
|
+
* <option value="science">Science</option>
|
|
791
|
+
* </select>
|
|
792
|
+
* {loading && <span>Loading...</span>}
|
|
793
|
+
* {error && <span className="error">{error.message}</span>}
|
|
794
|
+
* <ul>
|
|
795
|
+
* {results.map(r => (
|
|
796
|
+
* <li key={r._key}>{r.value.title}</li>
|
|
797
|
+
* ))}
|
|
798
|
+
* </ul>
|
|
799
|
+
* </div>
|
|
800
|
+
* );
|
|
801
|
+
* }
|
|
802
|
+
* ```
|
|
803
|
+
*/
|
|
804
|
+
declare function useHybridQuery<T = unknown>(mapName: string, filter?: HybridQueryFilter, options?: UseHybridQueryOptions): UseHybridQueryResult<T>;
|
|
805
|
+
|
|
806
|
+
export { TopGunProvider, type TopGunProviderProps, type UseConflictResolverOptions, type UseConflictResolverResult, type UseEntryProcessorOptions, type UseEntryProcessorResult, type UseEventJournalOptions, type UseEventJournalResult, type UseHybridQueryOptions, type UseHybridQueryResult, type UseMergeRejectionsOptions, type UseMergeRejectionsResult, type UseMutationResult, type UsePNCounterResult, type UseQueryOptions, type UseQueryResult, type UseSearchOptions, type UseSearchResult, useClient, useConflictResolver, useEntryProcessor, useEventJournal, useHybridQuery, useMap, useMergeRejections, useMutation, useORMap, usePNCounter, useQuery, useSearch, useTopic };
|
package/dist/index.js
CHANGED
|
@@ -25,12 +25,14 @@ __export(index_exports, {
|
|
|
25
25
|
useConflictResolver: () => useConflictResolver,
|
|
26
26
|
useEntryProcessor: () => useEntryProcessor,
|
|
27
27
|
useEventJournal: () => useEventJournal,
|
|
28
|
+
useHybridQuery: () => useHybridQuery,
|
|
28
29
|
useMap: () => useMap,
|
|
29
30
|
useMergeRejections: () => useMergeRejections,
|
|
30
31
|
useMutation: () => useMutation,
|
|
31
32
|
useORMap: () => useORMap,
|
|
32
33
|
usePNCounter: () => usePNCounter,
|
|
33
34
|
useQuery: () => useQuery,
|
|
35
|
+
useSearch: () => useSearch,
|
|
34
36
|
useTopic: () => useTopic
|
|
35
37
|
});
|
|
36
38
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -540,6 +542,179 @@ function useConflictResolver(mapName, options = {}) {
|
|
|
540
542
|
registered
|
|
541
543
|
};
|
|
542
544
|
}
|
|
545
|
+
|
|
546
|
+
// src/hooks/useSearch.ts
|
|
547
|
+
var import_react12 = require("react");
|
|
548
|
+
function useSearch(mapName, query, options) {
|
|
549
|
+
const client = useClient();
|
|
550
|
+
const [results, setResults] = (0, import_react12.useState)([]);
|
|
551
|
+
const [loading, setLoading] = (0, import_react12.useState)(true);
|
|
552
|
+
const [error, setError] = (0, import_react12.useState)(null);
|
|
553
|
+
const isMounted = (0, import_react12.useRef)(true);
|
|
554
|
+
const handleRef = (0, import_react12.useRef)(null);
|
|
555
|
+
const unsubscribeRef = (0, import_react12.useRef)(null);
|
|
556
|
+
const debounceTimeoutRef = (0, import_react12.useRef)(null);
|
|
557
|
+
const debounceMs = options?.debounceMs;
|
|
558
|
+
const searchOptions = (0, import_react12.useMemo)(() => {
|
|
559
|
+
if (!options) return {};
|
|
560
|
+
const { debounceMs: _, ...opts } = options;
|
|
561
|
+
return opts;
|
|
562
|
+
}, [options?.limit, options?.minScore, options?.boost]);
|
|
563
|
+
const [debouncedQuery, setDebouncedQuery] = (0, import_react12.useState)(query);
|
|
564
|
+
const isFirstQuery = (0, import_react12.useRef)(true);
|
|
565
|
+
(0, import_react12.useEffect)(() => {
|
|
566
|
+
if (debounceMs != null && debounceMs > 0) {
|
|
567
|
+
if (debounceTimeoutRef.current) {
|
|
568
|
+
clearTimeout(debounceTimeoutRef.current);
|
|
569
|
+
}
|
|
570
|
+
debounceTimeoutRef.current = setTimeout(() => {
|
|
571
|
+
if (isMounted.current) {
|
|
572
|
+
setDebouncedQuery(query);
|
|
573
|
+
}
|
|
574
|
+
}, debounceMs);
|
|
575
|
+
return () => {
|
|
576
|
+
if (debounceTimeoutRef.current) {
|
|
577
|
+
clearTimeout(debounceTimeoutRef.current);
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
} else {
|
|
581
|
+
setDebouncedQuery(query);
|
|
582
|
+
}
|
|
583
|
+
}, [query, debounceMs]);
|
|
584
|
+
(0, import_react12.useEffect)(() => {
|
|
585
|
+
isMounted.current = true;
|
|
586
|
+
isFirstQuery.current = true;
|
|
587
|
+
return () => {
|
|
588
|
+
isMounted.current = false;
|
|
589
|
+
if (unsubscribeRef.current) {
|
|
590
|
+
unsubscribeRef.current();
|
|
591
|
+
unsubscribeRef.current = null;
|
|
592
|
+
}
|
|
593
|
+
if (handleRef.current) {
|
|
594
|
+
handleRef.current.dispose();
|
|
595
|
+
handleRef.current = null;
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
}, [client, mapName]);
|
|
599
|
+
(0, import_react12.useEffect)(() => {
|
|
600
|
+
if (!debouncedQuery.trim()) {
|
|
601
|
+
setResults([]);
|
|
602
|
+
setLoading(false);
|
|
603
|
+
setError(null);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
setLoading(true);
|
|
607
|
+
setError(null);
|
|
608
|
+
try {
|
|
609
|
+
if (handleRef.current && !isFirstQuery.current) {
|
|
610
|
+
handleRef.current.setQuery(debouncedQuery);
|
|
611
|
+
} else {
|
|
612
|
+
if (handleRef.current) {
|
|
613
|
+
if (unsubscribeRef.current) {
|
|
614
|
+
unsubscribeRef.current();
|
|
615
|
+
}
|
|
616
|
+
handleRef.current.dispose();
|
|
617
|
+
}
|
|
618
|
+
const handle = client.searchSubscribe(mapName, debouncedQuery, searchOptions);
|
|
619
|
+
handleRef.current = handle;
|
|
620
|
+
isFirstQuery.current = false;
|
|
621
|
+
let hasReceivedResults = false;
|
|
622
|
+
unsubscribeRef.current = handle.subscribe((newResults) => {
|
|
623
|
+
if (isMounted.current) {
|
|
624
|
+
setResults(newResults);
|
|
625
|
+
if (!hasReceivedResults) {
|
|
626
|
+
hasReceivedResults = true;
|
|
627
|
+
setLoading(false);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
} catch (err) {
|
|
633
|
+
if (isMounted.current) {
|
|
634
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
635
|
+
setLoading(false);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}, [client, mapName, debouncedQuery, searchOptions]);
|
|
639
|
+
(0, import_react12.useEffect)(() => {
|
|
640
|
+
if (!handleRef.current || isFirstQuery.current) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
handleRef.current.setOptions(searchOptions);
|
|
644
|
+
}, [searchOptions]);
|
|
645
|
+
return (0, import_react12.useMemo)(
|
|
646
|
+
() => ({ results, loading, error }),
|
|
647
|
+
[results, loading, error]
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// src/hooks/useHybridQuery.ts
|
|
652
|
+
var import_react13 = require("react");
|
|
653
|
+
function useHybridQuery(mapName, filter = {}, options) {
|
|
654
|
+
const client = useClient();
|
|
655
|
+
const [results, setResults] = (0, import_react13.useState)([]);
|
|
656
|
+
const [loading, setLoading] = (0, import_react13.useState)(true);
|
|
657
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
658
|
+
const isMounted = (0, import_react13.useRef)(true);
|
|
659
|
+
const handleRef = (0, import_react13.useRef)(null);
|
|
660
|
+
const unsubscribeRef = (0, import_react13.useRef)(null);
|
|
661
|
+
const memoizedFilter = (0, import_react13.useMemo)(() => filter, [
|
|
662
|
+
JSON.stringify(filter.predicate),
|
|
663
|
+
JSON.stringify(filter.where),
|
|
664
|
+
JSON.stringify(filter.sort),
|
|
665
|
+
filter.limit,
|
|
666
|
+
filter.offset
|
|
667
|
+
]);
|
|
668
|
+
const skip = options?.skip ?? false;
|
|
669
|
+
(0, import_react13.useEffect)(() => {
|
|
670
|
+
isMounted.current = true;
|
|
671
|
+
if (skip) {
|
|
672
|
+
setResults([]);
|
|
673
|
+
setLoading(false);
|
|
674
|
+
setError(null);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
setLoading(true);
|
|
678
|
+
setError(null);
|
|
679
|
+
try {
|
|
680
|
+
if (handleRef.current) {
|
|
681
|
+
if (unsubscribeRef.current) {
|
|
682
|
+
unsubscribeRef.current();
|
|
683
|
+
unsubscribeRef.current = null;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const handle = client.hybridQuery(mapName, memoizedFilter);
|
|
687
|
+
handleRef.current = handle;
|
|
688
|
+
let hasReceivedResults = false;
|
|
689
|
+
unsubscribeRef.current = handle.subscribe((newResults) => {
|
|
690
|
+
if (isMounted.current) {
|
|
691
|
+
setResults(newResults);
|
|
692
|
+
if (!hasReceivedResults) {
|
|
693
|
+
hasReceivedResults = true;
|
|
694
|
+
setLoading(false);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
} catch (err) {
|
|
699
|
+
if (isMounted.current) {
|
|
700
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
701
|
+
setLoading(false);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return () => {
|
|
705
|
+
isMounted.current = false;
|
|
706
|
+
if (unsubscribeRef.current) {
|
|
707
|
+
unsubscribeRef.current();
|
|
708
|
+
unsubscribeRef.current = null;
|
|
709
|
+
}
|
|
710
|
+
handleRef.current = null;
|
|
711
|
+
};
|
|
712
|
+
}, [client, mapName, memoizedFilter, skip]);
|
|
713
|
+
return (0, import_react13.useMemo)(
|
|
714
|
+
() => ({ results, loading, error }),
|
|
715
|
+
[results, loading, error]
|
|
716
|
+
);
|
|
717
|
+
}
|
|
543
718
|
// Annotate the CommonJS export names for ESM import in node:
|
|
544
719
|
0 && (module.exports = {
|
|
545
720
|
TopGunProvider,
|
|
@@ -547,12 +722,14 @@ function useConflictResolver(mapName, options = {}) {
|
|
|
547
722
|
useConflictResolver,
|
|
548
723
|
useEntryProcessor,
|
|
549
724
|
useEventJournal,
|
|
725
|
+
useHybridQuery,
|
|
550
726
|
useMap,
|
|
551
727
|
useMergeRejections,
|
|
552
728
|
useMutation,
|
|
553
729
|
useORMap,
|
|
554
730
|
usePNCounter,
|
|
555
731
|
useQuery,
|
|
732
|
+
useSearch,
|
|
556
733
|
useTopic
|
|
557
734
|
});
|
|
558
735
|
//# sourceMappingURL=index.js.map
|