@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 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
+
@@ -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
+