create-specra 0.1.7 → 0.2.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.
Files changed (136) hide show
  1. package/LICENSE.MD +16 -4
  2. package/README.md +53 -17
  3. package/dist/chunk-3DKWECRK.js +45 -0
  4. package/dist/chunk-3DKWECRK.js.map +1 -0
  5. package/dist/chunk-MA7QG54W.js +74 -0
  6. package/dist/chunk-MA7QG54W.js.map +1 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +33 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/deploy-SCEMUQNS.js +141 -0
  11. package/dist/deploy-SCEMUQNS.js.map +1 -0
  12. package/dist/index.js +11 -11
  13. package/dist/index.js.map +1 -1
  14. package/dist/login-NKDRQXRE.js +71 -0
  15. package/dist/login-NKDRQXRE.js.map +1 -0
  16. package/dist/logout-H543QEKU.js +20 -0
  17. package/dist/logout-H543QEKU.js.map +1 -0
  18. package/dist/logs-YDAUCMAV.js +71 -0
  19. package/dist/logs-YDAUCMAV.js.map +1 -0
  20. package/dist/projects-3TAY7EDJ.js +42 -0
  21. package/dist/projects-3TAY7EDJ.js.map +1 -0
  22. package/package.json +7 -3
  23. package/templates/book-docs/docs/v1.0.0/concepts.mdx +89 -0
  24. package/templates/book-docs/docs/v1.0.0/content/_category_.json +7 -0
  25. package/templates/book-docs/docs/v1.0.0/content/formatting.mdx +128 -0
  26. package/templates/book-docs/docs/v1.0.0/content/reusable-content.mdx +116 -0
  27. package/templates/book-docs/docs/v1.0.0/content/structure.mdx +92 -0
  28. package/templates/book-docs/docs/v1.0.0/customization/_category_.json +7 -0
  29. package/templates/book-docs/docs/v1.0.0/customization/branding.mdx +115 -0
  30. package/templates/book-docs/docs/v1.0.0/customization/themes.mdx +81 -0
  31. package/templates/book-docs/docs/v1.0.0/introduction.mdx +38 -0
  32. package/templates/book-docs/docs/v1.0.0/quickstart.mdx +112 -0
  33. package/templates/book-docs/docs/v2.0.0/concepts.mdx +89 -0
  34. package/templates/book-docs/docs/v2.0.0/content/_category_.json +7 -0
  35. package/templates/book-docs/docs/v2.0.0/content/formatting.mdx +128 -0
  36. package/templates/book-docs/docs/v2.0.0/content/reusable-content.mdx +116 -0
  37. package/templates/book-docs/docs/v2.0.0/content/structure.mdx +92 -0
  38. package/templates/book-docs/docs/v2.0.0/customization/_category_.json +7 -0
  39. package/templates/book-docs/docs/v2.0.0/customization/branding.mdx +115 -0
  40. package/templates/book-docs/docs/v2.0.0/customization/themes.mdx +81 -0
  41. package/templates/book-docs/docs/v2.0.0/introduction.mdx +39 -0
  42. package/templates/book-docs/docs/v2.0.0/quickstart.mdx +112 -0
  43. package/templates/book-docs/gitignore +7 -0
  44. package/templates/book-docs/package.json +28 -0
  45. package/templates/book-docs/postcss.config.mjs +8 -0
  46. package/templates/book-docs/public/api-specs/openapi-example.json +259 -0
  47. package/templates/book-docs/public/api-specs/postman-example.json +205 -0
  48. package/templates/book-docs/public/api-specs/test-api.json +256 -0
  49. package/templates/book-docs/public/api-specs/users-api.json +264 -0
  50. package/templates/book-docs/specra.config.json +77 -0
  51. package/templates/book-docs/src/app.css +2 -0
  52. package/templates/book-docs/src/app.html +12 -0
  53. package/templates/book-docs/src/routes/+layout.server.ts +11 -0
  54. package/templates/book-docs/src/routes/+layout.svelte +21 -0
  55. package/templates/book-docs/src/routes/+page.server.ts +9 -0
  56. package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +119 -0
  57. package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.svelte +129 -0
  58. package/templates/book-docs/svelte.config.js +8 -0
  59. package/templates/book-docs/tsconfig.json +12 -0
  60. package/templates/book-docs/vite.config.ts +6 -0
  61. package/templates/jbrains-docs/docs/v1.0.0/advanced/_category_.json +8 -0
  62. package/templates/jbrains-docs/docs/v1.0.0/advanced/async.mdx +95 -0
  63. package/templates/jbrains-docs/docs/v1.0.0/advanced/generics.mdx +126 -0
  64. package/templates/jbrains-docs/docs/v1.0.0/basics/_category_.json +8 -0
  65. package/templates/jbrains-docs/docs/v1.0.0/basics/control-flow.mdx +106 -0
  66. package/templates/jbrains-docs/docs/v1.0.0/basics/syntax.mdx +129 -0
  67. package/templates/jbrains-docs/docs/v1.0.0/basics/types.mdx +135 -0
  68. package/templates/jbrains-docs/docs/v1.0.0/getting-started.mdx +111 -0
  69. package/templates/jbrains-docs/docs/v1.0.0/home.mdx +37 -0
  70. package/templates/jbrains-docs/docs/v1.0.0/tools/_category_.json +8 -0
  71. package/templates/jbrains-docs/docs/v1.0.0/tools/build-tools.mdx +165 -0
  72. package/templates/jbrains-docs/docs/v1.0.0/tools/testing.mdx +112 -0
  73. package/templates/jbrains-docs/docs/v2.0.0/advanced/_category_.json +8 -0
  74. package/templates/jbrains-docs/docs/v2.0.0/advanced/async.mdx +95 -0
  75. package/templates/jbrains-docs/docs/v2.0.0/advanced/generics.mdx +126 -0
  76. package/templates/jbrains-docs/docs/v2.0.0/basics/_category_.json +8 -0
  77. package/templates/jbrains-docs/docs/v2.0.0/basics/control-flow.mdx +106 -0
  78. package/templates/jbrains-docs/docs/v2.0.0/basics/syntax.mdx +129 -0
  79. package/templates/jbrains-docs/docs/v2.0.0/basics/types.mdx +135 -0
  80. package/templates/jbrains-docs/docs/v2.0.0/getting-started.mdx +111 -0
  81. package/templates/jbrains-docs/docs/v2.0.0/home.mdx +37 -0
  82. package/templates/jbrains-docs/docs/v2.0.0/tools/_category_.json +8 -0
  83. package/templates/jbrains-docs/docs/v2.0.0/tools/build-tools.mdx +165 -0
  84. package/templates/jbrains-docs/docs/v2.0.0/tools/testing.mdx +112 -0
  85. package/templates/jbrains-docs/gitignore +7 -0
  86. package/templates/jbrains-docs/package.json +28 -0
  87. package/templates/jbrains-docs/postcss.config.mjs +8 -0
  88. package/templates/jbrains-docs/public/api-specs/openapi-example.json +259 -0
  89. package/templates/jbrains-docs/public/api-specs/postman-example.json +205 -0
  90. package/templates/jbrains-docs/public/api-specs/test-api.json +256 -0
  91. package/templates/jbrains-docs/public/api-specs/users-api.json +264 -0
  92. package/templates/jbrains-docs/specra.config.json +80 -0
  93. package/templates/jbrains-docs/src/app.css +2 -0
  94. package/templates/jbrains-docs/src/app.html +12 -0
  95. package/templates/jbrains-docs/src/routes/+layout.server.ts +11 -0
  96. package/templates/jbrains-docs/src/routes/+layout.svelte +21 -0
  97. package/templates/jbrains-docs/src/routes/+page.server.ts +9 -0
  98. package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +119 -0
  99. package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.svelte +129 -0
  100. package/templates/jbrains-docs/svelte.config.js +8 -0
  101. package/templates/jbrains-docs/tsconfig.json +12 -0
  102. package/templates/jbrains-docs/vite.config.ts +6 -0
  103. package/templates/minimal/docs/v1.0.0/about.mdx +3 -3
  104. package/templates/minimal/docs/v2.0.0/about.mdx +3 -3
  105. package/templates/minimal/gitignore +7 -0
  106. package/templates/minimal/package.json +18 -24
  107. package/templates/minimal/specra.config.json +12 -63
  108. package/templates/minimal/src/app.css +2 -0
  109. package/templates/minimal/src/app.html +12 -0
  110. package/templates/minimal/src/routes/+layout.server.ts +11 -0
  111. package/templates/minimal/src/routes/+layout.svelte +21 -0
  112. package/templates/minimal/src/routes/+page.server.ts +9 -0
  113. package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.server.ts +119 -0
  114. package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.svelte +129 -0
  115. package/templates/minimal/svelte.config.js +8 -0
  116. package/templates/minimal/tsconfig.json +7 -36
  117. package/templates/minimal/vite.config.ts +6 -0
  118. package/templates/minimal/README.md +0 -132
  119. package/templates/minimal/app/api/mdx-watch/route.ts +0 -6
  120. package/templates/minimal/app/api/search/route.ts +0 -75
  121. package/templates/minimal/app/docs/[version]/[...slug]/loading.tsx +0 -7
  122. package/templates/minimal/app/docs/[version]/[...slug]/page.tsx +0 -205
  123. package/templates/minimal/app/docs/[version]/[...slug]/page.tsx.bak +0 -203
  124. package/templates/minimal/app/docs/[version]/not-found.tsx +0 -10
  125. package/templates/minimal/app/docs/[version]/page.tsx +0 -27
  126. package/templates/minimal/app/globals.css +0 -9
  127. package/templates/minimal/app/layout.tsx +0 -62
  128. package/templates/minimal/app/not-found.tsx +0 -10
  129. package/templates/minimal/app/page.tsx +0 -179
  130. package/templates/minimal/next.config.mjs +0 -1
  131. package/templates/minimal/package-lock.json +0 -7881
  132. package/templates/minimal/proxy_.ts +0 -22
  133. package/templates/minimal/scripts/generate-redirects.mjs +0 -128
  134. package/templates/minimal/scripts/generate-static-redirects.mjs +0 -115
  135. package/templates/minimal/scripts/index-search.ts +0 -182
  136. package/templates/minimal/scripts/test-search.ts +0 -83
