@graphcommerce/docs 3.1.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/{content/framework → framework}/deployment.md +0 -0
- package/{content/framework → framework}/environment-variables.md +0 -0
- package/{content/framework → framework}/favicon.md +0 -0
- package/{content/framework → framework}/graphcms.md +0 -0
- package/{content/framework → framework}/icons.md +0 -0
- package/{content/framework → framework}/readme.md +0 -0
- package/{content/framework → framework}/seo.md +0 -0
- package/{content/framework → framework}/static-file-serving.md +0 -0
- package/{content/framework → framework}/static-generation.md +0 -0
- package/{content/framework → framework}/theming.md +0 -0
- package/{content/framework → framework}/translations.md +0 -0
- package/{content/framework → framework}/troubleshooting.md +0 -0
- package/{content/framework → framework}/typography.md +0 -0
- package/{content/getting-started → getting-started}/create.md +0 -0
- package/{content/getting-started → getting-started}/graphcms-component.md +0 -0
- package/{content/getting-started → getting-started}/header.md +0 -0
- package/{content/getting-started → getting-started}/pages.md +0 -0
- package/{content/getting-started → getting-started}/readme.md +0 -0
- package/{content/getting-started → getting-started}/start-building.md +0 -0
- package/{content/getting-started → getting-started}/vscode.md +0 -0
- package/package.json +6 -51
- package/{content/readme.md → readme.md} +0 -0
- package/{content/roadmap.md → roadmap.md} +0 -0
- package/.babelrc +0 -4
- package/components/Layout/LayoutFull.tsx +0 -85
- package/components/Layout/Logo.tsx +0 -19
- package/components/Layout/graphcommerce.svg +0 -34
- package/components/Search.tsx +0 -37
- package/components/SearchForm.tsx +0 -110
- package/components/SidebarMenu/index.tsx +0 -101
- package/components/prism.css +0 -274
- package/components/rehype-prism-plus.css +0 -49
- package/components/theme.ts +0 -410
- package/lib/DocumentIndexer.ts +0 -59
- package/lib/files.ts +0 -168
- package/lib/instantSearch.ts +0 -26
- package/lib/typesense/IndexerHandler.ts +0 -47
- package/lib/typesense/Leaves.ts +0 -37
- package/lib/typesense/SearchIndexer.ts +0 -64
- package/lib/typesense/batchInterable.ts +0 -13
- package/lib/typesense/createInstantSearchProps.ts +0 -36
- package/lib/typesense/typesenseClientConf.ts +0 -23
- package/lib/typesense/typesenseIndexerHandler.ts +0 -23
- package/next-env.d.ts +0 -5
- package/next.config.js +0 -21
- package/pages/[[...url]].tsx +0 -391
- package/pages/_app.tsx +0 -26
- package/pages/_document.tsx +0 -22
- package/pages/api/reindex.ts +0 -4
- package/pages/menu/[[...url]].tsx +0 -69
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +0 -12
- package/public/link.svg +0 -4
- package/public/manifest/favicon-192.png +0 -0
- package/public/manifest/favicon-512.png +0 -0
- package/public/manifest.webmanifest +0 -20
package/lib/DocumentIndexer.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/* eslint-disable class-methods-use-this */
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import { remark } from 'remark'
|
|
4
|
-
import remarkGfm from 'remark-gfm'
|
|
5
|
-
import strip from 'strip-markdown'
|
|
6
|
-
import { toVFile as vfile } from 'to-vfile'
|
|
7
|
-
import { matter } from 'vfile-matter'
|
|
8
|
-
import { FileNode, findByUrl, getDirectoryPaths, getDirectoryTree } from './files'
|
|
9
|
-
import { indexName } from './instantSearch'
|
|
10
|
-
import { BaseDocument, SearchIndexer } from './typesense/SearchIndexer'
|
|
11
|
-
|
|
12
|
-
export type DocumentationDocument = BaseDocument & {
|
|
13
|
-
url: string
|
|
14
|
-
content: string
|
|
15
|
-
name: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class DocumentIndexer extends SearchIndexer<DocumentationDocument> {
|
|
19
|
-
#root: string
|
|
20
|
-
|
|
21
|
-
constructor(root: string) {
|
|
22
|
-
super({
|
|
23
|
-
name: indexName,
|
|
24
|
-
fields: {
|
|
25
|
-
id: { type: 'string' },
|
|
26
|
-
name: { type: 'string' },
|
|
27
|
-
url: { type: 'string' },
|
|
28
|
-
content: { type: 'string' },
|
|
29
|
-
},
|
|
30
|
-
})
|
|
31
|
-
this.#root = root
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// load a single document
|
|
35
|
-
async #loadDocument(node?: FileNode | false): Promise<DocumentationDocument> {
|
|
36
|
-
if (!node) throw Error('Node not found')
|
|
37
|
-
|
|
38
|
-
const file = path.join(process.cwd(), this.#root, node.path)
|
|
39
|
-
const res = matter(await vfile.read(file), { strip: true })
|
|
40
|
-
const content = (await remark().use(strip).process(res)).value.toString()
|
|
41
|
-
|
|
42
|
-
if (!content) throw Error('Can not load file')
|
|
43
|
-
|
|
44
|
-
const { name, url } = node
|
|
45
|
-
return { id: node.path, content, url, name: node.matter.menu ?? name }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async *all() {
|
|
49
|
-
const paths = await getDirectoryPaths(this.#root)
|
|
50
|
-
const menuData = await getDirectoryTree(this.#root)
|
|
51
|
-
if (!menuData) return false
|
|
52
|
-
|
|
53
|
-
for (const p of paths) {
|
|
54
|
-
yield this.#loadDocument(findByUrl(p.split('/'), menuData))
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return true
|
|
58
|
-
}
|
|
59
|
-
}
|
package/lib/files.ts
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import { toVFile as vfile } from 'to-vfile'
|
|
4
|
-
import { matter } from 'vfile-matter'
|
|
5
|
-
|
|
6
|
-
export type MatterFields = {
|
|
7
|
-
menu?: string
|
|
8
|
-
order?: string
|
|
9
|
-
metaTitle?: string
|
|
10
|
-
metaDescription?: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
type BaseFields = {
|
|
14
|
-
type: 'folder' | 'file'
|
|
15
|
-
path: string
|
|
16
|
-
name: string
|
|
17
|
-
childNodes?: FileOrFolderNode[]
|
|
18
|
-
matter?: MatterFields
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type FolderNode = BaseFields & { type?: 'folder' }
|
|
22
|
-
export type FileNode = BaseFields & { type?: 'file'; url: string; matter: MatterFields }
|
|
23
|
-
|
|
24
|
-
export type FileOrFolderNode = FolderNode | FileNode
|
|
25
|
-
|
|
26
|
-
function toUrl(p: string) {
|
|
27
|
-
let url = p.replace('.mdx', '')
|
|
28
|
-
url = url.replace('.md', '')
|
|
29
|
-
url = url.endsWith('/') ? url.slice(0, -1) : url
|
|
30
|
-
return url
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function dirTree(dir: string, root: string): Promise<FileOrFolderNode> {
|
|
34
|
-
const stats = await fs.promises.lstat(dir)
|
|
35
|
-
|
|
36
|
-
let name = path.basename(dir)
|
|
37
|
-
name = name.replace('.mdx', '')
|
|
38
|
-
name = name.replace('.md', '')
|
|
39
|
-
name = name.replace(/-/g, ' ').replace(/^./, (x) => x.toUpperCase())
|
|
40
|
-
|
|
41
|
-
const filePath = path.relative(root, dir)
|
|
42
|
-
|
|
43
|
-
const info: Partial<FileOrFolderNode> = { path: filePath, name }
|
|
44
|
-
|
|
45
|
-
if (stats.isDirectory()) {
|
|
46
|
-
info.type = 'folder'
|
|
47
|
-
info.childNodes = await Promise.all(
|
|
48
|
-
(await fs.promises.readdir(dir)).map((child) => dirTree(`${dir}/${child}`, root)),
|
|
49
|
-
)
|
|
50
|
-
} else {
|
|
51
|
-
info.type = 'file'
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (info.type === 'file') {
|
|
55
|
-
info.url = toUrl(path.relative(root, dir))
|
|
56
|
-
info.matter = matter(await vfile.read(dir)).data.matter as Record<string, string>
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return info as FileOrFolderNode
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Replace the path of the parent with the path of the child having index.mdx as name and remove from children.
|
|
63
|
-
// Do this recursively for each child.
|
|
64
|
-
// Remove type from tree
|
|
65
|
-
function hoistIndex(tree: FileOrFolderNode): FileOrFolderNode {
|
|
66
|
-
let newTree: FileOrFolderNode = tree
|
|
67
|
-
|
|
68
|
-
if (newTree.type === 'folder') {
|
|
69
|
-
const index = newTree.childNodes?.find(
|
|
70
|
-
(child) =>
|
|
71
|
-
child.path.endsWith('readme.mdx') ||
|
|
72
|
-
(child.path.endsWith('readme.md') && child.type === 'file'),
|
|
73
|
-
) as FileNode | undefined
|
|
74
|
-
|
|
75
|
-
if (index) {
|
|
76
|
-
newTree = { ...index, name: tree.name, url: index.url.slice(0, -7) }
|
|
77
|
-
newTree.childNodes = tree.childNodes?.filter((child) => child !== index)
|
|
78
|
-
|
|
79
|
-
const order = index.matter?.order?.split(',').map((x) => x.trim())
|
|
80
|
-
if (order) {
|
|
81
|
-
newTree.childNodes = newTree.childNodes?.sort((a, b) => {
|
|
82
|
-
const aPath = toUrl(a.path.split('/').pop() as string)
|
|
83
|
-
const bPath = toUrl(b.path.split('/').pop() as string)
|
|
84
|
-
return order.indexOf(aPath) === -1 ? 1 : order.indexOf(aPath) - order.indexOf(bPath)
|
|
85
|
-
})
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
newTree.childNodes = newTree.childNodes?.map((child) => hoistIndex(child))
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return newTree
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export async function getDirectoryTree(dir: string): Promise<false | FileNode> {
|
|
95
|
-
const absDir = path.join(process.cwd(), dir)
|
|
96
|
-
|
|
97
|
-
const tree = await dirTree(absDir, absDir)
|
|
98
|
-
|
|
99
|
-
return hoistIndex(tree) as FileNode
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export async function getDirectoryPaths(dir: string) {
|
|
103
|
-
const menuData = await getDirectoryTree(dir)
|
|
104
|
-
|
|
105
|
-
const paths: string[] = []
|
|
106
|
-
const addPathsFromTree = (tree: FileOrFolderNode) => {
|
|
107
|
-
if (tree.type === 'file') paths.push(tree.url)
|
|
108
|
-
|
|
109
|
-
if (tree.childNodes?.length) {
|
|
110
|
-
tree.childNodes.forEach((child) => addPathsFromTree(child))
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (menuData) addPathsFromTree(menuData)
|
|
114
|
-
|
|
115
|
-
return paths
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Recursively traverse the tree and return the aggregated path of of each node.
|
|
120
|
-
*
|
|
121
|
-
* Each URL segment should reference a path (except for the readme.md/readme.mdx file)
|
|
122
|
-
*/
|
|
123
|
-
export function urlToPath(url: string[], node: FileOrFolderNode): string | false {
|
|
124
|
-
if (node.childNodes?.length) {
|
|
125
|
-
const child = node.childNodes?.reduce<string | false>((prev, curr) => {
|
|
126
|
-
if (prev) return prev
|
|
127
|
-
const childPath = urlToPath(url, curr)
|
|
128
|
-
return childPath ?? prev
|
|
129
|
-
}, false)
|
|
130
|
-
|
|
131
|
-
if (child) return child
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (node.type === 'file' && node.url === url.join('/')) return node.path
|
|
135
|
-
|
|
136
|
-
return false
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** Get the contens of the requested file. */
|
|
140
|
-
export function getFileContents(dir: string, filePath: string) {
|
|
141
|
-
const absDir = path.join(process.cwd(), dir, filePath)
|
|
142
|
-
try {
|
|
143
|
-
return fs.promises.readFile(absDir, 'utf8')
|
|
144
|
-
} catch (e) {
|
|
145
|
-
return false
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Recursively traverse the tree and return the aggregated path of of each node.
|
|
151
|
-
*
|
|
152
|
-
* Each URL segment should reference a path (except for the readme.md/readme.mdx file)
|
|
153
|
-
*/
|
|
154
|
-
export function findByUrl(url: string[], node: FileOrFolderNode): FileNode | false {
|
|
155
|
-
if (node.childNodes?.length) {
|
|
156
|
-
const child = node.childNodes?.reduce<FileNode | false>((prev, curr) => {
|
|
157
|
-
if (prev) return prev
|
|
158
|
-
const childPath = findByUrl(url, curr)
|
|
159
|
-
return childPath ?? prev
|
|
160
|
-
}, false)
|
|
161
|
-
|
|
162
|
-
if (child) return child
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (node.type === 'file' && node.url === url.join('/')) return node
|
|
166
|
-
|
|
167
|
-
return false
|
|
168
|
-
}
|
package/lib/instantSearch.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { HitsRenderState } from 'instantsearch.js/es/connectors/hits/connectHits'
|
|
2
|
-
import { useHits as useHitsBase } from 'react-instantsearch-hooks'
|
|
3
|
-
import { SetReturnType } from 'type-fest'
|
|
4
|
-
import type { DocumentationDocument } from './DocumentIndexer'
|
|
5
|
-
import { createInstantSearchProps } from './typesense/createInstantSearchProps'
|
|
6
|
-
|
|
7
|
-
export const indexName = 'documents'
|
|
8
|
-
|
|
9
|
-
export const instantSearchProps = createInstantSearchProps<DocumentationDocument>(indexName, {
|
|
10
|
-
query_by: {
|
|
11
|
-
name: 2,
|
|
12
|
-
content: 1,
|
|
13
|
-
},
|
|
14
|
-
numTypos: '1',
|
|
15
|
-
typoTokensThreshold: 1,
|
|
16
|
-
highlight_fields: 'content',
|
|
17
|
-
highlight_full_fields: 'name',
|
|
18
|
-
include_fields: 'name,url',
|
|
19
|
-
// highlight_affix_num_tokens: ,
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
const useHits: SetReturnType<typeof useHitsBase, HitsRenderState<DocumentationDocument>> = (
|
|
23
|
-
...params
|
|
24
|
-
) => useHitsBase(...params) as HitsRenderState<DocumentationDocument>
|
|
25
|
-
|
|
26
|
-
export { useHits }
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { Client } from 'typesense/lib/Typesense'
|
|
2
|
-
import { ConfigurationOptions } from 'typesense/lib/Typesense/Configuration'
|
|
3
|
-
import { ObjectNotFound } from 'typesense/lib/Typesense/Errors'
|
|
4
|
-
import { BaseDocument, SearchIndexer } from './SearchIndexer'
|
|
5
|
-
import { batchIterable } from './batchInterable'
|
|
6
|
-
|
|
7
|
-
export class IndexerHandler {
|
|
8
|
-
client: Client
|
|
9
|
-
|
|
10
|
-
constructor(
|
|
11
|
-
clientOptions: ConfigurationOptions,
|
|
12
|
-
public indexers: SearchIndexer<BaseDocument>[],
|
|
13
|
-
private batchSize: number = 1000,
|
|
14
|
-
) {
|
|
15
|
-
this.client = new Client(clientOptions)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async all() {
|
|
19
|
-
let count = 0
|
|
20
|
-
for await (const indexer of this.indexers) {
|
|
21
|
-
const resCount = await this.#reindex(indexer)
|
|
22
|
-
count += resCount
|
|
23
|
-
}
|
|
24
|
-
return count
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** @throws {TypesenseError} */
|
|
28
|
-
async #reindex(indexer: SearchIndexer<BaseDocument>) {
|
|
29
|
-
const { name } = indexer.schema
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
await this.client.collections(name).retrieve()
|
|
33
|
-
await this.client.collections(name).delete()
|
|
34
|
-
} catch (e) {
|
|
35
|
-
if (!(e instanceof ObjectNotFound)) throw e
|
|
36
|
-
}
|
|
37
|
-
await this.client.collections().create(indexer.schema)
|
|
38
|
-
|
|
39
|
-
let count = 0
|
|
40
|
-
for await (const documents of batchIterable(indexer.all(), this.batchSize)) {
|
|
41
|
-
const result = await this.client.collections(name).documents().import(documents)
|
|
42
|
-
count += result.length
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return count
|
|
46
|
-
}
|
|
47
|
-
}
|
package/lib/typesense/Leaves.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export type Leaves<T, D extends number = 10> = [D] extends [never]
|
|
2
|
-
? never
|
|
3
|
-
: T extends object
|
|
4
|
-
? { [K in keyof T]-?: Join<K, Leaves<T[K], Prev[D]>> }[keyof T]
|
|
5
|
-
: ''
|
|
6
|
-
|
|
7
|
-
type Prev = [
|
|
8
|
-
never,
|
|
9
|
-
0,
|
|
10
|
-
1,
|
|
11
|
-
2,
|
|
12
|
-
3,
|
|
13
|
-
4,
|
|
14
|
-
5,
|
|
15
|
-
6,
|
|
16
|
-
7,
|
|
17
|
-
8,
|
|
18
|
-
9,
|
|
19
|
-
10,
|
|
20
|
-
11,
|
|
21
|
-
12,
|
|
22
|
-
13,
|
|
23
|
-
14,
|
|
24
|
-
15,
|
|
25
|
-
16,
|
|
26
|
-
17,
|
|
27
|
-
18,
|
|
28
|
-
19,
|
|
29
|
-
20,
|
|
30
|
-
...0[]
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
type Join<K, P> = K extends string | number
|
|
34
|
-
? P extends string | number
|
|
35
|
-
? `${K}${'' extends P ? '' : '.'}${P}`
|
|
36
|
-
: never
|
|
37
|
-
: never
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { ConditionalExcept, Get } from 'type-fest'
|
|
2
|
-
import { CollectionFieldSchema } from 'typesense/lib/Typesense/Collection'
|
|
3
|
-
import { CollectionCreateSchema } from 'typesense/lib/Typesense/Collections'
|
|
4
|
-
import { Leaves } from './Leaves'
|
|
5
|
-
|
|
6
|
-
type FieldValues = Record<string, any>
|
|
7
|
-
|
|
8
|
-
type SchemaType<
|
|
9
|
-
TFieldValues extends FieldValues,
|
|
10
|
-
TFieldPath extends Leaves<TFieldValues>,
|
|
11
|
-
T = Get<TFieldValues, TFieldPath>,
|
|
12
|
-
> = T extends object
|
|
13
|
-
? never
|
|
14
|
-
: T extends string
|
|
15
|
-
? 'string'
|
|
16
|
-
: T extends number
|
|
17
|
-
? 'number'
|
|
18
|
-
: T extends boolean
|
|
19
|
-
? 'boolean'
|
|
20
|
-
: T
|
|
21
|
-
|
|
22
|
-
type OptionalType<
|
|
23
|
-
TFieldValues extends FieldValues,
|
|
24
|
-
TFieldPath extends Leaves<TFieldValues>,
|
|
25
|
-
T = Get<TFieldValues, TFieldPath>,
|
|
26
|
-
> = T extends undefined ? true : never
|
|
27
|
-
|
|
28
|
-
export type BaseDocument = {
|
|
29
|
-
id: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export type CreateSchema<Schema extends BaseDocument> = Omit<CollectionCreateSchema, 'fields'> & {
|
|
33
|
-
fields: {
|
|
34
|
-
[K in Leaves<Schema>]: ConditionalExcept<
|
|
35
|
-
{
|
|
36
|
-
type: SchemaType<Schema, K>
|
|
37
|
-
optional: OptionalType<Schema, K>
|
|
38
|
-
} & Omit<CollectionFieldSchema, 'type' | 'optional' | 'name'>,
|
|
39
|
-
never
|
|
40
|
-
>
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export abstract class SearchIndexer<T extends BaseDocument> {
|
|
45
|
-
name: string
|
|
46
|
-
|
|
47
|
-
schema: CollectionCreateSchema
|
|
48
|
-
|
|
49
|
-
constructor(schema: CreateSchema<T>) {
|
|
50
|
-
this.name = schema.name
|
|
51
|
-
|
|
52
|
-
const fields = Object.entries(schema.fields).map(
|
|
53
|
-
([name, field]) =>
|
|
54
|
-
({
|
|
55
|
-
name,
|
|
56
|
-
...(field as Omit<CollectionFieldSchema, 'name'>),
|
|
57
|
-
} as CollectionFieldSchema),
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
this.schema = { ...schema, fields }
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
abstract all(): AsyncGenerator<T>
|
|
64
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export async function* batchIterable<T>(iterable: AsyncIterableIterator<T>, batchSize: number) {
|
|
2
|
-
let items: T[] = []
|
|
3
|
-
for await (const item of iterable) {
|
|
4
|
-
items.push(item)
|
|
5
|
-
if (items.length >= batchSize) {
|
|
6
|
-
yield items
|
|
7
|
-
items = []
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
if (items.length !== 0) {
|
|
11
|
-
yield items
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { InstantSearchProps } from 'react-instantsearch-hooks'
|
|
2
|
-
import TypesenseInstantSearchAdapter from 'typesense-instantsearch-adapter'
|
|
3
|
-
import { Leaves } from './Leaves'
|
|
4
|
-
import { BaseDocument } from './SearchIndexer'
|
|
5
|
-
import { typesenseClientConf } from './typesenseClientConf'
|
|
6
|
-
|
|
7
|
-
type BaseParams = Omit<
|
|
8
|
-
NonNullable<
|
|
9
|
-
ConstructorParameters<typeof TypesenseInstantSearchAdapter>[0]['additionalSearchParameters']
|
|
10
|
-
>,
|
|
11
|
-
'query_by' | 'query_by_weight'
|
|
12
|
-
>
|
|
13
|
-
|
|
14
|
-
type InstantSearchClientParams<Document extends BaseDocument> = BaseParams & {
|
|
15
|
-
query_by: {
|
|
16
|
-
[key in Leaves<Document>]?: number
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function createInstantSearchProps<Document extends BaseDocument>(
|
|
21
|
-
indexName: string,
|
|
22
|
-
params: InstantSearchClientParams<Document>,
|
|
23
|
-
): InstantSearchProps {
|
|
24
|
-
const { query_by, ...rest } = params
|
|
25
|
-
|
|
26
|
-
const { searchClient } = new TypesenseInstantSearchAdapter({
|
|
27
|
-
server: typesenseClientConf(),
|
|
28
|
-
additionalSearchParameters: {
|
|
29
|
-
query_by: Object.keys(query_by).join(','),
|
|
30
|
-
query_by_weights: Object.values(query_by).join(','),
|
|
31
|
-
...rest,
|
|
32
|
-
},
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
return { searchClient, indexName }
|
|
36
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { ConfigurationOptions } from 'typesense/lib/Typesense/Configuration'
|
|
2
|
-
|
|
3
|
-
export function typesenseClientConf() {
|
|
4
|
-
// Be sure to use an API key that only allows searches, in production
|
|
5
|
-
const apiKey = process.env.NEXT_PUBLIC_TYPESENSE_SEARCH_ONLY_API_KEY
|
|
6
|
-
const host = process.env.NEXT_PUBLIC_TYPESENSE_HOST
|
|
7
|
-
const port = Number(process.env.NEXT_PUBLIC_TYPESENSE_PORT)
|
|
8
|
-
const protocol = process.env.NEXT_PUBLIC_TYPESENSE_PROTOCOL
|
|
9
|
-
|
|
10
|
-
if (!apiKey) throw Error('Please provide NEXT_PUBLIC_TYPESENSE_SEARCH_ONLY_API_KEY')
|
|
11
|
-
if (!host) throw Error('Please provide NEXT_PUBLIC_TYPESENSE_HOST')
|
|
12
|
-
if (!port) throw Error('Please provide NEXT_PUBLIC_TYPESENSE_PORT')
|
|
13
|
-
if (!protocol) throw Error('Please provide NEXT_PUBLIC_TYPESENSE_PROTOCOL')
|
|
14
|
-
|
|
15
|
-
const conf: ConfigurationOptions = {
|
|
16
|
-
apiKey,
|
|
17
|
-
nodes: [{ host, port, protocol }],
|
|
18
|
-
numRetries: 8,
|
|
19
|
-
connectionTimeoutSeconds: 1,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return conf
|
|
23
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { NextApiRequest, NextApiResponse } from 'next'
|
|
2
|
-
import { TypesenseError } from 'typesense/lib/Typesense/Errors'
|
|
3
|
-
import { IndexerHandler } from './IndexerHandler'
|
|
4
|
-
import { BaseDocument, SearchIndexer } from './SearchIndexer'
|
|
5
|
-
import { typesenseClientConf } from './typesenseClientConf'
|
|
6
|
-
|
|
7
|
-
export function typesenseIndexerHandler(indexers: SearchIndexer<BaseDocument>[]) {
|
|
8
|
-
return async (req: NextApiRequest, res: NextApiResponse) => {
|
|
9
|
-
const handler = new IndexerHandler(typesenseClientConf(), indexers)
|
|
10
|
-
try {
|
|
11
|
-
const count = await handler.all()
|
|
12
|
-
res.status(200).json({ status: 'success', count })
|
|
13
|
-
} catch (e) {
|
|
14
|
-
if (e instanceof TypesenseError) {
|
|
15
|
-
res.json(e)
|
|
16
|
-
}
|
|
17
|
-
console.log(e)
|
|
18
|
-
res.status(500)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
res.end()
|
|
22
|
-
}
|
|
23
|
-
}
|
package/next-env.d.ts
DELETED
package/next.config.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
-
const withYarn1Workspaces = require('@graphcommerce/next-config').withYarn1Scopes()
|
|
3
|
-
|
|
4
|
-
/** @type {import('next').NextConfig} */
|
|
5
|
-
const nextConfig = {
|
|
6
|
-
// https://nextjs.org/docs/api-reference/next.config.js/configuring-onDemandEntries
|
|
7
|
-
onDemandEntries: {
|
|
8
|
-
maxInactiveAge: 10 * 60 * 1000, // 10 minutes
|
|
9
|
-
},
|
|
10
|
-
eslint: {
|
|
11
|
-
ignoreDuringBuilds: true,
|
|
12
|
-
},
|
|
13
|
-
typescript: {
|
|
14
|
-
ignoreBuildErrors: true,
|
|
15
|
-
},
|
|
16
|
-
experimental: {
|
|
17
|
-
scrollRestoration: true,
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
module.exports = withYarn1Workspaces(nextConfig)
|