11'use client' ;
22
33import SearchBox from '@node-core/ui-components/Common/Search' ;
4- import useOrama from '@node-core/ui-components/hooks/useOrama' ;
5- import { create , insertMultiple , save } from '@orama/orama' ;
4+ import { create , insertMultiple , search } from '@orama/orama' ;
65import { useTranslations } from 'next-intl' ;
6+ import { useMemo , useRef } from 'react' ;
77
88import { ORAMA_DB_URLS } from '#site/next.constants.mjs' ;
99
10+ import type { OramaCloud } from '@orama/core' ;
11+ import type { AnyOrama } from '@orama/orama' ;
1012import type { FC } from 'react' ;
1113
12- /**
13- * Shape of a single Orama document entry.
14- * `href` is required (we prefix it); other fields are passthrough.
15- */
1614type OramaDoc = { href : string } & Record < string , unknown > ;
1715
18- /**
19- * Shape of a serialized Orama database snapshot (from `save()` on the server
20- * side, fetched as JSON on the client). We only type the parts we touch.
21- */
2216type SerializedOramaDb = {
2317 docs : {
2418 docs : Record < string , OramaDoc > ;
2519 } ;
2620} ;
2721
28- /**
29- * Each locale/section of the site ships its own prebuilt Orama index, but the
30- * hrefs inside those indexes are relative to that section's root. When we
31- * merge multiple indexes into a single client-side DB, we need to re-scope
32- * those hrefs so clicks route to the correct top-level path.
33- */
3422export const addPrefixToDocs = < T extends SerializedOramaDb > (
3523 db : T ,
3624 prefix : string
3725) : T => {
3826 const prefixedDocs : Record < string , OramaDoc > = { } ;
3927
40- // Object.entries + Object.fromEntries would also work, but a single pass
41- // with a plain loop avoids the intermediate array allocations
4228 for ( const [ id , doc ] of Object . entries ( db . docs . docs ) ) {
4329 prefixedDocs [ id ] = { ...doc , href : `${ prefix } ${ doc . href } ` } ;
4430 }
@@ -49,35 +35,47 @@ export const addPrefixToDocs = <T extends SerializedOramaDb>(
4935 } ;
5036} ;
5137
52- const loadOrama = async ( ) => {
53- const db = create ( {
54- schema : {
55- title : 'string' ,
56- description : 'string' ,
57- href : 'string' ,
58- siteSection : 'string' ,
59- } ,
60- } ) ;
61-
38+ const loadOrama = async ( db : AnyOrama ) : Promise < void > => {
6239 const indexes = await Promise . all (
6340 Object . entries ( ORAMA_DB_URLS ) . map ( async ( [ key , url ] ) => {
6441 const response = await fetch ( url ) ;
6542 const fetchedDb = ( await response . json ( ) ) as SerializedOramaDb ;
43+
6644 return addPrefixToDocs ( fetchedDb , `/${ key } ` ) ;
6745 } )
6846 ) ;
6947
7048 for ( const index of indexes ) {
7149 await insertMultiple ( db , Object . values ( index . docs . docs ) as Array < never > ) ;
7250 }
51+ } ;
7352
74- return save ( db ) ;
53+ export const useOrama = ( ) => {
54+ const loadPromiseRef = useRef < Promise < void > | null > ( null ) ;
55+
56+ return useMemo ( ( ) => {
57+ const db = create ( {
58+ schema : {
59+ title : 'string' ,
60+ description : 'string' ,
61+ href : 'string' ,
62+ siteSection : 'string' ,
63+ } ,
64+ } ) ;
65+
66+ // @ts -expect-error We are overriding a method, an error is expected.
67+ db . search = async options => {
68+ await ( loadPromiseRef . current ??= loadOrama ( db ) ) ;
69+ return search ( db , options ) ;
70+ } ;
71+
72+ return db ;
73+ } , [ ] ) as unknown as OramaCloud ;
7574} ;
7675
7776const WithSearch : FC = ( ) => {
7877 const t = useTranslations ( ) ;
79-
80- const client = useOrama ( loadOrama ) ;
78+ const client = useOrama ( ) ;
8179
8280 return (
8381 < SearchBox
0 commit comments