@@ -1,22 +0,0 @@
1
- import { createSecurityProxy } from "specra/middleware/security"
2
-
3
- // Note: Redirects from frontmatter are handled at build time via next.config.js
4
- // This proxy handles runtime security headers, path validation, and dynamic logic
5
-
6
- export const proxy = createSecurityProxy({
7
- production: process.env.NODE_ENV === "production",
8
- strictPathValidation: true,
9
- })
10
-
11
- export const config = {
12
- matcher: [
13
- /*
14
- * Match all request paths except for the ones starting with:
15
- * - api (API routes)
16
- * - _next/static (static files)
17
- * - _next/image (image optimization files)
18
- * - favicon.ico (favicon file)
19
- */
20
- "/((?!api|_next/static|_next/image|favicon.ico).*)",
21
- ],
22
- }
@@ -1,128 +0,0 @@
1
- import fs from "fs"
2
- import path from "path"
3
- import matter from "gray-matter"
4
- import { fileURLToPath } from "url"
5
-
6
- const __filename = fileURLToPath(import.meta.url)
7
- const __dirname = path.dirname(__filename)
8
-
9
- const DOCS_DIR = path.join(__dirname, "..", "docs")
10
-
11
- /**
12
- * Recursively find all MDX files
13
- */
14
- function findMdxFiles(dir, baseDir = dir) {
15
- const files = []
16
-
17
- try {
18
- const entries = fs.readdirSync(dir, { withFileTypes: true })
19
-
20
- for (const entry of entries) {
21
- const fullPath = path.join(dir, entry.name)
22
-
23
- if (entry.isDirectory()) {
24
- files.push(...findMdxFiles(fullPath, baseDir))
25
- } else if (entry.isFile() && entry.name.endsWith(".mdx")) {
26
- const relativePath = path.relative(baseDir, fullPath)
27
- files.push(relativePath)
28
- }
29
- }
30
- } catch (error) {
31
- console.error(`Error reading directory ${dir}:`, error)
32
- }
33
-
34
- return files
35
- }
36
-
37
- /**
38
- * Get all folders (categories) that need redirect pages
39
- */
40
- function getFolderRedirects(mdxFiles, version) {
41
- const folders = new Map() // folder path -> first doc in folder
42
-
43
- for (const file of mdxFiles) {
44
- const normalized = file.replace(/\\/g, '/')
45
- const parts = normalized.split('/')
46
-
47
- // For each level of nesting, track the folder
48
- for (let i = 0; i < parts.length - 1; i++) {
49
- const folderPath = parts.slice(0, i + 1).join('/')
50
-
51
- if (!folders.has(folderPath)) {
52
- // This is the first doc we've seen in this folder
53
- const docSlug = normalized.replace(/\.mdx$/, '').replace(/\/index$/, '')
54
- folders.set(folderPath, `/docs/${version}/${docSlug}`)
55
- }
56
- }
57
- }
58
-
59
- return folders
60
- }
61
-
62
- /**
63
- * Build redirect mappings from frontmatter
64
- */
65
- async function buildRedirects() {
66
- const versions = fs.readdirSync(DOCS_DIR).filter((v) => {
67
- const stat = fs.statSync(path.join(DOCS_DIR, v))
68
- return stat.isDirectory()
69
- })
70
-
71
- const redirects = []
72
- const folderRedirects = []
73
-
74
- for (const version of versions) {
75
- const versionDir = path.join(DOCS_DIR, version)
76
- const mdxFiles = findMdxFiles(versionDir)
77
-
78
- // Build folder redirects for static export
79
- const folders = getFolderRedirects(mdxFiles, version)
80
- for (const [folderPath, destination] of folders.entries()) {
81
- const source = `/docs/${version}/${folderPath}`
82
- folderRedirects.push({
83
- source,
84
- destination,
85
- permanent: false, // Temporary redirect for folder access
86
- })
87
- }
88
-
89
- for (const file of mdxFiles) {
90
- const filePath = path.join(versionDir, file)
91
- const fileContents = fs.readFileSync(filePath, "utf8")
92
- const { data } = matter(fileContents)
93
-
94
- if (data.redirect_from && Array.isArray(data.redirect_from)) {
95
- const slug = file.replace(/\.mdx$/, "").replace(/\\/g, '/')
96
- const destination = `/docs/${version}/${slug}`
97
-
98
- for (const source of data.redirect_from) {
99
- redirects.push({
100
- source,
101
- destination,
102
- permanent: true,
103
- })
104
- }
105
- }
106
- }
107
- }
108
-
109
- return { redirects, folderRedirects }
110
- }
111
-
112
- /**
113
- * Generate redirects and write to a JSON file
114
- */
115
- async function main() {
116
- const { redirects, folderRedirects } = await buildRedirects()
117
-
118
- const allRedirects = [...redirects, ...folderRedirects]
119
-
120
- const outputPath = path.join(__dirname, "..", "redirects.json")
121
- fs.writeFileSync(outputPath, JSON.stringify(allRedirects, null, 2))
122
-
123
- console.log(`✅ Generated ${redirects.length} frontmatter redirects`)
124
- console.log(`✅ Generated ${folderRedirects.length} folder redirects`)
125
- console.log(`📝 Saved to: ${outputPath}`)
126
- }
127
-
128
- main().catch(console.error)
@@ -1,115 +0,0 @@
1
- import fs from "fs"
2
- import path from "path"
3
- import { fileURLToPath } from "url"
4
-
5
- const __filename = fileURLToPath(import.meta.url)
6
- const __dirname = path.dirname(__filename)
7
-
8
- /**
9
- * Generate an HTML redirect page
10
- */
11
- function generateRedirectHTML(destination, basePath = "") {
12
- const fullDestination = basePath + destination
13
-
14
- return `<!DOCTYPE html>
15
- <html lang="en">
16
- <head>
17
- <meta charset="UTF-8">
18
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
- <meta http-equiv="refresh" content="0;url=${fullDestination}">
20
- <link rel="canonical" href="${fullDestination}">
21
- <title>Redirecting...</title>
22
- <script>
23
- window.location.href = "${fullDestination}";
24
- </script>
25
- </head>
26
- <body>
27
- <p>Redirecting to <a href="${fullDestination}">${fullDestination}</a>...</p>
28
- </body>
29
- </html>`
30
- }
31
-
32
- /**
33
- * Create redirect HTML files in the out directory
34
- */
35
- async function createStaticRedirects() {
36
- const outDir = path.join(__dirname, "..", "out")
37
- const redirectsPath = path.join(__dirname, "..", "redirects.json")
38
-
39
- // Load basePath from specra.config.json
40
- let basePath = ""
41
- try {
42
- const configPath = path.join(__dirname, "..", "specra.config.json")
43
- if (fs.existsSync(configPath)) {
44
- const config = JSON.parse(fs.readFileSync(configPath, "utf8"))
45
- const deployment = config.deployment || {}
46
-
47
- // Only apply basePath for GitHub Pages without custom domain
48
- if (deployment.target === "github-pages" && !deployment.customDomain && deployment.basePath) {
49
- basePath = deployment.basePath.startsWith("/")
50
- ? deployment.basePath
51
- : `/${deployment.basePath}`
52
- }
53
- }
54
- } catch (error) {
55
- console.warn("Could not load deployment config:", error.message)
56
- }
57
-
58
- // Check if out directory exists
59
- if (!fs.existsSync(outDir)) {
60
- console.warn("⚠️ Output directory 'out' not found. Run 'npm run build:export' first.")
61
- return
62
- }
63
-
64
- // Load redirects
65
- let redirects = []
66
- try {
67
- if (fs.existsSync(redirectsPath)) {
68
- redirects = JSON.parse(fs.readFileSync(redirectsPath, "utf8"))
69
- }
70
- } catch (error) {
71
- console.error("Could not load redirects.json:", error.message)
72
- return
73
- }
74
-
75
- let created = 0
76
-
77
- for (const redirect of redirects) {
78
- const { source, destination } = redirect
79
-
80
- // Remove basePath from source if present (for file system path)
81
- let sourcePath = source
82
- if (basePath && sourcePath.startsWith(basePath)) {
83
- sourcePath = sourcePath.substring(basePath.length)
84
- }
85
-
86
- // Ensure source starts with /
87
- if (!sourcePath.startsWith('/')) {
88
- sourcePath = '/' + sourcePath
89
- }
90
-
91
- // Convert URL path to file system path
92
- const targetDir = path.join(outDir, sourcePath.substring(1))
93
- const indexPath = path.join(targetDir, "index.html")
94
-
95
- // Create directory if it doesn't exist
96
- if (!fs.existsSync(targetDir)) {
97
- fs.mkdirSync(targetDir, { recursive: true })
98
- }
99
-
100
- // Don't overwrite if index.html already exists (real content page)
101
- if (!fs.existsSync(indexPath)) {
102
- // Generate and write redirect HTML
103
- const html = generateRedirectHTML(destination, basePath)
104
- fs.writeFileSync(indexPath, html)
105
- created++
106
- }
107
- }
108
-
109
- console.log(`✅ Created ${created} static redirect HTML files`)
110
- if (basePath) {
111
- console.log(`🔗 Using basePath: ${basePath}`)
112
- }
113
- }
114
-
115
- createStaticRedirects().catch(console.error)
@@ -1,182 +0,0 @@
1
- import { MeiliSearch } from "meilisearch"
2
- import fs from "fs"
3
- import path from "path"
4
- import matter from "gray-matter"
5
- import { extractSearchText, getConfig, initConfig } from "specra"
6
- import specraConfig from "../specra.config.json"
7
- // import { extractSearchText } from "specra/components"
8
- // import { extractSearchText } from "@/components/docs/componentTextProps"
9
-
10
- interface SearchDocument {
11
- id: string
12
- title: string
13
- content: string
14
- slug: string
15
- version: string
16
- category?: string
17
- tags?: string[]
18
- tab_group?: string
19
- }
20
-
21
-
22
- async function indexDocuments() {
23
- initConfig(specraConfig as any)
24
- const config = getConfig()
25
- console.log(config)
26
- const searchConfig = config.search
27
-
28
- if (!searchConfig?.enabled || searchConfig.provider !== "meilisearch") {
29
- console.error("Meilisearch is not enabled in config")
30
- process.exit(1)
31
- }
32
-
33
- const meilisearchConfig = searchConfig.meilisearch
34
- if (!meilisearchConfig) {
35
- console.error("Meilisearch configuration is missing")
36
- process.exit(1)
37
- }
38
-
39
- console.log("Connecting to Meilisearch at:", meilisearchConfig.host)
40
-
41
- const client = new MeiliSearch({
42
- host: meilisearchConfig.host,
43
- apiKey: meilisearchConfig.apiKey || "",
44
- })
45
-
46
- const index = client.index(meilisearchConfig.indexName)
47
-
48
- // Get all MDX files
49
- const docsDir = path.join(process.cwd(), "docs")
50
- const documents: SearchDocument[] = []
51
-
52
- function processDirectory(dir: string, version: string) {
53
- const files = fs.readdirSync(dir)
54
-
55
- for (const file of files) {
56
- const filePath = path.join(dir, file)
57
- const stat = fs.statSync(filePath)
58
-
59
- if (stat.isDirectory()) {
60
- processDirectory(filePath, version)
61
- } else if (file.endsWith(".mdx") || file.endsWith(".md")) {
62
- const content = fs.readFileSync(filePath, "utf-8")
63
- const { data, content: mdxContent } = matter(content)
64
-
65
- // Generate slug from file path
66
- const relativePath = path.relative(path.join(docsDir, version), filePath)
67
- const slug = relativePath
68
- .replace(/\.(mdx|md)$/, "")
69
- .replace(/\\/g, "/")
70
-
71
- // Extract category from path
72
- const pathParts = slug.split("/")
73
- const category = pathParts.length > 1 ? pathParts[0] : undefined
74
-
75
- // Get tab_group from frontmatter or from parent _category_.json
76
- let tabGroup = data.tab_group
77
-
78
- // If not in frontmatter, check parent directory's _category_.json
79
- if (!tabGroup && pathParts.length > 1) {
80
- const folderPath = pathParts.slice(0, -1).join("/")
81
- const categoryPath = path.join(docsDir, version, folderPath, "_category_.json")
82
-
83
- if (fs.existsSync(categoryPath)) {
84
- try {
85
- const categoryData = JSON.parse(fs.readFileSync(categoryPath, "utf-8"))
86
- tabGroup = categoryData.tab_group
87
- } catch (e) {
88
- // Ignore JSON parse errors
89
- }
90
- }
91
- }
92
-
93
- // Clean content (remove code blocks and special chars for better search)
94
- // const cleanContent = mdxContent
95
- // .replace(/```[\s\S]*?```/g, "") // Remove code blocks
96
- // .replace(/`[^`]+`/g, "") // Remove inline code
97
- // .replace(/[#*_~]/g, "") // Remove markdown symbols
98
- // .replace(/\n+/g, " ") // Replace newlines with spaces
99
- // .trim()
100
- // .slice(0, 1000) // Limit content length
101
-
102
- const cleanContent = extractSearchText(mdxContent);
103
-
104
- // console.log("------");
105
- // console.log("Cleaned content: ");
106
- // console.log(cleanContent);
107
- // console.log("------");
108
- // Create a valid document ID (replace periods with underscores)
109
- // const docId = `${version.replace(/\./g, "_")}-${slug.replace(/\//g, "-")}`
110
- const docId = slug.replace(/\//g, "-")
111
-
112
- documents.push({
113
- id: docId,
114
- title: data.title || slug,
115
- content: cleanContent,
116
- slug: slug,
117
- version: version,
118
- category: category,
119
- tags: data.tags || [],
120
- tab_group: tabGroup,
121
- })
122
- }
123
- }
124
- }
125
-
126
- // Process all version directories
127
- const versions = fs.readdirSync(docsDir).filter((item) => {
128
- const itemPath = path.join(docsDir, item)
129
- return fs.statSync(itemPath).isDirectory()
130
- })
131
-
132
- console.log(`Found ${versions.length} version(s):`, versions.join(", "))
133
-
134
- for (const version of versions) {
135
- const versionPath = path.join(docsDir, version)
136
- processDirectory(versionPath, version)
137
- }
138
-
139
- console.log(`Indexing ${documents.length} documents...`)
140
-
141
- try {
142
- // Configure searchable attributes first
143
- console.log("Configuring search settings...")
144
- await index.updateSearchableAttributes(["title", "content", "tags"])
145
- await index.updateFilterableAttributes(["version", "category", "tags"])
146
- await index.updateSortableAttributes(["title"])
147
- await index.updateDistinctAttribute("id")
148
- await index.updateSettings({
149
- rankingRules: [
150
- "words",
151
- "typo",
152
- "proximity",
153
- "attribute",
154
- "sort",
155
- "exactness"
156
- ]
157
- })
158
- console.log("✅ Search configuration updated!")
159
-
160
- // Delete existing documents
161
- console.log("Clearing old documents...")
162
- const deleteTask = await index.deleteAllDocuments()
163
- console.log("Delete task created:", deleteTask.taskUid)
164
-
165
- // Add new documents
166
- console.log("Adding documents...")
167
- const addTask = await index.addDocuments(documents, { primaryKey: "id" })
168
- console.log("Add task created:", addTask.taskUid)
169
- console.log("✅ Documents sent for indexing!")
170
-
171
- console.log("\n⏳ Indexing is processing in the background...")
172
- console.log("Wait a few seconds, then run 'npm run test:search' to verify!")
173
- } catch (error) {
174
- console.error("Error indexing documents:", error)
175
- if (error instanceof Error) {
176
- console.error("Error message:", error.message)
177
- }
178
- process.exit(1)
179
- }
180
- }
181
-
182
- indexDocuments()
@@ -1,83 +0,0 @@
1
- import { MeiliSearch } from "meilisearch"
2
- import { getConfig } from "specra"
3
-
4
- async function testSearch() {
5
- const config = getConfig()
6
- const searchConfig = config.search
7
-
8
- if (!searchConfig?.enabled || searchConfig.provider !== "meilisearch") {
9
- console.error("Meilisearch is not enabled in config")
10
- process.exit(1)
11
- }
12
-
13
- const meilisearchConfig = searchConfig.meilisearch
14
- if (!meilisearchConfig) {
15
- console.error("Meilisearch configuration is missing")
16
- process.exit(1)
17
- }
18
-
19
- console.log("Connecting to Meilisearch at:", meilisearchConfig.host)
20
- console.log("Using API Key:", meilisearchConfig.apiKey ? "Yes" : "No")
21
-
22
- const client = new MeiliSearch({
23
- host: meilisearchConfig.host,
24
- apiKey: meilisearchConfig.apiKey || "",
25
- })
26
-
27
- const index = client.index(meilisearchConfig.indexName)
28
-
29
- try {
30
- // Get index stats
31
- console.log("\n📊 Index Stats:")
32
- const stats = await index.getStats()
33
- console.log("- Number of documents:", stats.numberOfDocuments)
34
- console.log("- Is indexing:", stats.isIndexing)
35
- console.log("- Field distribution:", JSON.stringify(stats.fieldDistribution, null, 2))
36
-
37
- // Get some documents
38
- console.log("\n📄 Sample Documents:")
39
- const documents = await index.getDocuments({ limit: 3 })
40
- console.log("Total documents:", documents.results.length)
41
- documents.results.forEach((doc: any, i: number) => {
42
- console.log(`\nDocument ${i + 1}:`)
43
- console.log("- ID:", doc.id)
44
- console.log("- Title:", doc.title)
45
- console.log("- Version:", doc.version)
46
- console.log("- Slug:", doc.slug)
47
- console.log("- Category:", doc.category)
48
- console.log("- Content preview:", doc.content?.substring(0, 100) + "...")
49
- })
50
-
51
- // Test search
52
- console.log("\n🔍 Testing Search:")
53
- const searchQueries = ["documentation", "getting", "guide", "intro"]
54
-
55
- for (const query of searchQueries) {
56
- const results = await index.search(query, {
57
- limit: 5,
58
- })
59
- console.log(`\nQuery: "${query}"`)
60
- console.log(`- Results found: ${results.hits.length}`)
61
- console.log(`- Processing time: ${results.processingTimeMs}ms`)
62
- if (results.hits.length > 0) {
63
- console.log("- First result:", (results.hits[0] as any).title)
64
- }
65
- }
66
-
67
- // Get searchable attributes
68
- console.log("\n⚙️ Index Settings:")
69
- const searchableAttrs = await index.getSearchableAttributes()
70
- console.log("- Searchable attributes:", searchableAttrs)
71
-
72
- const filterableAttrs = await index.getFilterableAttributes()
73
- console.log("- Filterable attributes:", filterableAttrs)
74
-
75
- } catch (error) {
76
- console.error("\n❌ Error:", error)
77
- if (error instanceof Error) {
78
- console.error("Message:", error.message)
79
- }
80
- }
81
- }
82
-
83
- testSearch()