@seekora-ai/search-sdk 1.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/README.md +213 -0
- package/cdn/.htaccess +37 -0
- package/cdn/README.md +130 -0
- package/cdn/_headers +25 -0
- package/cdn/example.html +299 -0
- package/cdn/seekora-sdk.js +4651 -0
- package/cdn/seekora-sdk.min.js +17 -0
- package/cdn/ui-sdk-integration-example.html +183 -0
- package/dist/cdn.d.ts +16 -0
- package/dist/cdn.js +26 -0
- package/dist/client.d.ts +401 -0
- package/dist/client.js +991 -0
- package/dist/config-loader.d.ts +43 -0
- package/dist/config-loader.js +147 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.js +58 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +40 -0
- package/dist/logger.d.ts +61 -0
- package/dist/logger.js +172 -0
- package/dist/src/cdn.d.ts +16 -0
- package/dist/src/cdn.js +26 -0
- package/dist/src/client.d.ts +401 -0
- package/dist/src/client.js +991 -0
- package/dist/src/config-loader.d.ts +43 -0
- package/dist/src/config-loader.js +147 -0
- package/dist/src/config.d.ts +22 -0
- package/dist/src/config.js +58 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.js +40 -0
- package/dist/src/logger.d.ts +61 -0
- package/dist/src/logger.js +172 -0
- package/dist/src/utils.d.ts +20 -0
- package/dist/src/utils.js +73 -0
- package/dist/utils.d.ts +20 -0
- package/dist/utils.js +73 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Seekora Search SDK
|
|
2
|
+
|
|
3
|
+
JavaScript/TypeScript SDK for the Seekora Search API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @seekora/search-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { SeekoraClient } from '@seekora/search-sdk';
|
|
15
|
+
|
|
16
|
+
const client = new SeekoraClient({
|
|
17
|
+
storeId: 'your-store-id',
|
|
18
|
+
readSecret: 'your-read-secret',
|
|
19
|
+
writeSecret: 'your-write-secret', // Optional, required for config updates
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Search
|
|
23
|
+
const results = await client.search('laptop', {
|
|
24
|
+
per_page: 20,
|
|
25
|
+
filter_by: 'price:100-500',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Track events
|
|
29
|
+
await client.trackClick('prod_123', 1, results.searchId);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Environment Configuration
|
|
33
|
+
|
|
34
|
+
The SDK supports different environments for development, staging, and production.
|
|
35
|
+
|
|
36
|
+
### Option 1: Environment Parameter
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// Local development
|
|
40
|
+
const client = new SeekoraClient({
|
|
41
|
+
storeId: 'your-store-id',
|
|
42
|
+
readSecret: 'your-read-secret',
|
|
43
|
+
environment: 'local', // Uses http://localhost:3000
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Staging
|
|
47
|
+
const client = new SeekoraClient({
|
|
48
|
+
storeId: 'your-store-id',
|
|
49
|
+
readSecret: 'your-read-secret',
|
|
50
|
+
environment: 'stage', // Uses https://stage-api.seekora.ai
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Production
|
|
54
|
+
const client = new SeekoraClient({
|
|
55
|
+
storeId: 'your-store-id',
|
|
56
|
+
readSecret: 'your-read-secret',
|
|
57
|
+
environment: 'production', // Uses https://api.seekora.com
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Option 2: Environment Variable
|
|
62
|
+
|
|
63
|
+
Set the `SEEKORA_ENV` environment variable:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Local development
|
|
67
|
+
SEEKORA_ENV=local node your-app.js
|
|
68
|
+
|
|
69
|
+
# Staging (default)
|
|
70
|
+
SEEKORA_ENV=stage node your-app.js
|
|
71
|
+
|
|
72
|
+
# Production
|
|
73
|
+
SEEKORA_ENV=production node your-app.js
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Option 3: Custom Base URL
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const client = new SeekoraClient({
|
|
80
|
+
storeId: 'your-store-id',
|
|
81
|
+
readSecret: 'your-read-secret',
|
|
82
|
+
baseUrl: 'https://custom-api.example.com', // Custom base URL
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Available Environments
|
|
87
|
+
|
|
88
|
+
| Environment | Base URL | Description |
|
|
89
|
+
|------------|----------|-------------|
|
|
90
|
+
| `local` | `http://localhost:3000` | Local development |
|
|
91
|
+
| `stage` | `https://stage-api.seekora.ai` | Staging (default) |
|
|
92
|
+
| `production` | `https://api.seekora.com` | Production |
|
|
93
|
+
|
|
94
|
+
**Default:** `stage` (staging environment)
|
|
95
|
+
|
|
96
|
+
## API Methods
|
|
97
|
+
|
|
98
|
+
### Search
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const results = await client.search('laptop', {
|
|
102
|
+
per_page: 20,
|
|
103
|
+
page: 1,
|
|
104
|
+
filter_by: 'price:100-500',
|
|
105
|
+
facet_by: 'category',
|
|
106
|
+
sort_by: 'price:asc',
|
|
107
|
+
analytics_tags: ['campaign:summer_sale'],
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Query Suggestions
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Basic: get suggestion hits (default returns array of hits)
|
|
115
|
+
const suggestions = await client.getSuggestions('lap', { hitsPerPage: 5 });
|
|
116
|
+
|
|
117
|
+
// With options: time range, categories/facets, analytics tags
|
|
118
|
+
const suggestions = await client.getSuggestions('lap', {
|
|
119
|
+
hitsPerPage: 10,
|
|
120
|
+
time_range: '30d',
|
|
121
|
+
include_categories: true,
|
|
122
|
+
include_facets: true,
|
|
123
|
+
analytics_tags: ['campaign:summer'],
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Dropdown recommendations (trending/top/related searches, filtered_tabs) — uses POST so include_dropdown_recommendations is sent
|
|
127
|
+
const withRecs = await client.getSuggestions('lap', {
|
|
128
|
+
include_dropdown_recommendations: true,
|
|
129
|
+
returnFullResponse: true,
|
|
130
|
+
}) as QuerySuggestionsFullResponse;
|
|
131
|
+
// withRecs.suggestions = hits; withRecs.extensions = { trending_searches, top_searches, related_searches, popular_brands, filtered_tabs }
|
|
132
|
+
|
|
133
|
+
// Filtered tabs (product lists per tab) — POST only
|
|
134
|
+
const withTabs = await client.getSuggestions('lap', {
|
|
135
|
+
filtered_tabs: [{ label: 'All', filter: '' }, { label: 'Category', filter: 'category:Electronics' }],
|
|
136
|
+
returnFullResponse: true,
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Query suggestions config (store-level):**
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// Get query suggestions config (read secret)
|
|
144
|
+
const config = await client.getSuggestionsConfig();
|
|
145
|
+
|
|
146
|
+
// Update query suggestions config (write secret required)
|
|
147
|
+
await client.updateQuerySuggestionsConfig({
|
|
148
|
+
enabled: true,
|
|
149
|
+
max_suggestions: 20,
|
|
150
|
+
minimum_letters: 2,
|
|
151
|
+
enable_personalization: false,
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
By default `getSuggestions()` returns an array of suggestion hits. Pass `returnFullResponse: true` to get `QuerySuggestionsFullResponse` with `suggestions`, `results`, and `extensions` (dropdown recommendations, filtered_tabs).
|
|
156
|
+
|
|
157
|
+
### Configuration Management
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
// Get config (read secret)
|
|
161
|
+
const config = await client.getConfig();
|
|
162
|
+
|
|
163
|
+
// Update config (write secret required)
|
|
164
|
+
await client.updateConfig({
|
|
165
|
+
num_typos: 2,
|
|
166
|
+
typo_tolerance: 'medium',
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Get config schema
|
|
170
|
+
const schema = await client.getConfigSchema();
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Analytics Events
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// Single event
|
|
177
|
+
await client.trackEvent({
|
|
178
|
+
event_name: 'search',
|
|
179
|
+
query: 'laptop',
|
|
180
|
+
results_count: 20,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Batch events
|
|
184
|
+
await client.trackEvents([
|
|
185
|
+
{ event_name: 'click', clicked_item_id: 'prod_123' },
|
|
186
|
+
{ event_name: 'view', clicked_item_id: 'prod_456' },
|
|
187
|
+
]);
|
|
188
|
+
|
|
189
|
+
// Convenience methods
|
|
190
|
+
await client.trackClick('prod_123', 1, searchId);
|
|
191
|
+
await client.trackView('prod_123', searchId);
|
|
192
|
+
await client.trackConversion('prod_123', 99.99, 'USD', searchId);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Error Handling
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
try {
|
|
199
|
+
const results = await client.search('laptop');
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error('Search failed:', error.message);
|
|
202
|
+
// Error format: [operation] message (status)
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## TypeScript Support
|
|
207
|
+
|
|
208
|
+
Full TypeScript support with type definitions included.
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
|
213
|
+
|
package/cdn/.htaccess
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Apache configuration for CDN bundle
|
|
2
|
+
# Place this file in your CDN directory if using Apache
|
|
3
|
+
|
|
4
|
+
# Enable CORS
|
|
5
|
+
<IfModule mod_headers.c>
|
|
6
|
+
Header set Access-Control-Allow-Origin "*"
|
|
7
|
+
Header set Access-Control-Allow-Methods "GET, HEAD, OPTIONS"
|
|
8
|
+
Header set Access-Control-Allow-Headers "*"
|
|
9
|
+
</IfModule>
|
|
10
|
+
|
|
11
|
+
# Cache control for production bundle
|
|
12
|
+
<FilesMatch "\.min\.js$">
|
|
13
|
+
Header set Cache-Control "public, max-age=31536000, immutable"
|
|
14
|
+
Header set Content-Type "application/javascript; charset=utf-8"
|
|
15
|
+
</FilesMatch>
|
|
16
|
+
|
|
17
|
+
# Cache control for development bundle
|
|
18
|
+
<FilesMatch "seekora-sdk\.js$">
|
|
19
|
+
Header set Cache-Control "public, max-age=3600"
|
|
20
|
+
Header set Content-Type "application/javascript; charset=utf-8"
|
|
21
|
+
</FilesMatch>
|
|
22
|
+
|
|
23
|
+
# Gzip compression
|
|
24
|
+
<IfModule mod_deflate.c>
|
|
25
|
+
AddOutputFilterByType DEFLATE application/javascript
|
|
26
|
+
</IfModule>
|
|
27
|
+
|
|
28
|
+
# Enable CORS preflight
|
|
29
|
+
<IfModule mod_rewrite.c>
|
|
30
|
+
RewriteEngine On
|
|
31
|
+
RewriteCond %{REQUEST_METHOD} OPTIONS
|
|
32
|
+
RewriteRule ^(.*)$ $1 [R=200,L]
|
|
33
|
+
</IfModule>
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
package/cdn/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Seekora SDK - CDN Bundle
|
|
2
|
+
|
|
3
|
+
This directory contains the browser-compatible CDN bundle of the Seekora Search SDK.
|
|
4
|
+
|
|
5
|
+
## Building the CDN Bundle
|
|
6
|
+
|
|
7
|
+
### Development Build
|
|
8
|
+
```bash
|
|
9
|
+
npm run build:cdn
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
This creates `cdn/seekora-sdk.js` (unminified, with source maps).
|
|
13
|
+
|
|
14
|
+
### Production Build
|
|
15
|
+
```bash
|
|
16
|
+
npm run build:cdn:prod
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This creates `cdn/seekora-sdk.min.js` (minified, optimized).
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Basic HTML Example
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<!DOCTYPE html>
|
|
27
|
+
<html>
|
|
28
|
+
<head>
|
|
29
|
+
<title>Seekora SDK Example</title>
|
|
30
|
+
</head>
|
|
31
|
+
<body>
|
|
32
|
+
<!-- Load the SDK -->
|
|
33
|
+
<script src="https://your-cdn-url.com/seekora-sdk.min.js"></script>
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
// Access the SDK via global variable
|
|
37
|
+
const { SeekoraClient } = window.SeekoraSDK;
|
|
38
|
+
|
|
39
|
+
// Initialize client
|
|
40
|
+
const client = new SeekoraClient({
|
|
41
|
+
storeId: 'your-store-id',
|
|
42
|
+
readSecret: 'your-read-secret',
|
|
43
|
+
environment: 'stage' // or 'production'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Perform search
|
|
47
|
+
async function search() {
|
|
48
|
+
const results = await client.search('laptop', {
|
|
49
|
+
per_page: 20
|
|
50
|
+
});
|
|
51
|
+
console.log('Results:', results);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
search();
|
|
55
|
+
</script>
|
|
56
|
+
</body>
|
|
57
|
+
</html>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Using with ES Modules (if supported)
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<script type="module">
|
|
64
|
+
import { SeekoraClient } from 'https://your-cdn-url.com/seekora-sdk.esm.js';
|
|
65
|
+
|
|
66
|
+
const client = new SeekoraClient({
|
|
67
|
+
storeId: 'your-store-id',
|
|
68
|
+
readSecret: 'your-read-secret'
|
|
69
|
+
});
|
|
70
|
+
</script>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Global Variable
|
|
74
|
+
|
|
75
|
+
The SDK is exposed as `window.SeekoraSDK` with the following structure:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
window.SeekoraSDK = {
|
|
79
|
+
SeekoraClient: class SeekoraClient { ... },
|
|
80
|
+
default: SeekoraClient,
|
|
81
|
+
ENVIRONMENTS: { ... },
|
|
82
|
+
getBaseUrl: function() { ... },
|
|
83
|
+
getEnvironment: function() { ... },
|
|
84
|
+
Logger: class Logger { ... },
|
|
85
|
+
createLogger: function() { ... },
|
|
86
|
+
// ... other exports
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Example
|
|
91
|
+
|
|
92
|
+
See `example.html` for a complete working example.
|
|
93
|
+
|
|
94
|
+
## File Sizes
|
|
95
|
+
|
|
96
|
+
- Development build: ~646 KB (unminified, with source maps)
|
|
97
|
+
- Production build: ~67 KB (minified)
|
|
98
|
+
|
|
99
|
+
## Browser Support
|
|
100
|
+
|
|
101
|
+
- Chrome 80+
|
|
102
|
+
- Firefox 78+
|
|
103
|
+
- Safari 14+
|
|
104
|
+
- Edge 80+
|
|
105
|
+
|
|
106
|
+
## Notes
|
|
107
|
+
|
|
108
|
+
- All dependencies (including axios) are bundled
|
|
109
|
+
- The bundle uses IIFE format for maximum compatibility
|
|
110
|
+
- Source maps are included in development builds
|
|
111
|
+
- The SDK automatically handles browser-specific features (localStorage, etc.)
|
|
112
|
+
|
|
113
|
+
## UI SDK Integration
|
|
114
|
+
|
|
115
|
+
For integrating with a UI SDK, see [CDN_UI_INTEGRATION.md](../CDN_UI_INTEGRATION.md).
|
|
116
|
+
|
|
117
|
+
**Quick summary:**
|
|
118
|
+
- Load Search SDK before UI SDK: `<script src="seekora-sdk.min.js"></script>`
|
|
119
|
+
- UI SDK can access via `window.SeekoraSDK.SeekoraClient`
|
|
120
|
+
- See `ui-sdk-integration-example.html` for a working example
|
|
121
|
+
|
|
122
|
+
## Deployment
|
|
123
|
+
|
|
124
|
+
For deployment instructions, see [DEPLOYMENT.md](../DEPLOYMENT.md).
|
|
125
|
+
|
|
126
|
+
Quick deployment options:
|
|
127
|
+
- **GitHub Pages**: Automatic via GitHub Actions on releases
|
|
128
|
+
- **AWS S3**: Use `./scripts/deploy-cdn.sh s3`
|
|
129
|
+
- **Cloudflare/Netlify/Vercel**: Upload `cdn/` directory (headers configured automatically)
|
|
130
|
+
|
package/cdn/_headers
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Netlify/Vercel headers configuration
|
|
2
|
+
# Place this file in your CDN directory
|
|
3
|
+
|
|
4
|
+
# Production bundle - long cache
|
|
5
|
+
/seekora-sdk.min.js
|
|
6
|
+
Access-Control-Allow-Origin: *
|
|
7
|
+
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
|
|
8
|
+
Access-Control-Allow-Headers: *
|
|
9
|
+
Cache-Control: public, max-age=31536000, immutable
|
|
10
|
+
Content-Type: application/javascript; charset=utf-8
|
|
11
|
+
|
|
12
|
+
# Development bundle - shorter cache
|
|
13
|
+
/seekora-sdk.js
|
|
14
|
+
Access-Control-Allow-Origin: *
|
|
15
|
+
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
|
|
16
|
+
Access-Control-Allow-Headers: *
|
|
17
|
+
Cache-Control: public, max-age=3600
|
|
18
|
+
Content-Type: application/javascript; charset=utf-8
|
|
19
|
+
|
|
20
|
+
# Source maps
|
|
21
|
+
/*.map
|
|
22
|
+
Access-Control-Allow-Origin: *
|
|
23
|
+
Cache-Control: public, max-age=3600
|
|
24
|
+
Content-Type: application/json; charset=utf-8
|
|
25
|
+
|
package/cdn/example.html
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Seekora SDK - CDN Example</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* CSS Variable Theme System */
|
|
9
|
+
:root {
|
|
10
|
+
/* Primary Colors */
|
|
11
|
+
--seekora-primary: #007bff;
|
|
12
|
+
--seekora-primary-dark: #0056b3;
|
|
13
|
+
|
|
14
|
+
/* Text Colors */
|
|
15
|
+
--seekora-text-primary: #333;
|
|
16
|
+
--seekora-text-secondary: #666;
|
|
17
|
+
|
|
18
|
+
/* Background Colors */
|
|
19
|
+
--seekora-bg-primary: #ffffff;
|
|
20
|
+
--seekora-bg-secondary: #f9f9f9;
|
|
21
|
+
--seekora-bg-tertiary: #f5f5f5;
|
|
22
|
+
--seekora-bg-config: #e9ecef;
|
|
23
|
+
|
|
24
|
+
/* Border Colors */
|
|
25
|
+
--seekora-border: #ddd;
|
|
26
|
+
|
|
27
|
+
/* Status Colors */
|
|
28
|
+
--seekora-error-text: #dc3545;
|
|
29
|
+
--seekora-error-bg: #f8d7da;
|
|
30
|
+
|
|
31
|
+
/* Shadows */
|
|
32
|
+
--seekora-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
33
|
+
|
|
34
|
+
/* Border Radius */
|
|
35
|
+
--seekora-radius: 4px;
|
|
36
|
+
--seekora-radius-lg: 8px;
|
|
37
|
+
|
|
38
|
+
/* Typography */
|
|
39
|
+
--seekora-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
body {
|
|
43
|
+
font-family: var(--seekora-font-family);
|
|
44
|
+
max-width: 1200px;
|
|
45
|
+
margin: 0 auto;
|
|
46
|
+
padding: 20px;
|
|
47
|
+
background: var(--seekora-bg-tertiary);
|
|
48
|
+
}
|
|
49
|
+
.container {
|
|
50
|
+
background: var(--seekora-bg-primary);
|
|
51
|
+
padding: 30px;
|
|
52
|
+
border-radius: var(--seekora-radius-lg);
|
|
53
|
+
box-shadow: var(--seekora-shadow);
|
|
54
|
+
}
|
|
55
|
+
h1 {
|
|
56
|
+
color: var(--seekora-text-primary);
|
|
57
|
+
margin-top: 0;
|
|
58
|
+
}
|
|
59
|
+
.search-box {
|
|
60
|
+
display: flex;
|
|
61
|
+
gap: 10px;
|
|
62
|
+
margin-bottom: 20px;
|
|
63
|
+
}
|
|
64
|
+
input[type="text"] {
|
|
65
|
+
flex: 1;
|
|
66
|
+
padding: 12px;
|
|
67
|
+
font-size: 16px;
|
|
68
|
+
border: 2px solid var(--seekora-border);
|
|
69
|
+
border-radius: var(--seekora-radius);
|
|
70
|
+
transition: border-color 0.2s ease;
|
|
71
|
+
}
|
|
72
|
+
input[type="text"]:focus {
|
|
73
|
+
outline: none;
|
|
74
|
+
border-color: var(--seekora-primary);
|
|
75
|
+
}
|
|
76
|
+
button {
|
|
77
|
+
padding: 12px 24px;
|
|
78
|
+
font-size: 16px;
|
|
79
|
+
background: var(--seekora-primary);
|
|
80
|
+
color: white;
|
|
81
|
+
border: none;
|
|
82
|
+
border-radius: var(--seekora-radius);
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
transition: background 0.2s ease;
|
|
85
|
+
}
|
|
86
|
+
button:hover {
|
|
87
|
+
background: var(--seekora-primary-dark);
|
|
88
|
+
}
|
|
89
|
+
button:disabled {
|
|
90
|
+
background: #ccc;
|
|
91
|
+
cursor: not-allowed;
|
|
92
|
+
}
|
|
93
|
+
.results {
|
|
94
|
+
margin-top: 20px;
|
|
95
|
+
}
|
|
96
|
+
.result-item {
|
|
97
|
+
padding: 15px;
|
|
98
|
+
border: 1px solid var(--seekora-border);
|
|
99
|
+
border-radius: var(--seekora-radius);
|
|
100
|
+
margin-bottom: 10px;
|
|
101
|
+
background: var(--seekora-bg-secondary);
|
|
102
|
+
cursor: pointer;
|
|
103
|
+
transition: box-shadow 0.2s ease, transform 0.2s ease;
|
|
104
|
+
}
|
|
105
|
+
.result-item:hover {
|
|
106
|
+
box-shadow: var(--seekora-shadow);
|
|
107
|
+
transform: translateY(-1px);
|
|
108
|
+
}
|
|
109
|
+
.result-item h3 {
|
|
110
|
+
margin-top: 0;
|
|
111
|
+
color: var(--seekora-primary);
|
|
112
|
+
}
|
|
113
|
+
.error {
|
|
114
|
+
color: var(--seekora-error-text);
|
|
115
|
+
padding: 10px;
|
|
116
|
+
background: var(--seekora-error-bg);
|
|
117
|
+
border-radius: var(--seekora-radius);
|
|
118
|
+
margin-top: 10px;
|
|
119
|
+
}
|
|
120
|
+
.loading {
|
|
121
|
+
text-align: center;
|
|
122
|
+
padding: 20px;
|
|
123
|
+
color: var(--seekora-text-secondary);
|
|
124
|
+
}
|
|
125
|
+
.config {
|
|
126
|
+
background: var(--seekora-bg-config);
|
|
127
|
+
padding: 15px;
|
|
128
|
+
border-radius: var(--seekora-radius);
|
|
129
|
+
margin-bottom: 20px;
|
|
130
|
+
font-family: monospace;
|
|
131
|
+
font-size: 12px;
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
134
|
+
</head>
|
|
135
|
+
<body>
|
|
136
|
+
<div class="container">
|
|
137
|
+
<h1>🔍 Seekora Search SDK - CDN Example</h1>
|
|
138
|
+
|
|
139
|
+
<div class="config">
|
|
140
|
+
<strong>Configuration:</strong><br>
|
|
141
|
+
Store ID: <span id="store-id">your-store-id</span><br>
|
|
142
|
+
Environment: <span id="env">stage</span>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div class="search-box">
|
|
146
|
+
<input
|
|
147
|
+
type="text"
|
|
148
|
+
id="search-input"
|
|
149
|
+
placeholder="Search for products..."
|
|
150
|
+
onkeypress="if(event.key === 'Enter') performSearch()"
|
|
151
|
+
>
|
|
152
|
+
<button onclick="performSearch()" id="search-btn">Search</button>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<div id="results" class="results"></div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<!-- Load the SDK from CDN -->
|
|
159
|
+
<!-- Replace with your actual CDN URL -->
|
|
160
|
+
<script src="seekora-sdk.js"></script>
|
|
161
|
+
<!-- Or use the minified version: <script src="seekora-sdk.min.js"></script> -->
|
|
162
|
+
|
|
163
|
+
<script>
|
|
164
|
+
// Configuration - Update these with your actual credentials
|
|
165
|
+
const CONFIG = {
|
|
166
|
+
storeId: 'your-store-id',
|
|
167
|
+
readSecret: 'your-read-secret',
|
|
168
|
+
writeSecret: 'your-write-secret', // Optional
|
|
169
|
+
environment: 'stage' // 'local', 'stage', or 'production'
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Initialize the SDK client
|
|
173
|
+
let client;
|
|
174
|
+
let lastSearchContext = null; // Store search context for analytics correlation
|
|
175
|
+
|
|
176
|
+
function initSDK() {
|
|
177
|
+
try {
|
|
178
|
+
// Access the SDK from the global variable
|
|
179
|
+
const { SeekoraClient } = window.SeekoraSDK;
|
|
180
|
+
|
|
181
|
+
client = new SeekoraClient({
|
|
182
|
+
storeId: CONFIG.storeId,
|
|
183
|
+
readSecret: CONFIG.readSecret,
|
|
184
|
+
writeSecret: CONFIG.writeSecret,
|
|
185
|
+
environment: CONFIG.environment,
|
|
186
|
+
logLevel: 'info'
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
console.log('✅ SDK initialized successfully');
|
|
190
|
+
document.getElementById('store-id').textContent = CONFIG.storeId;
|
|
191
|
+
document.getElementById('env').textContent = CONFIG.environment;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
showError('Failed to initialize SDK: ' + error.message);
|
|
194
|
+
console.error('SDK initialization error:', error);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function performSearch() {
|
|
199
|
+
const input = document.getElementById('search-input');
|
|
200
|
+
const query = input.value.trim();
|
|
201
|
+
const resultsDiv = document.getElementById('results');
|
|
202
|
+
const searchBtn = document.getElementById('search-btn');
|
|
203
|
+
|
|
204
|
+
if (!query) {
|
|
205
|
+
showError('Please enter a search query');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!client) {
|
|
210
|
+
showError('SDK not initialized. Check console for errors.');
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Disable button and show loading
|
|
215
|
+
searchBtn.disabled = true;
|
|
216
|
+
resultsDiv.innerHTML = '<div class="loading">Searching...</div>';
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
// Perform search
|
|
220
|
+
const response = await client.search(query, {
|
|
221
|
+
per_page: 10
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Store search context for analytics correlation
|
|
225
|
+
lastSearchContext = response.context || null;
|
|
226
|
+
|
|
227
|
+
// Display results
|
|
228
|
+
displayResults(response);
|
|
229
|
+
|
|
230
|
+
// Log search context for debugging
|
|
231
|
+
if (response.context) {
|
|
232
|
+
console.log('Search Context:', {
|
|
233
|
+
correlationId: response.context.correlationId,
|
|
234
|
+
searchId: response.context.searchId,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
showError('Search failed: ' + error.message);
|
|
239
|
+
console.error('Search error:', error);
|
|
240
|
+
} finally {
|
|
241
|
+
searchBtn.disabled = false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function displayResults(response) {
|
|
246
|
+
const resultsDiv = document.getElementById('results');
|
|
247
|
+
|
|
248
|
+
if (!response.results || response.results.length === 0) {
|
|
249
|
+
resultsDiv.innerHTML = '<div class="error">No results found</div>';
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
let html = `<h2>Found ${response.total || response.results.length} results</h2>`;
|
|
254
|
+
|
|
255
|
+
response.results.forEach((item, index) => {
|
|
256
|
+
html += `
|
|
257
|
+
<div class="result-item" onclick="trackClick('${item.id || index}', ${index})">
|
|
258
|
+
<h3>${item.title || item.name || 'Untitled'}</h3>
|
|
259
|
+
${item.description ? `<p>${item.description}</p>` : ''}
|
|
260
|
+
${item.price ? `<p><strong>Price: $${item.price}</strong></p>` : ''}
|
|
261
|
+
<small>ID: ${item.id || 'N/A'}</small>
|
|
262
|
+
</div>
|
|
263
|
+
`;
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
resultsDiv.innerHTML = html;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function trackClick(productId, position) {
|
|
270
|
+
if (!client) return;
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
// Pass search context for proper analytics correlation
|
|
274
|
+
await client.trackClick(productId, position, lastSearchContext);
|
|
275
|
+
console.log('[Analytics] Click tracked:', {
|
|
276
|
+
productId,
|
|
277
|
+
position,
|
|
278
|
+
correlationId: lastSearchContext?.correlationId,
|
|
279
|
+
searchId: lastSearchContext?.searchId,
|
|
280
|
+
});
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error('[Analytics] Failed to track click:', error);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function showError(message) {
|
|
287
|
+
const resultsDiv = document.getElementById('results');
|
|
288
|
+
resultsDiv.innerHTML = `<div class="error">${message}</div>`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Initialize SDK when page loads
|
|
292
|
+
window.addEventListener('DOMContentLoaded', initSDK);
|
|
293
|
+
</script>
|
|
294
|
+
</body>
|
|
295
|
+
</html>
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|