@skawr/search 0.1.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/README.md +90 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @skawr/search
|
|
2
|
+
|
|
3
|
+
Official SKAWR browser search SDK — lightweight, search-only client safe for frontend use.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @skawr/search
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
ESM only. ~1.7 KB minified. Zero runtime dependencies.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { SkawrSearch } from '@skawr/search';
|
|
17
|
+
|
|
18
|
+
const search = new SkawrSearch({
|
|
19
|
+
publicKey: 'your-public-key',
|
|
20
|
+
baseUrl: 'https://api.skawr.com', // optional
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Search
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
const results = await search.search('laptop', {
|
|
30
|
+
filters: { category: 'electronics', min_price: 100 },
|
|
31
|
+
page: 1,
|
|
32
|
+
per_page: 20,
|
|
33
|
+
sort_by: 'relevance',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
console.log(results.total_results);
|
|
37
|
+
results.results.forEach((item) => {
|
|
38
|
+
console.log(item.title, item.price);
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Suggestions
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
const suggestions = await search.suggest('lap', {
|
|
46
|
+
include_trending: true,
|
|
47
|
+
limit: 10,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
console.log(suggestions.query_completions); // ['laptop', 'laptop bag', ...]
|
|
51
|
+
console.log(suggestions.trending_queries); // ['phone', 'tablet', ...]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Autocomplete
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const { suggestions } = await search.autocomplete('lap', { limit: 5 });
|
|
58
|
+
console.log(suggestions); // ['laptop', 'laptop case', 'laptop stand']
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Configuration
|
|
62
|
+
|
|
63
|
+
| Option | Type | Default | Description |
|
|
64
|
+
|---|---|---|---|
|
|
65
|
+
| `publicKey` | `string` | *required* | Your SKAWR public/search key |
|
|
66
|
+
| `baseUrl` | `string` | `https://api.skawr.com` | API base URL |
|
|
67
|
+
| `timeout` | `number` | `10000` | Request timeout in ms |
|
|
68
|
+
|
|
69
|
+
## Error Handling
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { SearchError } from '@skawr/search';
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
await search.search('test');
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error instanceof SearchError) {
|
|
78
|
+
console.log(error.status); // HTTP status code
|
|
79
|
+
console.log(error.detail); // Error message from API
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Browser Support
|
|
85
|
+
|
|
86
|
+
Works in all modern browsers with native `fetch` support (Chrome 42+, Firefox 39+, Safari 10.1+, Edge 14+).
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
interface SkawrSearchConfig {
|
|
2
|
+
publicKey: string;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
timeout?: number;
|
|
5
|
+
}
|
|
6
|
+
interface SearchFilters {
|
|
7
|
+
min_price?: number;
|
|
8
|
+
max_price?: number;
|
|
9
|
+
source?: string[];
|
|
10
|
+
category?: string;
|
|
11
|
+
custom_attributes?: Record<string, unknown>;
|
|
12
|
+
boost_fields?: Record<string, number>;
|
|
13
|
+
}
|
|
14
|
+
type SortBy = 'relevance' | 'price_asc' | 'price_desc' | 'date_desc';
|
|
15
|
+
interface SearchOptions {
|
|
16
|
+
index?: string;
|
|
17
|
+
filters?: SearchFilters;
|
|
18
|
+
page?: number;
|
|
19
|
+
per_page?: number;
|
|
20
|
+
sort_by?: SortBy;
|
|
21
|
+
highlight_matches?: boolean;
|
|
22
|
+
result_format?: 'standard' | 'minimal' | 'detailed';
|
|
23
|
+
}
|
|
24
|
+
interface SearchResult {
|
|
25
|
+
id: string;
|
|
26
|
+
title: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
price?: number;
|
|
29
|
+
currency?: string;
|
|
30
|
+
image_url?: string;
|
|
31
|
+
product_url?: string;
|
|
32
|
+
source?: string;
|
|
33
|
+
store_name?: string;
|
|
34
|
+
category?: string;
|
|
35
|
+
score?: number;
|
|
36
|
+
highlighted_title?: string;
|
|
37
|
+
highlighted_description?: string;
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
interface FacetCount {
|
|
41
|
+
value: string;
|
|
42
|
+
count: number;
|
|
43
|
+
}
|
|
44
|
+
interface Facet {
|
|
45
|
+
field_name: string;
|
|
46
|
+
counts: FacetCount[];
|
|
47
|
+
}
|
|
48
|
+
interface SearchResponse {
|
|
49
|
+
results: SearchResult[];
|
|
50
|
+
total_results: number;
|
|
51
|
+
page: number;
|
|
52
|
+
per_page: number;
|
|
53
|
+
total_pages: number;
|
|
54
|
+
took_ms: number;
|
|
55
|
+
facets?: Facet[];
|
|
56
|
+
suggestions?: string[];
|
|
57
|
+
search_id: string;
|
|
58
|
+
}
|
|
59
|
+
interface SuggestOptions {
|
|
60
|
+
index?: string;
|
|
61
|
+
limit?: number;
|
|
62
|
+
include_trending?: boolean;
|
|
63
|
+
}
|
|
64
|
+
interface SuggestionsResponse {
|
|
65
|
+
query_completions: string[];
|
|
66
|
+
trending_queries?: string[];
|
|
67
|
+
category_suggestions?: string[];
|
|
68
|
+
}
|
|
69
|
+
interface AutocompleteOptions {
|
|
70
|
+
limit?: number;
|
|
71
|
+
}
|
|
72
|
+
interface AutocompleteResponse {
|
|
73
|
+
suggestions: string[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
declare class SkawrSearch {
|
|
77
|
+
private readonly baseUrl;
|
|
78
|
+
private readonly publicKey;
|
|
79
|
+
private readonly timeout;
|
|
80
|
+
constructor(config: SkawrSearchConfig);
|
|
81
|
+
search(query: string, options?: SearchOptions): Promise<SearchResponse>;
|
|
82
|
+
suggest(query: string, options?: SuggestOptions): Promise<SuggestionsResponse>;
|
|
83
|
+
autocomplete(query: string, options?: AutocompleteOptions): Promise<AutocompleteResponse>;
|
|
84
|
+
private get;
|
|
85
|
+
private post;
|
|
86
|
+
private request;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
declare class SearchError extends Error {
|
|
90
|
+
readonly status: number;
|
|
91
|
+
readonly detail: string;
|
|
92
|
+
constructor(message: string, status: number, detail?: string);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export { type AutocompleteOptions, type AutocompleteResponse, type Facet, type FacetCount, SearchError, type SearchFilters, type SearchOptions, type SearchResponse, type SearchResult, SkawrSearch, type SkawrSearchConfig, type SortBy, type SuggestOptions, type SuggestionsResponse };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var n=class extends Error{status;detail;constructor(t,e,r){super(t),this.name="SearchError",this.status=e,this.detail=r??t;}};var u="https://api.skawr.com",g=1e4,a=class{baseUrl;publicKey;timeout;constructor(t){if(!t.publicKey)throw new Error("publicKey is required");this.baseUrl=(t.baseUrl??u).replace(/\/+$/,""),this.publicKey=t.publicKey,this.timeout=t.timeout??g;}async search(t,e){return this.post("/api/v1/search",{query:t,filters:e?.filters,page:e?.page,per_page:e?.per_page,sort_by:e?.sort_by,highlight_matches:e?.highlight_matches??true,result_format:e?.result_format})}async suggest(t,e){let r={q:t};return e?.limit!==void 0&&(r.limit=String(e.limit)),e?.include_trending!==void 0&&(r.include_trending=String(e.include_trending)),this.get("/api/v1/search/suggestions",r)}async autocomplete(t,e){let r={q:t};return e?.limit!==void 0&&(r.limit=String(e.limit)),this.get("/api/v1/autocomplete",r)}async get(t,e){let r=`${this.baseUrl}${t}`;if(e){let s=new URLSearchParams(e).toString();s&&(r+=`?${s}`);}return this.request(r,{method:"GET"})}async post(t,e){return this.request(`${this.baseUrl}${t}`,{method:"POST",body:JSON.stringify(e)})}async request(t,e){let r={"X-API-Key":this.publicKey,Accept:"application/json"};e.body&&(r["Content-Type"]="application/json");let s;try{s=await fetch(t,{...e,headers:r,signal:AbortSignal.timeout(this.timeout)});}catch(i){throw i instanceof DOMException&&i.name==="TimeoutError"?new n("Request timed out",0,"Request timed out"):i}if(!s.ok){let i=await s.text(),o;try{let c=JSON.parse(i);o=typeof c.detail=="string"?c.detail:i;}catch{o=i||s.statusText;}throw new n(o,s.status,o)}return await s.json()}};
|
|
2
|
+
export{n as SearchError,a as SkawrSearch};//# sourceMappingURL=index.js.map
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":["SearchError","message","status","detail","DEFAULT_BASE_URL","DEFAULT_TIMEOUT","SkawrSearch","config","query","options","params","path","url","qs","body","init","headers","response","error","text","parsed"],"mappings":"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","file":"index.js","sourcesContent":["export class SearchError extends Error {\n readonly status: number;\n readonly detail: string;\n\n constructor(message: string, status: number, detail?: string) {\n super(message);\n this.name = 'SearchError';\n this.status = status;\n this.detail = detail ?? message;\n }\n}\n","import { SearchError } from './errors.js';\nimport type {\n SkawrSearchConfig,\n SearchOptions,\n SearchResponse,\n SuggestOptions,\n SuggestionsResponse,\n AutocompleteOptions,\n AutocompleteResponse,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.skawr.com';\nconst DEFAULT_TIMEOUT = 10_000;\n\nexport class SkawrSearch {\n private readonly baseUrl: string;\n private readonly publicKey: string;\n private readonly timeout: number;\n\n constructor(config: SkawrSearchConfig) {\n if (!config.publicKey) {\n throw new Error('publicKey is required');\n }\n\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.publicKey = config.publicKey;\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n }\n\n async search(query: string, options?: SearchOptions): Promise<SearchResponse> {\n return this.post<SearchResponse>('/api/v1/search', {\n query,\n filters: options?.filters,\n page: options?.page,\n per_page: options?.per_page,\n sort_by: options?.sort_by,\n highlight_matches: options?.highlight_matches ?? true,\n result_format: options?.result_format,\n });\n }\n\n async suggest(query: string, options?: SuggestOptions): Promise<SuggestionsResponse> {\n const params: Record<string, string> = { q: query };\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.include_trending !== undefined) params.include_trending = String(options.include_trending);\n\n return this.get<SuggestionsResponse>('/api/v1/search/suggestions', params);\n }\n\n async autocomplete(query: string, options?: AutocompleteOptions): Promise<AutocompleteResponse> {\n const params: Record<string, string> = { q: query };\n if (options?.limit !== undefined) params.limit = String(options.limit);\n\n return this.get<AutocompleteResponse>('/api/v1/autocomplete', params);\n }\n\n private async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n let url = `${this.baseUrl}${path}`;\n if (params) {\n const qs = new URLSearchParams(params).toString();\n if (qs) url += `?${qs}`;\n }\n\n return this.request<T>(url, { method: 'GET' });\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n return this.request<T>(`${this.baseUrl}${path}`, {\n method: 'POST',\n body: JSON.stringify(body),\n });\n }\n\n private async request<T>(url: string, init: RequestInit): Promise<T> {\n const headers: Record<string, string> = {\n 'X-API-Key': this.publicKey,\n 'Accept': 'application/json',\n };\n\n if (init.body) {\n headers['Content-Type'] = 'application/json';\n }\n\n let response: Response;\n try {\n response = await fetch(url, {\n ...init,\n headers,\n signal: AbortSignal.timeout(this.timeout),\n });\n } catch (error) {\n if (error instanceof DOMException && error.name === 'TimeoutError') {\n throw new SearchError('Request timed out', 0, 'Request timed out');\n }\n throw error;\n }\n\n if (!response.ok) {\n const text = await response.text();\n let detail: string;\n try {\n const parsed = JSON.parse(text);\n detail = typeof parsed.detail === 'string' ? parsed.detail : text;\n } catch {\n detail = text || response.statusText;\n }\n throw new SearchError(detail, response.status, detail);\n }\n\n return (await response.json()) as T;\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@skawr/search",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official SKAWR browser search SDK — lightweight, search-only client",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"tsup": "^8.0.0",
|
|
31
|
+
"typescript": "^5.4.0",
|
|
32
|
+
"vitest": "^2.0.0"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"skawr",
|
|
39
|
+
"search",
|
|
40
|
+
"frontend",
|
|
41
|
+
"browser",
|
|
42
|
+
"autocomplete"
|
|
43
|
+
],
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/skawr/skawr-sdk-search"
|
|
48
|
+
}
|
|
49
|
+
}
|