{"_id":"@duct-ui/client-search-provider","_rev":"5-864a4297d1c383eae54d15afa69e08c1","name":"@duct-ui/client-search-provider","dist-tags":{"latest":"0.8.4"},"versions":{"0.8.0":{"name":"@duct-ui/client-search-provider","version":"0.8.0","author":{"name":"navilan","email":"navilan@folds.in"},"license":"MIT","_id":"@duct-ui/client-search-provider@0.8.0","maintainers":[{"name":"navilan.puthir","email":"navilan@puthir.in"}],"homepage":"https://github.com/navilan/duct#readme","bugs":{"url":"https://github.com/navilan/duct/issues"},"dist":{"shasum":"38fd61fa3a3cb81a1cd9628abb712be6a87410ee","tarball":"https://registry.npmjs.org/@duct-ui/client-search-provider/-/client-search-provider-0.8.0.tgz","fileCount":8,"integrity":"sha512-sFlbqhXl/xlfAUBVK3xEzD0SjLPSa46u3O8exuJV6Fm3QuvC/Xn4HzaMQs14jb+f/vWwz1T5/lBjqdp88y9VJQ==","signatures":[{"sig":"MEYCIQD3SakZdMLJtGVOcu9Gjl2lc1k5o70B1aKZnyILy9YpdwIhAPrRj1QLEULly3W1OIED1kzthg5MWNIt/Er2vyjA3p3G","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17346},"main":"./dist/index.js","type":"module","_from":"file:duct-ui-client-search-provider-0.8.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc","clean":"rimraf dist"},"_npmUser":{"name":"navilan.puthir","email":"navilan@puthir.in"},"_resolved":"/private/var/folders/mm/7tvfnj1n4kjbgbjpd8rp3ypw0000gn/T/6a28fb5a50a2d8460f317cc5a184768a/duct-ui-client-search-provider-0.8.0.tgz","_integrity":"sha512-sFlbqhXl/xlfAUBVK3xEzD0SjLPSa46u3O8exuJV6Fm3QuvC/Xn4HzaMQs14jb+f/vWwz1T5/lBjqdp88y9VJQ==","repository":{"url":"git+https://github.com/navilan/duct.git","type":"git"},"_npmVersion":"10.9.2","description":"Client-side search provider for Duct UI using FlexSearch","directories":{},"_nodeVersion":"22.16.0","dependencies":{"@duct-ui/search-core":"1.0.0"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","typescript":"^5.9.2"},"_npmOperationalInternal":{"tmp":"tmp/client-search-provider_0.8.0_1756487784770_0.7217754811235975","host":"s3://npm-registry-packages-npm-production"}},"0.8.1":{"name":"@duct-ui/client-search-provider","version":"0.8.1","author":{"name":"navilan","email":"navilan@folds.in"},"license":"MIT","_id":"@duct-ui/client-search-provider@0.8.1","maintainers":[{"name":"navilan.puthir","email":"navilan@puthir.in"}],"homepage":"https://github.com/navilan/duct#readme","bugs":{"url":"https://github.com/navilan/duct/issues"},"dist":{"shasum":"224ae85ff967c76cab5fc072e9f2d74e3a6f3537","tarball":"https://registry.npmjs.org/@duct-ui/client-search-provider/-/client-search-provider-0.8.1.tgz","fileCount":8,"integrity":"sha512-8c6fEzKDdEYvf+rFpkC2TcFRck6ptCamqnl69e6tlzETQAHMDM7ci03ROWxMD48/gxtmU3kw7zuSL8+NS0Ah2g==","signatures":[{"sig":"MEUCIFLYC8UGI/RNIZPkiNPAQMF00OfLusGN7P1dOXBWKclJAiEAmqPEFO89pmga2CvgHIy/4Ka1KkRApNpnfG/11VxVbtY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17346},"main":"./dist/index.js","type":"module","_from":"file:duct-ui-client-search-provider-0.8.1.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc","clean":"rimraf dist"},"_npmUser":{"name":"navilan.puthir","email":"navilan@puthir.in"},"_resolved":"/private/var/folders/mm/7tvfnj1n4kjbgbjpd8rp3ypw0000gn/T/fe1540b21dddb0ddcaaaf1963bf33e45/duct-ui-client-search-provider-0.8.1.tgz","_integrity":"sha512-8c6fEzKDdEYvf+rFpkC2TcFRck6ptCamqnl69e6tlzETQAHMDM7ci03ROWxMD48/gxtmU3kw7zuSL8+NS0Ah2g==","repository":{"url":"git+https://github.com/navilan/duct.git","type":"git"},"_npmVersion":"10.9.2","description":"Client-side search provider for Duct UI using FlexSearch","directories":{},"_nodeVersion":"22.16.0","dependencies":{"@duct-ui/search-core":"0.8.1"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","typescript":"^5.9.2"},"_npmOperationalInternal":{"tmp":"tmp/client-search-provider_0.8.1_1756548908478_0.20408450794897215","host":"s3://npm-registry-packages-npm-production"}},"0.8.2":{"name":"@duct-ui/client-search-provider","version":"0.8.2","author":{"name":"navilan","email":"navilan@folds.in"},"license":"MIT","_id":"@duct-ui/client-search-provider@0.8.2","maintainers":[{"name":"navilan.puthir","email":"navilan@puthir.in"}],"homepage":"https://github.com/navilan/duct#readme","bugs":{"url":"https://github.com/navilan/duct/issues"},"dist":{"shasum":"a02f8a9273c95269bd12eabe50a4e733cfa9e9c4","tarball":"https://registry.npmjs.org/@duct-ui/client-search-provider/-/client-search-provider-0.8.2.tgz","fileCount":8,"integrity":"sha512-CzElhxfMLpfqGvNpcz3+roBQ/4kw5YXXKDDfe9tVvGqPqH40Lpy78ECp4NVUXhPTWBxp7/NA6LLcQYq8C11rjg==","signatures":[{"sig":"MEUCICHZdDDMwBEqi+wgKCcyZo9VKAIV0ObHmScnUGxWswewAiEAvNnpR4jqRUKpYW5vqRXha26Ya/Sey/0NgCwLOgYPDjs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17346},"main":"./dist/index.js","type":"module","_from":"file:duct-ui-client-search-provider-0.8.2.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc","clean":"rimraf dist"},"_npmUser":{"name":"navilan.puthir","email":"navilan@puthir.in"},"_resolved":"/private/var/folders/mm/7tvfnj1n4kjbgbjpd8rp3ypw0000gn/T/b62f0bd09c1028d0c84b32f261aa9f78/duct-ui-client-search-provider-0.8.2.tgz","_integrity":"sha512-CzElhxfMLpfqGvNpcz3+roBQ/4kw5YXXKDDfe9tVvGqPqH40Lpy78ECp4NVUXhPTWBxp7/NA6LLcQYq8C11rjg==","repository":{"url":"git+https://github.com/navilan/duct.git","type":"git"},"_npmVersion":"10.9.2","description":"Client-side search provider for Duct UI using FlexSearch","directories":{},"_nodeVersion":"22.16.0","dependencies":{"@duct-ui/search-core":"0.8.2"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","typescript":"^5.9.2"},"_npmOperationalInternal":{"tmp":"tmp/client-search-provider_0.8.2_1756561788852_0.8252482523017415","host":"s3://npm-registry-packages-npm-production"}},"0.8.3":{"name":"@duct-ui/client-search-provider","version":"0.8.3","author":{"name":"navilan","email":"navilan@folds.in"},"license":"MIT","_id":"@duct-ui/client-search-provider@0.8.3","maintainers":[{"name":"navilan.puthir","email":"navilan@puthir.in"}],"homepage":"https://github.com/navilan/duct#readme","bugs":{"url":"https://github.com/navilan/duct/issues"},"dist":{"shasum":"68839f9273ee89de64466a45548c2bdaa90b6ef7","tarball":"https://registry.npmjs.org/@duct-ui/client-search-provider/-/client-search-provider-0.8.3.tgz","fileCount":8,"integrity":"sha512-ZbxLbFUU0OJ9ki7/jgCWGxhJiMgOFNXLbCuMuoBsxSBzSD/3RXOjjiZJk2D5ecvT4CHGtoigbsRh9osgaHL5rg==","signatures":[{"sig":"MEYCIQDoC5eRyBn8xuDKgqHugrwch5nrT7ZAukDSeEO2qL8/HAIhAI73xLdE9Wd5Oy607vJUiy0uWJQhYtJnH+Zu99ghFYfO","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17346},"main":"./dist/index.js","type":"module","_from":"file:duct-ui-client-search-provider-0.8.3.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc","clean":"rimraf dist"},"_npmUser":{"name":"navilan.puthir","email":"navilan@puthir.in"},"_resolved":"/private/var/folders/mm/7tvfnj1n4kjbgbjpd8rp3ypw0000gn/T/bb3e7d41d79016c6cff15275eda12b2e/duct-ui-client-search-provider-0.8.3.tgz","_integrity":"sha512-ZbxLbFUU0OJ9ki7/jgCWGxhJiMgOFNXLbCuMuoBsxSBzSD/3RXOjjiZJk2D5ecvT4CHGtoigbsRh9osgaHL5rg==","repository":{"url":"git+https://github.com/navilan/duct.git","type":"git"},"_npmVersion":"10.9.2","description":"Client-side search provider for Duct UI using FlexSearch","directories":{},"_nodeVersion":"22.16.0","dependencies":{"@duct-ui/search-core":"0.8.3"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","typescript":"^5.9.2"},"_npmOperationalInternal":{"tmp":"tmp/client-search-provider_0.8.3_1758281740236_0.3684394949555403","host":"s3://npm-registry-packages-npm-production"}},"0.8.4":{"name":"@duct-ui/client-search-provider","version":"0.8.4","description":"Client-side search provider for Duct UI using FlexSearch","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"dependencies":{"@duct-ui/search-core":"0.8.4"},"devDependencies":{"rimraf":"^6.0.1","typescript":"^5.9.2"},"repository":{"type":"git","url":"git+https://github.com/navilan/duct.git"},"author":{"name":"navilan","email":"navilan@folds.in"},"license":"MIT","scripts":{"build":"tsc","clean":"rimraf dist"},"_id":"@duct-ui/client-search-provider@0.8.4","bugs":{"url":"https://github.com/navilan/duct/issues"},"homepage":"https://github.com/navilan/duct#readme","_integrity":"sha512-NdI9z3uCHcEDU5avj7pvNGulY2e6fSJTxy+4ZRS0bLBkbdqVuVg0XiyoxerHH064M0YtiEOJ+P5hc9F16OfVbQ==","_resolved":"/private/var/folders/mm/7tvfnj1n4kjbgbjpd8rp3ypw0000gn/T/69c2bc54bfb5d3c3be65291b1afe9b48/duct-ui-client-search-provider-0.8.4.tgz","_from":"file:duct-ui-client-search-provider-0.8.4.tgz","_nodeVersion":"22.16.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-NdI9z3uCHcEDU5avj7pvNGulY2e6fSJTxy+4ZRS0bLBkbdqVuVg0XiyoxerHH064M0YtiEOJ+P5hc9F16OfVbQ==","shasum":"727fff0b204c3f6c860088e8f9d112c30298e474","tarball":"https://registry.npmjs.org/@duct-ui/client-search-provider/-/client-search-provider-0.8.4.tgz","fileCount":8,"unpackedSize":17346,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICgfwS8t1t1LFAASc+8OlrtEqu0LHJ95MvYv5wt48ceOAiAnRVw+Y7YNsHhoS87MPHjAVFvJfL0m5/nR+ddopOXv0g=="}]},"_npmUser":{"name":"navilan.puthir","email":"navilan@puthir.in"},"directories":{},"maintainers":[{"name":"navilan.puthir","email":"navilan@puthir.in"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/client-search-provider_0.8.4_1758960526297_0.797519094419894"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-29T17:16:24.666Z","modified":"2025-09-27T08:08:46.676Z","0.8.0":"2025-08-29T17:16:24.957Z","0.8.1":"2025-08-30T10:15:08.671Z","0.8.2":"2025-08-30T13:49:49.045Z","0.8.3":"2025-09-19T11:35:40.415Z","0.8.4":"2025-09-27T08:08:46.476Z"},"bugs":{"url":"https://github.com/navilan/duct/issues"},"author":{"name":"navilan","email":"navilan@folds.in"},"license":"MIT","homepage":"https://github.com/navilan/duct#readme","repository":{"type":"git","url":"git+https://github.com/navilan/duct.git"},"description":"Client-side search provider for Duct UI using FlexSearch","maintainers":[{"name":"navilan.puthir","email":"navilan@puthir.in"}],"readme":"# @duct-ui/client-search-provider\n\nClient-side search provider for Duct UI applications using FlexSearch for fast, in-browser full-text search.\n\n## Features\n\n- **Client-side Search**: Fast, in-browser search with no server dependencies\n- **FlexSearch Integration**: Powerful full-text search with relevance scoring\n- **Small Bundle Size**: Lightweight implementation for optimal performance\n- **Fuzzy Matching**: Configurable threshold for typo tolerance\n- **Highlighting Support**: Built-in search result highlighting\n- **TypeScript Support**: Full type safety\n\n## Installation\n\n```bash\nnpm install @duct-ui/client-search-provider\n# or\npnpm add @duct-ui/client-search-provider\n```\n\n## Usage\n\n### Basic Setup\n\n```typescript\nimport { ClientSearchProvider } from '@duct-ui/client-search-provider'\n\n// Create and initialize the provider\nconst searchProvider = new ClientSearchProvider()\n\nawait searchProvider.initialize({\n  indexUrl: '/search-index.json', // URL to your search index\n  threshold: 0.3 // Optional: fuzzy matching threshold (0-1)\n})\n\n// Perform a search\nconst results = await searchProvider.search('your search query', {\n  limit: 10,\n  offset: 0\n})\n\n// Results format:\n// [\n//   {\n//     url: '/page-url',\n//     title: 'Page Title',\n//     excerpt: 'Matching excerpt with <mark>highlighted</mark> terms...',\n//     score: 0.95\n//   }\n// ]\n```\n\n### Search Index Format\n\nYour `search-index.json` should be an array of entries:\n\n```json\n[\n  {\n    \"url\": \"/page-1\",\n    \"title\": \"Page Title\",\n    \"description\": \"Page description\",\n    \"content\": \"Full searchable content of the page\",\n    \"tags\": [\"tag1\", \"tag2\"],\n    \"keywords\": [\"keyword1\", \"keyword2\"]\n  },\n  {\n    \"url\": \"/page-2\",\n    \"title\": \"Another Page\",\n    \"description\": \"Another description\",\n    \"content\": \"More searchable content\",\n    \"tags\": [\"tag3\"],\n    \"keywords\": [\"keyword3\"]\n  }\n]\n```\n\n### With Duct UI Components\n\n```typescript\nimport { createBlueprint, createRef } from '@duct-ui/core'\nimport { SearchModal } from '@duct-ui/components'\nimport { ClientSearchProvider } from '@duct-ui/client-search-provider'\n\nconst searchModalRef = createRef()\nlet searchProvider = null\n\n// Initialize on component mount\nasync function initializeSearch() {\n  searchProvider = new ClientSearchProvider()\n  await searchProvider.initialize({\n    indexUrl: '/search-index.json'\n  })\n}\n\n// Handle search\nasync function performSearch(el, query) {\n  if (!searchProvider || !query.trim()) {\n    searchModalRef.current?.setResults([])\n    return\n  }\n  \n  const results = await searchProvider.search(query)\n  searchModalRef.current?.setResults(results)\n}\n\n// Component render\nfunction render(props) {\n  return (\n    <SearchModal\n      ref={searchModalRef}\n      placeholder=\"Search...\"\n      on:search={performSearch}\n    />\n  )\n}\n```\n\n### Advanced Options\n\n```typescript\nconst searchProvider = new ClientSearchProvider()\n\n// Initialize with custom configuration\nawait searchProvider.initialize({\n  indexUrl: '/search-index.json',\n  threshold: 0.3, // Fuzzy matching (0 = exact, 1 = very fuzzy)\n  limit: 10, // Default result limit\n  highlightTag: 'mark', // HTML tag for highlighting\n  highlightClass: 'highlight' // CSS class for highlights\n})\n\n// Search with options\nconst results = await searchProvider.search('query', {\n  limit: 20, // Override default limit\n  offset: 10, // For pagination\n  fuzzy: true, // Enable fuzzy matching\n  highlight: true // Enable highlighting\n})\n```\n\n## Search Index Generation\n\n### During Build (Static Sites)\n\nGenerate the search index during your build process:\n\n```javascript\n// build-search-index.js\nimport fs from 'fs'\nimport path from 'path'\n\nconst pages = []\n\n// Collect your pages (example for markdown files)\nconst contentDir = './content'\nconst files = fs.readdirSync(contentDir)\n\nfor (const file of files) {\n  const content = fs.readFileSync(path.join(contentDir, file), 'utf-8')\n  // Parse your content (markdown, HTML, etc.)\n  \n  pages.push({\n    url: `/posts/${file.replace('.md', '')}`,\n    title: extractTitle(content),\n    description: extractDescription(content),\n    content: extractText(content),\n    tags: extractTags(content),\n    keywords: extractKeywords(content)\n  })\n}\n\n// Write the index\nfs.writeFileSync(\n  './dist/search-index.json',\n  JSON.stringify(pages)\n)\n```\n\n### With Duct SSG\n\nDuct's static site generator can automatically generate the search index:\n\n```javascript\n// duct.config.js\nexport default {\n  build: {\n    searchIndex: {\n      enabled: true,\n      output: 'search-index.json',\n      include: ['**/*.html'],\n      exclude: ['404.html']\n    }\n  }\n}\n```\n\n## Performance Considerations\n\n### Index Size\n\n- **< 100 entries**: Excellent performance, instant results\n- **100-1000 entries**: Good performance, < 50ms search time\n- **1000-5000 entries**: Acceptable performance, < 200ms search time\n- **> 5000 entries**: Consider server-side search or index splitting\n\n### Optimization Tips\n\n1. **Preload Index**: Load the index on page load for instant first search\n2. **Compress Content**: Minimize content field to essential text\n3. **Use Keywords**: Leverage keywords field for important terms\n4. **Lazy Loading**: Load the search provider only when needed\n\n```typescript\n// Lazy load search provider\nlet searchProvider = null\n\nasync function getSearchProvider() {\n  if (!searchProvider) {\n    const { ClientSearchProvider } = await import('@duct-ui/client-search-provider')\n    searchProvider = new ClientSearchProvider()\n    await searchProvider.initialize({\n      indexUrl: '/search-index.json'\n    })\n  }\n  return searchProvider\n}\n\n// Use when needed\nasync function handleSearch(query) {\n  const provider = await getSearchProvider()\n  return provider.search(query)\n}\n```\n\n## Comparison with CloudflareSearchProvider\n\n| Feature | ClientSearchProvider | CloudflareSearchProvider |\n|---------|---------------------|-------------------------|\n| Location | Browser | Cloudflare Worker |\n| Index Size Limit | ~5MB practical | Unlimited |\n| Network Requests | Initial index download | Per search query |\n| Latency | Instant after load | Network dependent |\n| Offline Support | Yes | No |\n| Server Costs | None | Cloudflare Workers |\n| Best For | Small-medium sites | Large sites |\n\n## Fallback Strategy\n\nImplement graceful fallback between providers:\n\n```typescript\nimport { ClientSearchProvider } from '@duct-ui/client-search-provider'\nimport { CloudflareSearchProvider } from '@duct-ui/cloudflare-search-provider'\n\nclass SearchProviderWithFallback {\n  constructor() {\n    this.providers = []\n  }\n  \n  async initialize() {\n    // Try server-side first (faster for large indexes)\n    try {\n      const cloudflare = new CloudflareSearchProvider()\n      await cloudflare.initialize({ \n        workerUrl: '/api',\n        timeout: 5000\n      })\n      this.providers.push(cloudflare)\n    } catch (e) {\n      console.log('Server search unavailable')\n    }\n    \n    // Always have client-side as fallback\n    try {\n      const client = new ClientSearchProvider()\n      await client.initialize({\n        indexUrl: '/search-index.json'\n      })\n      this.providers.push(client)\n    } catch (e) {\n      console.error('Client search failed to initialize')\n    }\n  }\n  \n  async search(query, options) {\n    for (const provider of this.providers) {\n      try {\n        return await provider.search(query, options)\n      } catch (e) {\n        continue // Try next provider\n      }\n    }\n    return [] // No providers available\n  }\n}\n```\n\n## TypeScript Support\n\nFull TypeScript support with exported types:\n\n```typescript\nimport type {\n  SearchProvider,\n  SearchResult,\n  SearchOptions,\n  SearchIndexEntry\n} from '@duct-ui/client-search-provider'\n```\n\n## Browser Support\n\n- Chrome 90+\n- Firefox 88+\n- Safari 14+\n- Edge 90+\n\n## License\n\nMIT","readmeFilename":"README.md"}