{"id":47797589,"url":"https://github.com/searchcraft-inc/searchcraft-client-js","last_synced_at":"2026-04-03T16:36:51.326Z","repository":{"id":339529244,"uuid":"1133997484","full_name":"searchcraft-inc/searchcraft-client-js","owner":"searchcraft-inc","description":"TypeScript client library for the Searchcraft API","archived":false,"fork":false,"pushed_at":"2026-03-13T03:47:44.000Z","size":1310,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"development","last_synced_at":"2026-03-13T10:55:07.836Z","etag":null,"topics":["api-client","browser","esm","federated-search","full-text-search","javascript","nodejs","query-builder","sdk","search","search-engine","searchcraft","typescript","zero-dependencies"],"latest_commit_sha":null,"homepage":"https://searchcraft.io/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/searchcraft-inc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":"CLA.md"}},"created_at":"2026-01-14T05:18:17.000Z","updated_at":"2026-02-24T04:50:49.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/searchcraft-inc/searchcraft-client-js","commit_stats":null,"previous_names":["searchcraft-inc/searchcraft-client-js"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/searchcraft-inc/searchcraft-client-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/searchcraft-inc%2Fsearchcraft-client-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/searchcraft-inc%2Fsearchcraft-client-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/searchcraft-inc%2Fsearchcraft-client-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/searchcraft-inc%2Fsearchcraft-client-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/searchcraft-inc","download_url":"https://codeload.github.com/searchcraft-inc/searchcraft-client-js/tar.gz/refs/heads/development","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/searchcraft-inc%2Fsearchcraft-client-js/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31364537,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T15:19:21.178Z","status":"ssl_error","status_checked_at":"2026-04-03T15:19:20.670Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-client","browser","esm","federated-search","full-text-search","javascript","nodejs","query-builder","sdk","search","search-engine","searchcraft","typescript","zero-dependencies"],"created_at":"2026-04-03T16:36:50.565Z","updated_at":"2026-04-03T16:36:51.304Z","avatar_url":"https://github.com/searchcraft-inc.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg alt=\"Searchcraft\" src=\"https://raw.githubusercontent.com/searchcraft-inc/searchcraft-client-js/main/header.png\"\u003e\n\n\u003ch1 align=\"center\"\u003eSearchcraft TypeScript Client\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\nA TypeScript client library for the \u003ca href=\"https://searchcraft.io\"\u003eSearchcraft\u003c/a\u003e API.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://docs.searchcraft.io\"\u003e\u003cstrong\u003eDocumentation\u003c/strong\u003e\u003c/a\u003e ·\n  \u003ca href=\"https://searchcraft.io\"\u003e\u003cstrong\u003eWebsite\u003c/strong\u003e\u003c/a\u003e ·\n  \u003ca href=\"https://github.com/searchcraft-inc/searchcraft-client-js/issues\"\u003e\u003cstrong\u003eIssues\u003c/strong\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.typescriptlang.org/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/TypeScript-5.9-blue.svg?logo=typescript\u0026style=flat\" alt=\"TypeScript\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://nodejs.org/en/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Node.js-18+-339933.svg?logo=node.js\u0026style=flat\" alt=\"Node.js\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/searchcraft-inc/searchcraft-client-js/blob/main/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/License-Apache_2.0-blue.svg?style=flat\" alt=\"License\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Features\n\n- Full TypeScript support with comprehensive type definitions.\n- Works with JavaScript projects as well.\n- Functional, immutable API design.\n- Composable query builder.\n- Support for all Searchcraft query modes (fuzzy, exact, dynamic) and operations.\n- Full index, federation, synonyms, and stopwords management.\n- Complete query language support.\n- Works in both Node.js and browser environments.\n- Available via NPM and CDN.\n- Zero runtime dependencies.\n\n## Installation\n\n### NPM\n\n```bash\nnpm install @searchcraft/client\n```\n\n### Yarn\n\n```bash\nyarn add @searchcraft/client\n```\n\n### PNPM\n\n```bash\npnpm add @searchcraft/client\n```\n\n### CDN (UMD)\n\n```html\n\u003cscript src=\"https://unpkg.com/@searchcraft/client/dist/index.umd.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  const { createClient, createApiKey, createIndexName } = SearchcraftClient;\n\u003c/script\u003e\n```\n\n## Quick Start\n\n```typescript\nimport { createClient, createApiKey, createIndexName, fuzzy } from '@searchcraft/client';\n\n// Initialize the client\nconst client = createClient({\n  endpointUrl: 'https://your-searchcraft-instance.com',\n  readKey: createApiKey('your-read-key'),\n});\n\n// Perform a search\nconst indexName = createIndexName('your-index');\nconst request = fuzzy().term('search query').limit(10).buildRequest();\n\nconst response = await client.search.searchIndex(indexName, request);\n\nconsole.log(`Found ${response.data.count} results`);\nconsole.log(response.data.hits);\n```\n\n## Usage\n\n### Client Initialization\n\n```typescript\nimport { createClient, createApiKey } from '@searchcraft/client';\n\nconst client = createClient({\n  endpointUrl: 'https://your-instance.com',\n  readKey: createApiKey('your-read-key'),\n  ingestKey: createApiKey('your-ingest-key'), // Optional, for document operations\n  adminKey: createApiKey('your-admin-key'), // Optional, only needed for self-hosted clusters\n  timeout: 30000, // Optional, default 30 seconds\n  headers: {\n    // Optional Searchcraft headers for analytics and tracking:\n    'X-Sc-User-Id': 'user-123', // Unique identifier for the current user\n    'X-Sc-Session-Id': 'session-abc', // Session identifier for tracking user sessions\n    'X-Sc-User-Type': 'authenticated', // User type: 'authenticated' or 'anonymous'\n  },\n});\n```\n\n\u003e **Note:** The `adminKey` is only required for self-hosted Searchcraft clusters. If you're using Searchcraft Cloud, you do not need to provide an admin key.\n\n### Basic Search\n\n```typescript\nimport { fuzzy, exact, dynamic } from '@searchcraft/client';\n\n// Fuzzy search (typo-tolerant, uses weight ranking, synonyms and stopwords)\nconst fuzzyQuery = fuzzy().term('search term').limit(20).buildRequest();\n\n// Exact search\nconst exactQuery = exact().term('exact match').buildRequest();\n\n// Dynamic search (adapts based on word count)\nconst dynamicQuery = dynamic().term('adaptive search').buildRequest();\n\nconst response = await client.search.searchIndex(indexName, fuzzyQuery);\n```\n\n### Query Builder\n\nThe query builder provides a fluent, immutable API for constructing complex queries.\n\n\u003e **📚 Full Query Syntax Documentation:** For complete details on the Searchcraft query language, operators, and advanced features, see the [official documentation](https://docs.searchcraft.io).\n\n```typescript\nimport { exact } from '@searchcraft/client';\n\nconst query = exact()\n  .field('category', 'electronics')\n  .and(exact().range('price', 10, 100))\n  .not('discontinued')\n  .orderBy('rating', 'desc')\n  .limit(25)\n  .buildRequest();\n\nconst response = await client.search.searchIndex(indexName, query);\n```\n\n### Field Queries\n\n```typescript\n// Simple field match\nexact().field('title', 'laptop').buildRequest();\n\n// IN query\nexact().fieldIn('tags', ['tech', 'gadgets', 'mobile']).buildRequest();\n\n// Range query\nexact().range('price', 10, 100).buildRequest(); // Inclusive\nexact().range('price', 10, 100, false).buildRequest(); // Exclusive\n\n// Comparison queries\nexact().compare('rating', '\u003e', 4.5).buildRequest();\nexact().compare('stock', '\u003c=', 10).buildRequest();\n```\n\n### Date Queries\n\n```typescript\nconst from = new Date('2024-01-01');\nconst to = new Date('2024-12-31');\n\nexact().range('created_at', from, to).buildRequest();\nexact().compare('updated_at', '\u003e=', new Date('2024-06-01')).buildRequest();\n```\n\n### Boolean Operators\n\n```typescript\n// AND\nexact().field('active', true).and(exact().compare('price', '\u003c', 100)).buildRequest();\n\n// OR\nexact().field('brand', 'Apple').or(exact().field('brand', 'Samsung')).buildRequest();\n\n// NOT (exclusion)\nfuzzy().term('laptop').not('refurbished').buildRequest();\n\n// Grouping\nconst subQuery = exact().field('category', 'tech').or('category:science');\nexact().group(subQuery).and(exact().compare('rating', '\u003e', 4)).buildRequest();\n```\n\n### Pagination and Sorting\n\n```typescript\nfuzzy()\n  .term('query')\n  .limit(20)\n  .offset(40)\n  .orderBy('created_at', 'desc')\n  .buildRequest();\n```\n\n### Federated Search\n\nSearch across multiple indices:\n\n```typescript\nimport { createFederationName } from '@searchcraft/client';\n\nconst federationName = createFederationName('my-federation');\nconst request = fuzzy().term('search term').buildRequest();\n\nconst response = await client.search.searchFederation(federationName, request);\n```\n\n### Advanced: Raw Query Objects\n\nFor complex queries or when you need maximum control, you can construct request objects directly instead of using the query builder:\n\n```typescript\nconst request = {\n  query: [\n    { occur: 'must', exact: { ctx: 'active:true' } },\n    { occur: 'must', exact: { ctx: 'category:/electronics' } },\n    { fuzzy: { ctx: 'wireless headphones' } },\n  ],\n  limit: 20,\n};\n\nconst response = await client.search.searchIndex(indexName, request);\n```\n\n**Note:** The query builder (e.g., `fuzzy().term()...`) is recommended for most use cases. Use raw objects when you need to dynamically construct complex queries or have specific requirements the builder doesn't cover.\n\n### Document Management\n\n```typescript\nimport { createIndexName } from '@searchcraft/client';\n\nconst indexName = createIndexName('my-index');\n\n// Insert a document\nawait client.documents.insert(indexName, {\n  id: '123',\n  title: 'Product Title',\n  description: 'Product description',\n  price: 99.99,\n});\n\n// Delete a document by its id\nawait client.documents.delete(indexName, '123');\n\n// Batch insert documents\nawait client.documents.batchInsert(indexName, [\n  { id: '1', title: 'Product 1' },\n  { id: '2', title: 'Product 2' },\n]);\n\n// Batch delete documents by their ids\nawait client.documents.batchDelete(indexName, ['1', '2']);\n\n// Delete all documents from an index\nawait client.documents.deleteAll(indexName);\n```\n\n### Index Management\n\n```typescript\nimport { createIndexName } from '@searchcraft/client';\n\nconst indexName = createIndexName('my-index');\n\n// List all index names\nconst { index_names } = await client.indices.list();\n\n// Get index configuration\nconst config = await client.indices.get(indexName);\n\n// Create a new index\nawait client.indices.create(indexName, {\n  search_fields: ['title', 'body'],\n  fields: {\n    title: { type: 'text', indexed: true, stored: true },\n    body: { type: 'text', indexed: true, stored: true },\n  },\n});\n\n// Update index configuration (partial)\nawait client.indices.update(indexName, {\n  weight_multipliers: { title: 2.0, body: 0.7 },\n});\n\n// Delete an index\nawait client.indices.delete(indexName);\n```\n\n### Federation Management\n\n```typescript\nimport { createFederationName } from '@searchcraft/client';\n\nconst federationName = createFederationName('my-federation');\n\n// List all federations\nconst federations = await client.federations.list();\n\n// Get a specific federation\nconst federation = await client.federations.get(federationName);\n\n// Delete a federation\nawait client.federations.delete(federationName);\n```\n\n### Synonyms\n\n```typescript\n// Get all synonyms for an index\nconst synonyms = await client.synonyms.get(indexName);\n\n// Add synonyms — format: \"synonym:original-term\" or \"many,synonyms:original,terms\"\nawait client.synonyms.add(indexName, [\n  'nyc:new york city',\n  'usa:united states',\n]);\n\n// Delete specific synonyms by key\nawait client.synonyms.delete(indexName, ['nyc', 'usa']);\n\n// Delete all synonyms\nawait client.synonyms.deleteAll(indexName);\n```\n\n### Stopwords\n\n```typescript\n// Get all stopwords for an index\nconst stopwords = await client.stopwords.get(indexName);\n\n// Add custom stopwords\nawait client.stopwords.add(indexName, ['foo', 'bar']);\n\n// Delete specific stopwords\nawait client.stopwords.delete(indexName, ['foo', 'bar']);\n\n// Delete all custom stopwords\nawait client.stopwords.deleteAll(indexName);\n```\n\n### Health Check\n\n```typescript\n// check() throws on error, so if it returns the service is healthy\nconst health = await client.health.check();\nconsole.log('Status:', health.status);   // 200\nconsole.log('Message:', health.data);    // \"Searchcraft is healthy.\"\n```\n\n## API Reference\n\n### Client\n\n- `createClient(config: SearchcraftConfig): SearchcraftClient`\n\n### Search API\n\n- `searchIndex\u003cT\u003e(indexName: IndexName, request: SearchRequest): Promise\u003cSearchResponse\u003cT\u003e\u003e`\n- `searchFederation\u003cT\u003e(federationName: FederationName, request: SearchRequest): Promise\u003cSearchResponse\u003cT\u003e\u003e`\n\n### Document API\n\n- `insert(indexName: IndexName, document: DocumentWithId): Promise\u003cDocumentOperationResponse\u003e` - Insert a document\n- `delete(indexName: IndexName, documentId: string | number): Promise\u003cDocumentDeleteResponse\u003e` - Delete a document by id\n- `batchInsert(indexName: IndexName, documents: DocumentWithId[]): Promise\u003cDocumentOperationResponse\u003e` - Batch insert documents\n- `batchDelete(indexName: IndexName, documentIds: (string | number)[]): Promise\u003cDocumentDeleteResponse\u003e` - Batch delete documents by ids\n- `deleteAll(indexName: IndexName): Promise\u003cDocumentOperationResponse\u003e` - Delete all documents from an index\n- `get\u003cT\u003e(indexName: IndexName, internalId: string): Promise\u003cSearchHit\u003cT\u003e\u003e` - Get a document by its internal Searchcraft ID (_id)\n\n### Index API\n\n- `list(): Promise\u003cIndexListResponse\u003e` - List all index names\n- `get(indexName: IndexName): Promise\u003cIndexConfig\u003e` - Get the configuration for a specific index\n- `create(indexName: IndexName, indexConfig: IndexConfig): Promise\u003cIndexOperationResponse\u003e` - Create a new index\n- `update(indexName: IndexName, indexConfig: Partial\u003cIndexConfig\u003e): Promise\u003cIndexOperationResponse\u003e` - Update an existing index (partial)\n- `delete(indexName: IndexName): Promise\u003cIndexOperationResponse\u003e` - Delete an index\n\n### Federation API\n\n- `list(): Promise\u003cFederation[]\u003e` - List all federations\n- `get(federationName: FederationName): Promise\u003cFederation\u003e` - Get a specific federation\n- `delete(federationName: FederationName): Promise\u003cFederationOperationResponse\u003e` - Delete a federation\n\n### Synonyms API\n\n- `get(indexName: IndexName): Promise\u003cSynonymsMap\u003e` - Get all synonyms for an index\n- `add(indexName: IndexName, synonyms: string[]): Promise\u003cSynonymOperationResponse\u003e` - Add synonyms in `\"synonym:original-term\"` format\n- `delete(indexName: IndexName, synonyms: string[]): Promise\u003cSynonymOperationResponse\u003e` - Delete specific synonyms by key\n- `deleteAll(indexName: IndexName): Promise\u003cSynonymOperationResponse\u003e` - Delete all synonyms from an index\n\n### Stopwords API\n\n- `get(indexName: IndexName): Promise\u003cstring[]\u003e` - Get all stopwords for an index\n- `add(indexName: IndexName, stopwords: string[]): Promise\u003cStopwordOperationResponse\u003e` - Add custom stopwords\n- `delete(indexName: IndexName, stopwords: string[]): Promise\u003cStopwordOperationResponse\u003e` - Delete specific stopwords\n- `deleteAll(indexName: IndexName): Promise\u003cStopwordOperationResponse\u003e` - Delete all stopwords from an index\n\n### Health API\n\n- `check(): Promise\u003cHealthCheckResponse\u003e`\n\n### Query Builder\n\n- `fuzzy(): QueryBuilder` - Create a fuzzy query builder\n- `exact(): QueryBuilder` - Create an exact query builder\n- `dynamic(): QueryBuilder` - Create a dynamic query builder\n\n#### QueryBuilder Methods\n\nAll methods return a new QueryBuilder instance (immutable):\n\n- `term(term: string): QueryBuilder` - Add a search term\n- `field(field: string, value: string | number | boolean): QueryBuilder` - Add field:value query\n- `fieldIn(field: string, values: (string | number)[]): QueryBuilder` - Add field IN query\n- `range(field: string, from: string | number | Date, to: string | number | Date, inclusive?: boolean): QueryBuilder` - Add range query\n- `compare(field: string, operator: '\u003e' | '\u003c' | '\u003e=' | '\u003c=', value: number | Date): QueryBuilder` - Add comparison query\n- `and(query: string | QueryBuilder): QueryBuilder` - Add AND operator\n- `or(query: string | QueryBuilder): QueryBuilder` - Add OR operator\n- `not(term: string): QueryBuilder` - Add NOT operator\n- `group(query: string | QueryBuilder): QueryBuilder` - Group query with parentheses\n- `limit(limit: number): QueryBuilder` - Set result limit\n- `offset(offset: number): QueryBuilder` - Set result offset\n- `orderBy(field: string, sort?: 'asc' | 'desc'): QueryBuilder` - Set ordering\n- `occur(occur: 'should' | 'must'): QueryBuilder` - Set occur mode\n- `build(): SearchQuery` - Build the query object\n- `buildRequest(): SearchRequest` - Build the complete request object\n\n## Type Safety\n\nThe library uses **branded types** for enhanced compile-time type safety. Branded types prevent you from accidentally mixing up different string-based identifiers.\n\n### Benefits of Branded Types\n\n- ✅ **Compile-time safety** - TypeScript catches type mismatches before runtime\n- ✅ **Prevents mixing types** - Can't accidentally use an `IndexName` where a `FederationName` is expected\n- ✅ **IDE support** - Better autocomplete and inline error detection\n- ✅ **Zero runtime overhead** - Types are erased during compilation\n- ✅ **Works in JavaScript** - Functions work normally, just without compile-time checks\n\n### Usage\n\n```typescript\nimport {\n  createApiKey,\n  createIndexName,\n  createFederationName,\n  createDocumentId\n} from '@searchcraft/client';\n\nconst apiKey = createApiKey('key');                    // ApiKey type\nconst indexName = createIndexName('my-index');         // IndexName type\nconst federationName = createFederationName('my-fed'); // FederationName type\nconst docId = createDocumentId('123');                 // DocumentId type\n\n// ✅ TypeScript ensures you use the correct type\nawait client.search.searchIndex(indexName, request);\nawait client.search.searchFederation(federationName, request);\n\n// ❌ TypeScript error - prevents mistakes at compile time\nawait client.search.searchFederation(indexName, request); // Error!\n```\n\n## Error Handling\n\n```typescript\nimport {\n  SearchcraftError,\n  AuthenticationError,\n  NotFoundError,\n  ValidationError,\n  NetworkError,\n  ApiError,\n} from '@searchcraft/client';\n\ntry {\n  const response = await client.search.searchIndex(indexName, request);\n} catch (error) {\n  if (error instanceof AuthenticationError) {\n    console.error('Authentication failed');\n  } else if (error instanceof ValidationError) {\n    console.error('Validation error:', error.field);\n  } else if (error instanceof NetworkError) {\n    console.error('Network error');\n  }\n}\n```\n\n## Documentation\n\nFor comprehensive documentation on Searchcraft, including:\n\n- **Query Language Reference** - Complete syntax and operators\n- **API Reference** - All endpoints and parameters\n- **Search Guides** - Best practices and advanced techniques\n- **Integration Examples** - Real-world use cases\n\nVisit the official documentation at **[docs.searchcraft.io](https://docs.searchcraft.io)**\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Run tests\nnpm test\n\n# Run tests with coverage\nnpm run test:coverage\n\n# Build\nnpm run build\n\n# Lint\nnpm run lint\n\n# Format\nnpm run format\n```\n\n## Issues\n\nPlease file issues in the [Searchcraft Issues](https://github.com/searchcraft-inc/searchcraft-issues) repository.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsearchcraft-inc%2Fsearchcraft-client-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsearchcraft-inc%2Fsearchcraft-client-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsearchcraft-inc%2Fsearchcraft-client-js/lists"}