@lanonasis/memory-client 1.0.1 → 2.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/dist/index.js CHANGED
@@ -1,22 +1,261 @@
1
1
  'use strict';
2
2
 
3
+ var zod = require('zod');
3
4
  var child_process = require('child_process');
4
5
  var util = require('util');
5
- var zod = require('zod');
6
6
 
7
7
  /**
8
- * Memory Client class for interacting with the Memory as a Service API
8
+ * Memory types supported by the service
9
+ */
10
+ const MEMORY_TYPES = ['context', 'project', 'knowledge', 'reference', 'personal', 'workflow'];
11
+ /**
12
+ * Memory status values
13
+ */
14
+ const MEMORY_STATUSES = ['active', 'archived', 'draft', 'deleted'];
15
+ /**
16
+ * Validation schemas using Zod
17
+ */
18
+ const createMemorySchema = zod.z.object({
19
+ title: zod.z.string().min(1).max(500),
20
+ content: zod.z.string().min(1).max(50000),
21
+ summary: zod.z.string().max(1000).optional(),
22
+ memory_type: zod.z.enum(MEMORY_TYPES).default('context'),
23
+ topic_id: zod.z.string().uuid().optional(),
24
+ project_ref: zod.z.string().max(100).optional(),
25
+ tags: zod.z.array(zod.z.string().min(1).max(50)).max(20).default([]),
26
+ metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
27
+ });
28
+ const updateMemorySchema = zod.z.object({
29
+ title: zod.z.string().min(1).max(500).optional(),
30
+ content: zod.z.string().min(1).max(50000).optional(),
31
+ summary: zod.z.string().max(1000).optional(),
32
+ memory_type: zod.z.enum(MEMORY_TYPES).optional(),
33
+ status: zod.z.enum(MEMORY_STATUSES).optional(),
34
+ topic_id: zod.z.string().uuid().nullable().optional(),
35
+ project_ref: zod.z.string().max(100).nullable().optional(),
36
+ tags: zod.z.array(zod.z.string().min(1).max(50)).max(20).optional(),
37
+ metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
38
+ });
39
+ const searchMemorySchema = zod.z.object({
40
+ query: zod.z.string().min(1).max(1000),
41
+ memory_types: zod.z.array(zod.z.enum(MEMORY_TYPES)).optional(),
42
+ tags: zod.z.array(zod.z.string()).optional(),
43
+ topic_id: zod.z.string().uuid().optional(),
44
+ project_ref: zod.z.string().optional(),
45
+ status: zod.z.enum(MEMORY_STATUSES).default('active'),
46
+ limit: zod.z.number().int().min(1).max(100).default(20),
47
+ threshold: zod.z.number().min(0).max(1).default(0.7)
48
+ });
49
+ const createTopicSchema = zod.z.object({
50
+ name: zod.z.string().min(1).max(100),
51
+ description: zod.z.string().max(500).optional(),
52
+ color: zod.z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
53
+ icon: zod.z.string().max(50).optional(),
54
+ parent_topic_id: zod.z.string().uuid().optional()
55
+ });
56
+ // ========================================
57
+ // Intelligence Feature Types (v2.0)
58
+ // ========================================
59
+ /**
60
+ * Chunking strategies for content preprocessing
61
+ */
62
+ const CHUNKING_STRATEGIES = ['semantic', 'fixed-size', 'paragraph', 'sentence', 'code-block'];
63
+ /**
64
+ * Content types detected or specified
65
+ */
66
+ const CONTENT_TYPES = ['text', 'code', 'markdown', 'json', 'yaml'];
67
+ // ========================================
68
+ // Enhanced Search Types
69
+ // ========================================
70
+ /**
71
+ * Search modes for memory queries
72
+ */
73
+ const SEARCH_MODES = ['vector', 'text', 'hybrid'];
74
+ // ========================================
75
+ // Validation Schemas for Intelligence
76
+ // ========================================
77
+ const preprocessingOptionsSchema = zod.z.object({
78
+ chunking: zod.z.object({
79
+ strategy: zod.z.enum(CHUNKING_STRATEGIES).optional(),
80
+ maxChunkSize: zod.z.number().int().min(100).max(10000).optional(),
81
+ overlap: zod.z.number().int().min(0).max(500).optional()
82
+ }).optional(),
83
+ cleanContent: zod.z.boolean().optional(),
84
+ extractMetadata: zod.z.boolean().optional()
85
+ }).optional();
86
+ const enhancedSearchSchema = zod.z.object({
87
+ query: zod.z.string().min(1).max(1000),
88
+ type: zod.z.enum(MEMORY_TYPES).optional(),
89
+ threshold: zod.z.number().min(0).max(1).default(0.7),
90
+ limit: zod.z.number().int().min(1).max(100).default(20),
91
+ search_mode: zod.z.enum(SEARCH_MODES).default('hybrid'),
92
+ filters: zod.z.object({
93
+ tags: zod.z.array(zod.z.string()).optional(),
94
+ project_id: zod.z.string().uuid().optional(),
95
+ topic_id: zod.z.string().uuid().optional(),
96
+ date_range: zod.z.object({
97
+ from: zod.z.string().optional(),
98
+ to: zod.z.string().optional()
99
+ }).optional()
100
+ }).optional(),
101
+ include_chunks: zod.z.boolean().default(false)
102
+ });
103
+ const analyticsDateRangeSchema = zod.z.object({
104
+ from: zod.z.string().optional(),
105
+ to: zod.z.string().optional(),
106
+ group_by: zod.z.enum(['day', 'week', 'month']).default('day')
107
+ });
108
+
109
+ /**
110
+ * Core Utilities for Memory Client
111
+ * Browser-safe, no Node.js dependencies
112
+ */
113
+ /**
114
+ * Safely parse JSON with detailed error reporting
115
+ * Prevents scattered try/catch blocks throughout the codebase
116
+ */
117
+ function safeJsonParse(input) {
118
+ try {
119
+ const data = JSON.parse(input);
120
+ return { success: true, data };
121
+ }
122
+ catch (error) {
123
+ const message = error instanceof Error
124
+ ? error.message
125
+ : 'Unknown JSON parse error';
126
+ return { success: false, error: `Invalid JSON: ${message}` };
127
+ }
128
+ }
129
+ /**
130
+ * HTTP status code to error code mapping
9
131
  */
10
- class MemoryClient {
132
+ function httpStatusToErrorCode(status) {
133
+ switch (status) {
134
+ case 400:
135
+ return 'VALIDATION_ERROR';
136
+ case 401:
137
+ return 'AUTH_ERROR';
138
+ case 403:
139
+ return 'FORBIDDEN';
140
+ case 404:
141
+ return 'NOT_FOUND';
142
+ case 408:
143
+ return 'TIMEOUT_ERROR';
144
+ case 409:
145
+ return 'CONFLICT';
146
+ case 429:
147
+ return 'RATE_LIMIT_ERROR';
148
+ case 500:
149
+ case 502:
150
+ case 503:
151
+ case 504:
152
+ return 'SERVER_ERROR';
153
+ default:
154
+ return 'API_ERROR';
155
+ }
156
+ }
157
+ /**
158
+ * Create a standardized error response from various error sources
159
+ */
160
+ function createErrorResponse(message, code = 'API_ERROR', statusCode, details) {
161
+ return {
162
+ code,
163
+ message,
164
+ statusCode,
165
+ details,
166
+ timestamp: new Date().toISOString()
167
+ };
168
+ }
169
+ /**
170
+ * Create an error response from an HTTP response
171
+ */
172
+ function createErrorFromResponse(status, statusText, body) {
173
+ const code = httpStatusToErrorCode(status);
174
+ // Try to extract message from response body
175
+ let message = `HTTP ${status}: ${statusText}`;
176
+ let details = undefined;
177
+ if (body && typeof body === 'object') {
178
+ const bodyObj = body;
179
+ if (typeof bodyObj.error === 'string') {
180
+ message = bodyObj.error;
181
+ }
182
+ else if (typeof bodyObj.message === 'string') {
183
+ message = bodyObj.message;
184
+ }
185
+ if (bodyObj.details) {
186
+ details = bodyObj.details;
187
+ }
188
+ }
189
+ return createErrorResponse(message, code, status, details);
190
+ }
191
+ /**
192
+ * Sleep utility for retry logic
193
+ */
194
+ function sleep(ms) {
195
+ return new Promise(resolve => setTimeout(resolve, ms));
196
+ }
197
+ /**
198
+ * Calculate retry delay with exponential backoff and jitter
199
+ */
200
+ function calculateRetryDelay(attempt, baseDelay = 1000, backoff = 'exponential', maxDelay = 30000) {
201
+ let delay;
202
+ if (backoff === 'exponential') {
203
+ delay = baseDelay * Math.pow(2, attempt);
204
+ }
205
+ else {
206
+ delay = baseDelay * (attempt + 1);
207
+ }
208
+ // Add jitter (±20%) to prevent thundering herd
209
+ const jitter = delay * 0.2 * (Math.random() * 2 - 1);
210
+ delay = Math.min(delay + jitter, maxDelay);
211
+ return Math.round(delay);
212
+ }
213
+ /**
214
+ * Check if an error is retryable based on status code
215
+ */
216
+ function isRetryableError(statusCode) {
217
+ if (!statusCode)
218
+ return true; // Network errors are retryable
219
+ // Retry on server errors and rate limits
220
+ return statusCode >= 500 || statusCode === 429 || statusCode === 408;
221
+ }
222
+
223
+ /**
224
+ * Core Memory Client - Pure Browser-Safe Implementation
225
+ *
226
+ * NO Node.js dependencies, NO CLI code, NO child_process
227
+ * Works in: Browser, React Native, Cloudflare Workers, Edge Functions, Deno, Bun
228
+ *
229
+ * Bundle size: ~15KB gzipped
230
+ */
231
+ /**
232
+ * Helper to check if response has error
233
+ */
234
+ function hasError(response) {
235
+ return response.error !== undefined;
236
+ }
237
+ /**
238
+ * Helper to check if response has data
239
+ */
240
+ function hasData(response) {
241
+ return response.data !== undefined;
242
+ }
243
+ /**
244
+ * Core Memory Client class for interacting with the Memory as a Service API
245
+ *
246
+ * This is a pure browser-safe client with zero Node.js dependencies.
247
+ * It uses only standard web APIs (fetch, AbortController, etc.)
248
+ */
249
+ class CoreMemoryClient {
11
250
  constructor(config) {
12
251
  this.config = {
13
252
  timeout: 30000,
14
- useGateway: true,
15
253
  ...config
16
254
  };
17
255
  this.baseHeaders = {
18
256
  'Content-Type': 'application/json',
19
- 'User-Agent': '@lanonasis/memory-client/1.0.0',
257
+ 'User-Agent': '@lanonasis/memory-client/2.0.0',
258
+ 'X-Project-Scope': 'lanonasis-maas', // Required by backend auth middleware
20
259
  ...config.headers
21
260
  };
22
261
  // Set authentication headers
@@ -26,48 +265,171 @@ class MemoryClient {
26
265
  else if (config.apiKey) {
27
266
  this.baseHeaders['X-API-Key'] = config.apiKey;
28
267
  }
268
+ // Add organization ID header if provided
269
+ if (config.organizationId) {
270
+ this.baseHeaders['X-Organization-ID'] = config.organizationId;
271
+ }
29
272
  }
30
273
  /**
31
- * Make an HTTP request to the API
274
+ * Enrich request body with organization context if configured
275
+ * This ensures the API has the organization_id even if not in auth token
276
+ */
277
+ enrichWithOrgContext(body) {
278
+ // If organizationId is configured, include it in the request body
279
+ if (this.config.organizationId && !body.organization_id) {
280
+ return {
281
+ ...body,
282
+ organization_id: this.config.organizationId
283
+ };
284
+ }
285
+ // Fallback to userId if no organizationId configured
286
+ if (!this.config.organizationId && this.config.userId && !body.organization_id) {
287
+ return {
288
+ ...body,
289
+ organization_id: this.config.userId
290
+ };
291
+ }
292
+ return body;
293
+ }
294
+ /**
295
+ * Make an HTTP request to the API with retry support
32
296
  */
33
297
  async request(endpoint, options = {}) {
298
+ const startTime = Date.now();
299
+ const maxRetries = this.config.retry?.maxRetries ?? 3;
300
+ const baseDelay = this.config.retry?.retryDelay ?? 1000;
301
+ const backoff = this.config.retry?.backoff ?? 'exponential';
302
+ // Call onRequest hook if provided
303
+ if (this.config.onRequest) {
304
+ try {
305
+ this.config.onRequest(endpoint);
306
+ }
307
+ catch (error) {
308
+ console.warn('onRequest hook error:', error);
309
+ }
310
+ }
34
311
  // Handle gateway vs direct API URL formatting
35
312
  const baseUrl = this.config.apiUrl.includes('/api')
36
313
  ? this.config.apiUrl.replace('/api', '')
37
314
  : this.config.apiUrl;
38
315
  const url = `${baseUrl}/api/v1${endpoint}`;
39
- try {
40
- const controller = new AbortController();
41
- const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
42
- const response = await fetch(url, {
43
- headers: { ...this.baseHeaders, ...options.headers },
44
- signal: controller.signal,
45
- ...options,
46
- });
47
- clearTimeout(timeoutId);
48
- let data;
49
- const contentType = response.headers.get('content-type');
50
- if (contentType && contentType.includes('application/json')) {
51
- data = await response.json();
52
- }
53
- else {
54
- data = await response.text();
316
+ let lastError;
317
+ let attempt = 0;
318
+ while (attempt <= maxRetries) {
319
+ try {
320
+ const controller = new AbortController();
321
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
322
+ const response = await fetch(url, {
323
+ headers: { ...this.baseHeaders, ...options.headers },
324
+ signal: controller.signal,
325
+ ...options,
326
+ });
327
+ clearTimeout(timeoutId);
328
+ let data;
329
+ const contentType = response.headers.get('content-type');
330
+ if (contentType && contentType.includes('application/json')) {
331
+ data = await response.json();
332
+ }
333
+ else {
334
+ data = await response.text();
335
+ }
336
+ if (!response.ok) {
337
+ const error = createErrorFromResponse(response.status, response.statusText, data);
338
+ // Only retry on retryable errors (5xx, 429, 408)
339
+ if (isRetryableError(response.status) && attempt < maxRetries) {
340
+ lastError = error;
341
+ const delay = calculateRetryDelay(attempt, baseDelay, backoff);
342
+ await sleep(delay);
343
+ attempt++;
344
+ continue;
345
+ }
346
+ // Call onError hook if provided
347
+ if (this.config.onError) {
348
+ try {
349
+ this.config.onError(error);
350
+ }
351
+ catch (hookError) {
352
+ console.warn('onError hook error:', hookError);
353
+ }
354
+ }
355
+ return { error, meta: { duration: Date.now() - startTime, retries: attempt } };
356
+ }
357
+ // Call onResponse hook if provided
358
+ if (this.config.onResponse) {
359
+ try {
360
+ const duration = Date.now() - startTime;
361
+ this.config.onResponse(endpoint, duration);
362
+ }
363
+ catch (error) {
364
+ console.warn('onResponse hook error:', error);
365
+ }
366
+ }
367
+ return { data, meta: { duration: Date.now() - startTime, retries: attempt } };
55
368
  }
56
- if (!response.ok) {
57
- return {
58
- error: data?.error || `HTTP ${response.status}: ${response.statusText}`
59
- };
369
+ catch (error) {
370
+ if (error instanceof Error && error.name === 'AbortError') {
371
+ const timeoutError = createErrorResponse('Request timeout', 'TIMEOUT_ERROR', 408);
372
+ // Retry on timeout
373
+ if (attempt < maxRetries) {
374
+ lastError = timeoutError;
375
+ const delay = calculateRetryDelay(attempt, baseDelay, backoff);
376
+ await sleep(delay);
377
+ attempt++;
378
+ continue;
379
+ }
380
+ if (this.config.onError) {
381
+ try {
382
+ this.config.onError(timeoutError);
383
+ }
384
+ catch (hookError) {
385
+ console.warn('onError hook error:', hookError);
386
+ }
387
+ }
388
+ return { error: timeoutError, meta: { duration: Date.now() - startTime, retries: attempt } };
389
+ }
390
+ const networkError = createErrorResponse(error instanceof Error ? error.message : 'Network error', 'NETWORK_ERROR');
391
+ // Retry on network errors
392
+ if (attempt < maxRetries) {
393
+ lastError = networkError;
394
+ const delay = calculateRetryDelay(attempt, baseDelay, backoff);
395
+ await sleep(delay);
396
+ attempt++;
397
+ continue;
398
+ }
399
+ if (this.config.onError) {
400
+ try {
401
+ this.config.onError(networkError);
402
+ }
403
+ catch (hookError) {
404
+ console.warn('onError hook error:', hookError);
405
+ }
406
+ }
407
+ return { error: networkError, meta: { duration: Date.now() - startTime, retries: attempt } };
60
408
  }
61
- return { data };
62
409
  }
63
- catch (error) {
64
- if (error instanceof Error && error.name === 'AbortError') {
65
- return { error: 'Request timeout' };
66
- }
410
+ // Should never reach here, but handle it gracefully
411
+ return {
412
+ error: lastError ?? createErrorResponse('Max retries exceeded', 'API_ERROR'),
413
+ meta: { duration: Date.now() - startTime, retries: attempt }
414
+ };
415
+ }
416
+ /**
417
+ * Validate input using Zod schema and return validation error if invalid
418
+ */
419
+ validateInput(schema, data) {
420
+ const result = schema.safeParse(data);
421
+ if (!result.success) {
422
+ // Extract error details from Zod error
423
+ const zodError = result.error;
424
+ const details = zodError?.issues?.map(issue => ({
425
+ field: issue.path.map(String).join('.'),
426
+ message: issue.message
427
+ })) ?? [];
67
428
  return {
68
- error: error instanceof Error ? error.message : 'Network error'
429
+ error: createErrorResponse('Validation failed', 'VALIDATION_ERROR', 400, details)
69
430
  };
70
431
  }
432
+ return null;
71
433
  }
72
434
  /**
73
435
  * Test the API connection and authentication
@@ -77,12 +439,18 @@ class MemoryClient {
77
439
  }
78
440
  // Memory Operations
79
441
  /**
80
- * Create a new memory
442
+ * Create a new memory with validation
81
443
  */
82
444
  async createMemory(memory) {
445
+ // Validate input before making request
446
+ const validationError = this.validateInput(createMemorySchema, memory);
447
+ if (validationError) {
448
+ return { error: validationError.error };
449
+ }
450
+ const enrichedMemory = this.enrichWithOrgContext(memory);
83
451
  return this.request('/memory', {
84
452
  method: 'POST',
85
- body: JSON.stringify(memory)
453
+ body: JSON.stringify(enrichedMemory)
86
454
  });
87
455
  }
88
456
  /**
@@ -92,9 +460,14 @@ class MemoryClient {
92
460
  return this.request(`/memory/${encodeURIComponent(id)}`);
93
461
  }
94
462
  /**
95
- * Update an existing memory
463
+ * Update an existing memory with validation
96
464
  */
97
465
  async updateMemory(id, updates) {
466
+ // Validate input before making request
467
+ const validationError = this.validateInput(updateMemorySchema, updates);
468
+ if (validationError) {
469
+ return { error: validationError.error };
470
+ }
98
471
  return this.request(`/memory/${encodeURIComponent(id)}`, {
99
472
  method: 'PUT',
100
473
  body: JSON.stringify(updates)
@@ -128,31 +501,45 @@ class MemoryClient {
128
501
  return this.request(endpoint);
129
502
  }
130
503
  /**
131
- * Search memories using semantic search
504
+ * Search memories using semantic search with validation
132
505
  */
133
506
  async searchMemories(request) {
507
+ // Validate input before making request
508
+ const validationError = this.validateInput(searchMemorySchema, request);
509
+ if (validationError) {
510
+ // Return error response (data will be undefined, only error is set)
511
+ return { error: validationError.error };
512
+ }
513
+ const enrichedRequest = this.enrichWithOrgContext(request);
134
514
  return this.request('/memory/search', {
135
515
  method: 'POST',
136
- body: JSON.stringify(request)
516
+ body: JSON.stringify(enrichedRequest)
137
517
  });
138
518
  }
139
519
  /**
140
520
  * Bulk delete multiple memories
141
521
  */
142
522
  async bulkDeleteMemories(memoryIds) {
523
+ const enrichedRequest = this.enrichWithOrgContext({ memory_ids: memoryIds });
143
524
  return this.request('/memory/bulk/delete', {
144
525
  method: 'POST',
145
- body: JSON.stringify({ memory_ids: memoryIds })
526
+ body: JSON.stringify(enrichedRequest)
146
527
  });
147
528
  }
148
529
  // Topic Operations
149
530
  /**
150
- * Create a new topic
531
+ * Create a new topic with validation
151
532
  */
152
533
  async createTopic(topic) {
534
+ // Validate input before making request
535
+ const validationError = this.validateInput(createTopicSchema, topic);
536
+ if (validationError) {
537
+ return { error: validationError.error };
538
+ }
539
+ const enrichedTopic = this.enrichWithOrgContext(topic);
153
540
  return this.request('/topics', {
154
541
  method: 'POST',
155
- body: JSON.stringify(topic)
542
+ body: JSON.stringify(enrichedTopic)
156
543
  });
157
544
  }
158
545
  /**
@@ -190,6 +577,182 @@ class MemoryClient {
190
577
  async getMemoryStats() {
191
578
  return this.request('/memory/stats');
192
579
  }
580
+ // ========================================
581
+ // Intelligence Features (v2.0)
582
+ // ========================================
583
+ /**
584
+ * Create a memory with preprocessing options (chunking, intelligence extraction)
585
+ *
586
+ * @example
587
+ * ```typescript
588
+ * const result = await client.createMemoryWithPreprocessing({
589
+ * title: 'Auth System Docs',
590
+ * content: 'Long content...',
591
+ * memory_type: 'knowledge',
592
+ * preprocessing: {
593
+ * chunking: { strategy: 'semantic', maxChunkSize: 1000 },
594
+ * extractMetadata: true
595
+ * }
596
+ * });
597
+ * ```
598
+ */
599
+ async createMemoryWithPreprocessing(memory) {
600
+ // Validate base memory fields
601
+ const validationError = this.validateInput(createMemorySchema, memory);
602
+ if (validationError) {
603
+ return { error: validationError.error };
604
+ }
605
+ const enrichedMemory = this.enrichWithOrgContext(memory);
606
+ return this.request('/memory', {
607
+ method: 'POST',
608
+ body: JSON.stringify(enrichedMemory)
609
+ });
610
+ }
611
+ /**
612
+ * Update a memory with re-chunking and embedding regeneration
613
+ *
614
+ * @example
615
+ * ```typescript
616
+ * const result = await client.updateMemoryWithPreprocessing('mem_123', {
617
+ * content: 'Updated content...',
618
+ * rechunk: true,
619
+ * regenerate_embedding: true
620
+ * });
621
+ * ```
622
+ */
623
+ async updateMemoryWithPreprocessing(id, updates) {
624
+ const validationError = this.validateInput(updateMemorySchema, updates);
625
+ if (validationError) {
626
+ return { error: validationError.error };
627
+ }
628
+ return this.request(`/memory/${encodeURIComponent(id)}`, {
629
+ method: 'PUT',
630
+ body: JSON.stringify(updates)
631
+ });
632
+ }
633
+ /**
634
+ * Enhanced semantic search with hybrid mode (vector + text)
635
+ *
636
+ * @example
637
+ * ```typescript
638
+ * const result = await client.enhancedSearch({
639
+ * query: 'authentication flow',
640
+ * search_mode: 'hybrid',
641
+ * filters: { tags: ['auth'], project_id: 'proj_123' },
642
+ * include_chunks: true
643
+ * });
644
+ * ```
645
+ */
646
+ async enhancedSearch(request) {
647
+ const validationError = this.validateInput(enhancedSearchSchema, request);
648
+ if (validationError) {
649
+ return { error: validationError.error };
650
+ }
651
+ const enrichedRequest = this.enrichWithOrgContext(request);
652
+ return this.request('/memory/search', {
653
+ method: 'POST',
654
+ body: JSON.stringify(enrichedRequest)
655
+ });
656
+ }
657
+ // ========================================
658
+ // Analytics Operations
659
+ // ========================================
660
+ /**
661
+ * Get search analytics data
662
+ *
663
+ * @example
664
+ * ```typescript
665
+ * const analytics = await client.getSearchAnalytics({
666
+ * from: '2025-01-01',
667
+ * to: '2025-12-31',
668
+ * group_by: 'day'
669
+ * });
670
+ * ```
671
+ */
672
+ async getSearchAnalytics(options = {}) {
673
+ const validationError = this.validateInput(analyticsDateRangeSchema, options);
674
+ if (validationError) {
675
+ return { error: validationError.error };
676
+ }
677
+ const params = new URLSearchParams();
678
+ if (options.from)
679
+ params.append('from', options.from);
680
+ if (options.to)
681
+ params.append('to', options.to);
682
+ if (options.group_by)
683
+ params.append('group_by', options.group_by);
684
+ const queryString = params.toString();
685
+ const endpoint = queryString ? `/analytics/search?${queryString}` : '/analytics/search';
686
+ return this.request(endpoint);
687
+ }
688
+ /**
689
+ * Get memory access patterns
690
+ *
691
+ * @example
692
+ * ```typescript
693
+ * const patterns = await client.getAccessPatterns({
694
+ * from: '2025-01-01',
695
+ * to: '2025-12-31'
696
+ * });
697
+ * console.log(patterns.data?.most_accessed);
698
+ * ```
699
+ */
700
+ async getAccessPatterns(options = {}) {
701
+ const params = new URLSearchParams();
702
+ if (options.from)
703
+ params.append('from', options.from);
704
+ if (options.to)
705
+ params.append('to', options.to);
706
+ const queryString = params.toString();
707
+ const endpoint = queryString ? `/analytics/access?${queryString}` : '/analytics/access';
708
+ return this.request(endpoint);
709
+ }
710
+ /**
711
+ * Get extended memory statistics with storage and activity metrics
712
+ *
713
+ * @example
714
+ * ```typescript
715
+ * const stats = await client.getExtendedStats();
716
+ * console.log(`Total chunks: ${stats.data?.storage.total_chunks}`);
717
+ * console.log(`Created today: ${stats.data?.activity.created_today}`);
718
+ * ```
719
+ */
720
+ async getExtendedStats() {
721
+ return this.request('/analytics/stats');
722
+ }
723
+ /**
724
+ * Get topic with its memories
725
+ *
726
+ * @example
727
+ * ```typescript
728
+ * const topic = await client.getTopicWithMemories('topic_123');
729
+ * console.log(topic.data?.memories);
730
+ * ```
731
+ */
732
+ async getTopicWithMemories(topicId, options = {}) {
733
+ const params = new URLSearchParams();
734
+ if (options.limit)
735
+ params.append('limit', String(options.limit));
736
+ if (options.offset)
737
+ params.append('offset', String(options.offset));
738
+ const queryString = params.toString();
739
+ const endpoint = queryString
740
+ ? `/topics/${encodeURIComponent(topicId)}/memories?${queryString}`
741
+ : `/topics/${encodeURIComponent(topicId)}/memories`;
742
+ return this.request(endpoint);
743
+ }
744
+ /**
745
+ * Get topics in hierarchical structure
746
+ *
747
+ * @example
748
+ * ```typescript
749
+ * const topics = await client.getTopicsHierarchy();
750
+ * // Returns nested topic tree with children
751
+ * ```
752
+ */
753
+ async getTopicsHierarchy() {
754
+ return this.request('/topics?include_hierarchy=true');
755
+ }
193
756
  // Utility Methods
194
757
  /**
195
758
  * Update authentication token
@@ -231,17 +794,431 @@ class MemoryClient {
231
794
  }
232
795
  }
233
796
  /**
234
- * Factory function to create a new Memory Client instance
797
+ * Factory function to create a new Core Memory Client instance
235
798
  */
236
799
  function createMemoryClient(config) {
237
- return new MemoryClient(config);
800
+ return new CoreMemoryClient(config);
238
801
  }
239
802
 
803
+ /**
804
+ * Error handling for Memory Client
805
+ * Browser-safe, no Node.js dependencies
806
+ */
807
+ /**
808
+ * Standardized error codes for programmatic error handling
809
+ */
810
+ const ERROR_CODES = [
811
+ 'API_ERROR',
812
+ 'AUTH_ERROR',
813
+ 'VALIDATION_ERROR',
814
+ 'TIMEOUT_ERROR',
815
+ 'RATE_LIMIT_ERROR',
816
+ 'NOT_FOUND',
817
+ 'NETWORK_ERROR',
818
+ 'FORBIDDEN',
819
+ 'CONFLICT',
820
+ 'SERVER_ERROR'
821
+ ];
822
+ /**
823
+ * Type guard to check if an object is an ApiErrorResponse
824
+ */
825
+ function isApiErrorResponse(value) {
826
+ return (typeof value === 'object' &&
827
+ value !== null &&
828
+ 'code' in value &&
829
+ 'message' in value &&
830
+ typeof value.code === 'string' &&
831
+ typeof value.message === 'string');
832
+ }
833
+ /**
834
+ * Base error class for Memory Client errors
835
+ */
836
+ class MemoryClientError extends Error {
837
+ constructor(message, code = 'API_ERROR', statusCode, details) {
838
+ super(message);
839
+ this.code = code;
840
+ this.statusCode = statusCode;
841
+ this.details = details;
842
+ this.name = 'MemoryClientError';
843
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
844
+ if (Error.captureStackTrace) {
845
+ Error.captureStackTrace(this, MemoryClientError);
846
+ }
847
+ }
848
+ /**
849
+ * Convert to ApiErrorResponse for consistent API responses
850
+ */
851
+ toResponse() {
852
+ return {
853
+ code: this.code,
854
+ message: this.message,
855
+ statusCode: this.statusCode,
856
+ details: this.details,
857
+ timestamp: new Date().toISOString()
858
+ };
859
+ }
860
+ }
861
+ /**
862
+ * Network/API error
863
+ */
864
+ class ApiError extends MemoryClientError {
865
+ constructor(message, statusCode, details) {
866
+ super(message, 'API_ERROR', statusCode, details);
867
+ this.name = 'ApiError';
868
+ }
869
+ /**
870
+ * Create from an HTTP response
871
+ */
872
+ static fromResponse(status, statusText, body) {
873
+ let message = `HTTP ${status}: ${statusText}`;
874
+ let details = undefined;
875
+ if (body && typeof body === 'object') {
876
+ const bodyObj = body;
877
+ if (typeof bodyObj.error === 'string') {
878
+ message = bodyObj.error;
879
+ }
880
+ else if (typeof bodyObj.message === 'string') {
881
+ message = bodyObj.message;
882
+ }
883
+ if (bodyObj.details) {
884
+ details = bodyObj.details;
885
+ }
886
+ }
887
+ return new ApiError(message, status, details);
888
+ }
889
+ }
890
+ /**
891
+ * Authentication error
892
+ */
893
+ class AuthenticationError extends MemoryClientError {
894
+ constructor(message = 'Authentication required') {
895
+ super(message, 'AUTH_ERROR', 401);
896
+ this.name = 'AuthenticationError';
897
+ }
898
+ }
899
+ /**
900
+ * Validation error with field-level details
901
+ */
902
+ class ValidationError extends MemoryClientError {
903
+ constructor(message, details) {
904
+ super(message, 'VALIDATION_ERROR', 400, details);
905
+ this.name = 'ValidationError';
906
+ // Parse validation details into field errors
907
+ this.validationErrors = [];
908
+ if (Array.isArray(details)) {
909
+ this.validationErrors = details.filter((item) => typeof item === 'object' &&
910
+ item !== null &&
911
+ typeof item.field === 'string' &&
912
+ typeof item.message === 'string');
913
+ }
914
+ }
915
+ /**
916
+ * Create from Zod error
917
+ */
918
+ static fromZodError(error) {
919
+ const details = error.issues.map(issue => ({
920
+ field: issue.path.join('.'),
921
+ message: issue.message
922
+ }));
923
+ return new ValidationError('Validation failed', details);
924
+ }
925
+ }
926
+ /**
927
+ * Timeout error
928
+ */
929
+ class TimeoutError extends MemoryClientError {
930
+ constructor(message = 'Request timeout') {
931
+ super(message, 'TIMEOUT_ERROR', 408);
932
+ this.name = 'TimeoutError';
933
+ }
934
+ }
935
+ /**
936
+ * Rate limit error with retry-after info
937
+ */
938
+ class RateLimitError extends MemoryClientError {
939
+ constructor(message = 'Rate limit exceeded', retryAfter) {
940
+ super(message, 'RATE_LIMIT_ERROR', 429, { retryAfter });
941
+ this.name = 'RateLimitError';
942
+ this.retryAfter = retryAfter;
943
+ }
944
+ }
945
+ /**
946
+ * Not found error
947
+ */
948
+ class NotFoundError extends MemoryClientError {
949
+ constructor(resource) {
950
+ super(`${resource} not found`, 'NOT_FOUND', 404);
951
+ this.name = 'NotFoundError';
952
+ this.resource = resource;
953
+ }
954
+ }
955
+ /**
956
+ * Network error (no response received)
957
+ */
958
+ class NetworkError extends MemoryClientError {
959
+ constructor(message = 'Network error') {
960
+ super(message, 'NETWORK_ERROR');
961
+ this.name = 'NetworkError';
962
+ }
963
+ }
964
+ /**
965
+ * Server error (5xx responses)
966
+ */
967
+ class ServerError extends MemoryClientError {
968
+ constructor(message, statusCode = 500) {
969
+ super(message, 'SERVER_ERROR', statusCode);
970
+ this.name = 'ServerError';
971
+ }
972
+ }
973
+ /**
974
+ * Create appropriate error class from status code
975
+ */
976
+ function createErrorFromStatus(status, message, details) {
977
+ switch (status) {
978
+ case 400:
979
+ return new ValidationError(message, details);
980
+ case 401:
981
+ return new AuthenticationError(message);
982
+ case 404:
983
+ return new NotFoundError(message);
984
+ case 408:
985
+ return new TimeoutError(message);
986
+ case 429:
987
+ return new RateLimitError(message);
988
+ case 500:
989
+ case 502:
990
+ case 503:
991
+ case 504:
992
+ return new ServerError(message, status);
993
+ default:
994
+ return new ApiError(message, status, details);
995
+ }
996
+ }
997
+
998
+ /**
999
+ * @lanonasis/memory-client
1000
+ *
1001
+ * Universal Memory as a Service (MaaS) Client SDK for Lanonasis
1002
+ * Intelligent memory management with semantic search capabilities
1003
+ *
1004
+ * v2.0.0 - Universal SDK Redesign
1005
+ * "Drop In and Sleep" Architecture - Works everywhere with zero configuration
1006
+ *
1007
+ * @example Browser/Web App
1008
+ * ```ts
1009
+ * import { createMemoryClient } from '@lanonasis/memory-client/core';
1010
+ * const client = createMemoryClient({ apiKey: 'your-key' });
1011
+ * ```
1012
+ *
1013
+ * @example Node.js
1014
+ * ```ts
1015
+ * import { createNodeMemoryClient } from '@lanonasis/memory-client/node';
1016
+ * const client = await createNodeMemoryClient({ apiKey: process.env.KEY });
1017
+ * ```
1018
+ *
1019
+ * @example React
1020
+ * ```tsx
1021
+ * import { MemoryProvider, useMemories } from '@lanonasis/memory-client/react';
1022
+ * ```
1023
+ *
1024
+ * @example Vue
1025
+ * ```ts
1026
+ * import { createMemoryPlugin, useMemories } from '@lanonasis/memory-client/vue';
1027
+ * ```
1028
+ */
1029
+ // ========================================
1030
+ // Internal Imports (for use in this file)
1031
+ // ========================================
1032
+ // ========================================
1033
+ // Constants
1034
+ // ========================================
1035
+ const VERSION = '2.0.0';
1036
+ const CLIENT_NAME = '@lanonasis/memory-client';
1037
+ // ========================================
1038
+ // Environment Detection
1039
+ // ========================================
1040
+ const isBrowser = typeof window !== 'undefined';
1041
+ const isNode = typeof globalThis !== 'undefined' && 'process' in globalThis && globalThis.process?.versions?.node;
1042
+ const isEdge = !isBrowser && !isNode;
1043
+ /**
1044
+ * Get the current runtime environment
1045
+ */
1046
+ function getEnvironment() {
1047
+ if (isBrowser)
1048
+ return 'browser';
1049
+ if (isNode)
1050
+ return 'node';
1051
+ return 'edge';
1052
+ }
1053
+ /**
1054
+ * Auto-detecting client factory - "Drop In and Sleep" architecture
1055
+ *
1056
+ * Automatically detects the runtime environment and returns the appropriate client:
1057
+ * - Browser/Edge: Returns CoreMemoryClient (lightweight, browser-safe)
1058
+ * - Node.js: Returns EnhancedMemoryClient (with CLI/MCP support)
1059
+ *
1060
+ * @example
1061
+ * ```typescript
1062
+ * import { createClient } from '@lanonasis/memory-client';
1063
+ *
1064
+ * // Works in any environment!
1065
+ * const client = await createClient({
1066
+ * apiUrl: 'https://api.lanonasis.com',
1067
+ * apiKey: 'your-key'
1068
+ * });
1069
+ *
1070
+ * const memories = await client.listMemories();
1071
+ * ```
1072
+ */
1073
+ async function createClient(config) {
1074
+ const environment = getEnvironment();
1075
+ if (environment === 'node') {
1076
+ try {
1077
+ // Dynamic import for Node.js client to avoid bundling in browser
1078
+ const { createNodeMemoryClient } = await Promise.resolve().then(function () { return index; });
1079
+ return await createNodeMemoryClient({
1080
+ ...config,
1081
+ preferCLI: config.preferCLI ?? true,
1082
+ enableMCP: config.enableMCP ?? true
1083
+ });
1084
+ }
1085
+ catch {
1086
+ // Fallback to core client if Node module fails to load
1087
+ console.warn('Failed to load Node.js client, falling back to core client');
1088
+ }
1089
+ }
1090
+ // Browser, Edge, or fallback
1091
+ const clientConfig = {
1092
+ apiUrl: config.apiUrl,
1093
+ apiKey: config.apiKey,
1094
+ authToken: config.authToken,
1095
+ organizationId: config.organizationId,
1096
+ timeout: config.timeout ?? (environment === 'edge' ? 5000 : 30000),
1097
+ headers: config.headers,
1098
+ retry: config.retry
1099
+ };
1100
+ return createMemoryClient(clientConfig);
1101
+ }
1102
+ // ========================================
1103
+ // Default Configurations
1104
+ // ========================================
1105
+ const defaultConfigs = {
1106
+ development: {
1107
+ apiUrl: 'http://localhost:3001',
1108
+ timeout: 30000,
1109
+ },
1110
+ production: {
1111
+ apiUrl: 'https://api.lanonasis.com',
1112
+ timeout: 15000,
1113
+ },
1114
+ edge: {
1115
+ apiUrl: 'https://api.lanonasis.com',
1116
+ timeout: 5000,
1117
+ }
1118
+ };
1119
+ // Note: Enhanced client requires Node.js, so we don't export it from main entry
1120
+ // Users should import from '@lanonasis/memory-client/node' instead
1121
+ // ========================================
1122
+ // Usage Instructions
1123
+ // ========================================
1124
+ /**
1125
+ * # @lanonasis/memory-client v2.0
1126
+ *
1127
+ * ## Quick Start
1128
+ *
1129
+ * ### Browser / Web App
1130
+ * ```bash
1131
+ * npm install @lanonasis/memory-client
1132
+ * ```
1133
+ * ```typescript
1134
+ * import { createMemoryClient } from '@lanonasis/memory-client/core';
1135
+ *
1136
+ * const client = createMemoryClient({
1137
+ * apiUrl: 'https://api.lanonasis.com',
1138
+ * apiKey: 'your-key-here'
1139
+ * });
1140
+ *
1141
+ * const memories = await client.listMemories();
1142
+ * ```
1143
+ *
1144
+ * ### Node.js with CLI Support
1145
+ * ```typescript
1146
+ * import { createNodeMemoryClient } from '@lanonasis/memory-client/node';
1147
+ *
1148
+ * const client = await createNodeMemoryClient({
1149
+ * apiKey: process.env.LANONASIS_KEY,
1150
+ * preferCLI: true // Automatically uses CLI if available
1151
+ * });
1152
+ *
1153
+ * const result = await client.listMemories();
1154
+ * console.log(`Using: ${result.source}`); // 'cli' or 'api'
1155
+ * ```
1156
+ *
1157
+ * ### React
1158
+ * ```tsx
1159
+ * import { MemoryProvider, useMemories } from '@lanonasis/memory-client/react';
1160
+ *
1161
+ * function App() {
1162
+ * return (
1163
+ * <MemoryProvider apiKey="your-key">
1164
+ * <MemoryList />
1165
+ * </MemoryProvider>
1166
+ * );
1167
+ * }
1168
+ *
1169
+ * function MemoryList() {
1170
+ * const { memories, loading } = useMemories();
1171
+ * if (loading) return <div>Loading...</div>;
1172
+ * return <div>{memories.map(m => <div key={m.id}>{m.title}</div>)}</div>;
1173
+ * }
1174
+ * ```
1175
+ *
1176
+ * ### Vue 3
1177
+ * ```typescript
1178
+ * import { createMemoryPlugin, useMemories } from '@lanonasis/memory-client/vue';
1179
+ *
1180
+ * const app = createApp(App);
1181
+ * app.use(createMemoryPlugin({ apiKey: 'your-key' }));
1182
+ * ```
1183
+ *
1184
+ * ### Edge Functions (Cloudflare Workers, Vercel Edge)
1185
+ * ```typescript
1186
+ * import { createMemoryClient } from '@lanonasis/memory-client/core';
1187
+ * import { edgePreset } from '@lanonasis/memory-client/presets';
1188
+ *
1189
+ * export default {
1190
+ * async fetch(request: Request, env: Env) {
1191
+ * const client = createMemoryClient(edgePreset({
1192
+ * apiKey: env.LANONASIS_KEY
1193
+ * }));
1194
+ * const memories = await client.searchMemories({ query: 'test' });
1195
+ * return Response.json(memories.data);
1196
+ * }
1197
+ * };
1198
+ * ```
1199
+ *
1200
+ * ## Bundle Sizes
1201
+ *
1202
+ * - **Core** (browser): ~15KB gzipped
1203
+ * - **Node** (with CLI): ~35KB gzipped
1204
+ * - **React**: ~18KB gzipped (+ React)
1205
+ * - **Vue**: ~17KB gzipped (+ Vue)
1206
+ * - **Presets**: ~2KB gzipped
1207
+ *
1208
+ * ## Documentation
1209
+ *
1210
+ * - Full docs: https://docs.lanonasis.com/sdk
1211
+ * - API reference: https://docs.lanonasis.com/api
1212
+ * - Examples: https://github.com/lanonasis/examples
1213
+ */
1214
+
240
1215
  /**
241
1216
  * CLI Integration Module for Memory Client SDK
242
1217
  *
243
1218
  * Provides intelligent CLI detection and MCP channel utilization
244
1219
  * when @lanonasis/cli v1.5.2+ is available in the environment
1220
+ *
1221
+ * IMPORTANT: This file imports Node.js modules and should only be used in Node.js environments
245
1222
  */
246
1223
  const execAsync = util.promisify(child_process.exec);
247
1224
  /**
@@ -315,8 +1292,10 @@ class CLIIntegration {
315
1292
  const { stdout: authOutput } = await execAsync('onasis auth status --output json 2>/dev/null || lanonasis auth status --output json 2>/dev/null', {
316
1293
  timeout: 3000
317
1294
  });
318
- const authStatus = JSON.parse(authOutput);
319
- authenticated = authStatus.authenticated === true;
1295
+ const parseResult = safeJsonParse(authOutput);
1296
+ if (parseResult.success) {
1297
+ authenticated = parseResult.data.authenticated === true;
1298
+ }
320
1299
  }
321
1300
  catch {
322
1301
  // Authentication check failed
@@ -338,10 +1317,10 @@ class CLIIntegration {
338
1317
  async executeCLICommand(command, options = {}) {
339
1318
  const cliInfo = await this.detectCLI();
340
1319
  if (!cliInfo.available) {
341
- return { error: 'CLI not available' };
1320
+ return { error: createErrorResponse('CLI not available', 'API_ERROR') };
342
1321
  }
343
1322
  if (!cliInfo.authenticated) {
344
- return { error: 'CLI not authenticated. Run: onasis login' };
1323
+ return { error: createErrorResponse('CLI not authenticated. Run: onasis login', 'AUTH_ERROR', 401) };
345
1324
  }
346
1325
  try {
347
1326
  const timeout = options.timeout || 30000;
@@ -358,22 +1337,20 @@ class CLIIntegration {
358
1337
  console.warn('CLI warning:', stderr);
359
1338
  }
360
1339
  if (outputFormat === 'json') {
361
- try {
362
- const result = JSON.parse(stdout);
363
- return { data: result };
364
- }
365
- catch (parseError) {
366
- return { error: `Failed to parse CLI JSON output: ${parseError instanceof Error ? parseError.message : 'Unknown error'}` };
1340
+ const parseResult = safeJsonParse(stdout);
1341
+ if (parseResult.success) {
1342
+ return { data: parseResult.data };
367
1343
  }
1344
+ return { error: createErrorResponse(parseResult.error, 'VALIDATION_ERROR', 400) };
368
1345
  }
369
1346
  return { data: stdout };
370
1347
  }
371
1348
  catch (error) {
372
1349
  if (error instanceof Error && error.message.includes('timeout')) {
373
- return { error: 'CLI command timeout' };
1350
+ return { error: createErrorResponse('CLI command timeout', 'TIMEOUT_ERROR', 408) };
374
1351
  }
375
1352
  return {
376
- error: error instanceof Error ? error.message : 'CLI command failed'
1353
+ error: createErrorResponse(error instanceof Error ? error.message : 'CLI command failed', 'API_ERROR')
377
1354
  };
378
1355
  }
379
1356
  }
@@ -441,14 +1418,14 @@ class CLIIntegration {
441
1418
  async getMCPStatus() {
442
1419
  const cliInfo = await this.detectCLI();
443
1420
  if (!cliInfo.mcpAvailable) {
444
- return { error: 'MCP not available via CLI' };
1421
+ return { error: createErrorResponse('MCP not available via CLI', 'API_ERROR') };
445
1422
  }
446
1423
  return this.executeCLICommand('mcp status');
447
1424
  }
448
1425
  async listMCPTools() {
449
1426
  const cliInfo = await this.detectCLI();
450
1427
  if (!cliInfo.mcpAvailable) {
451
- return { error: 'MCP not available via CLI' };
1428
+ return { error: createErrorResponse('MCP not available via CLI', 'API_ERROR') };
452
1429
  }
453
1430
  return this.executeCLICommand('mcp tools');
454
1431
  }
@@ -501,6 +1478,8 @@ class CLIIntegration {
501
1478
  *
502
1479
  * Intelligently routes requests through CLI v1.5.2+ when available,
503
1480
  * with fallback to direct API for maximum compatibility and performance
1481
+ *
1482
+ * IMPORTANT: This file uses Node.js-specific features (process.env) and should only be used in Node.js environments
504
1483
  */
505
1484
  /**
506
1485
  * Enhanced Memory Client with intelligent CLI/API routing
@@ -516,21 +1495,24 @@ class EnhancedMemoryClient {
516
1495
  }
517
1496
  constructor(config) {
518
1497
  this.capabilities = null;
519
- this.config = {
520
- preferCLI: true,
521
- enableMCP: true,
522
- cliDetectionTimeout: 5000,
523
- fallbackToAPI: true,
524
- minCLIVersion: '1.5.2',
525
- verbose: false,
526
- timeout: 30000,
527
- useGateway: true,
1498
+ // Merge config with defaults, ensuring all required fields are present
1499
+ // Spread config first, then apply defaults only for undefined values
1500
+ const mergedConfig = {
1501
+ ...config,
1502
+ preferCLI: config.preferCLI ?? true,
1503
+ enableMCP: config.enableMCP ?? true,
1504
+ cliDetectionTimeout: config.cliDetectionTimeout ?? 5000,
1505
+ fallbackToAPI: config.fallbackToAPI ?? true,
1506
+ minCLIVersion: config.minCLIVersion ?? '1.5.2',
1507
+ verbose: config.verbose ?? false,
1508
+ timeout: config.timeout ?? 30000,
1509
+ apiUrl: config.apiUrl || 'https://api.lanonasis.com',
528
1510
  apiKey: config.apiKey || process.env.LANONASIS_API_KEY || '',
529
1511
  authToken: config.authToken || '',
530
- headers: config.headers || {},
531
- ...config
1512
+ headers: config.headers || {}
532
1513
  };
533
- this.directClient = new MemoryClient(config);
1514
+ this.config = mergedConfig;
1515
+ this.directClient = new CoreMemoryClient(config);
534
1516
  this.cliIntegration = new CLIIntegration();
535
1517
  }
536
1518
  /**
@@ -625,7 +1607,7 @@ class EnhancedMemoryClient {
625
1607
  };
626
1608
  }
627
1609
  return {
628
- error: error instanceof Error ? error.message : `CLI ${operation} failed`,
1610
+ error: createErrorResponse(error instanceof Error ? error.message : `CLI ${operation} failed`, 'API_ERROR'),
629
1611
  source: 'cli',
630
1612
  mcpUsed: false
631
1613
  };
@@ -760,7 +1742,7 @@ class EnhancedMemoryClient {
760
1742
  }
761
1743
  catch (error) {
762
1744
  return {
763
- error: error instanceof Error ? error.message : 'Auth status check failed',
1745
+ error: createErrorResponse(error instanceof Error ? error.message : 'Auth status check failed', 'API_ERROR'),
764
1746
  source: 'cli',
765
1747
  mcpUsed: false
766
1748
  };
@@ -773,7 +1755,7 @@ class EnhancedMemoryClient {
773
1755
  const capabilities = await this.getCapabilities();
774
1756
  if (!capabilities.mcpSupport) {
775
1757
  return {
776
- error: 'MCP not available',
1758
+ error: createErrorResponse('MCP not available', 'API_ERROR'),
777
1759
  source: 'cli',
778
1760
  mcpUsed: false
779
1761
  };
@@ -784,7 +1766,7 @@ class EnhancedMemoryClient {
784
1766
  }
785
1767
  catch (error) {
786
1768
  return {
787
- error: error instanceof Error ? error.message : 'MCP status check failed',
1769
+ error: createErrorResponse(error instanceof Error ? error.message : 'MCP status check failed', 'API_ERROR'),
788
1770
  source: 'cli',
789
1771
  mcpUsed: false
790
1772
  };
@@ -824,237 +1806,70 @@ class EnhancedMemoryClient {
824
1806
  /**
825
1807
  * Factory function to create an enhanced memory client
826
1808
  */
827
- async function createEnhancedMemoryClient(config) {
1809
+ async function createNodeMemoryClient(config) {
828
1810
  const client = new EnhancedMemoryClient(config);
829
1811
  await client.initialize();
830
1812
  return client;
831
1813
  }
832
1814
 
833
1815
  /**
834
- * Configuration utilities for Memory Client SDK
835
- * Provides smart defaults and environment detection for CLI/MCP integration
836
- */
837
- /**
838
- * Environment detection utilities
839
- */
840
- const Environment = {
841
- isNode: typeof globalThis !== 'undefined' && 'process' in globalThis && globalThis.process?.versions?.node,
842
- isBrowser: typeof window !== 'undefined',
843
- isVSCode: typeof globalThis !== 'undefined' && 'vscode' in globalThis,
844
- isCursor: typeof globalThis !== 'undefined' && 'cursor' in globalThis,
845
- isWindsurf: typeof globalThis !== 'undefined' && 'windsurf' in globalThis,
846
- get isIDE() {
847
- return this.isVSCode || this.isCursor || this.isWindsurf;
848
- },
849
- get supportsCLI() {
850
- return Boolean(this.isNode && !this.isBrowser);
851
- }
852
- };
853
- /**
854
- * Create smart configuration with environment-aware defaults
855
- */
856
- function createSmartConfig(baseConfig, options = {}) {
857
- const defaults = {
858
- preferCLI: Environment.supportsCLI,
859
- minCLIVersion: '1.5.2',
860
- enableMCP: true,
861
- cliDetectionTimeout: 3000,
862
- verbose: false
863
- };
864
- const config = { ...defaults, ...options };
865
- const preferCLI = config.preferCLI ?? defaults.preferCLI ?? false;
866
- const minCLIVersion = config.minCLIVersion ?? defaults.minCLIVersion ?? '1.5.2';
867
- const enableMCP = config.enableMCP ?? defaults.enableMCP ?? true;
868
- const cliDetectionTimeout = config.cliDetectionTimeout ?? defaults.cliDetectionTimeout ?? 3000;
869
- const verbose = config.verbose ?? defaults.verbose ?? false;
870
- return {
871
- ...baseConfig,
872
- preferCLI,
873
- minCLIVersion,
874
- enableMCP,
875
- cliDetectionTimeout,
876
- verbose,
877
- // Smart API configuration with environment detection
878
- apiUrl: baseConfig.apiUrl || (process?.env?.NODE_ENV === 'development'
879
- ? 'http://localhost:3001'
880
- : 'https://api.lanonasis.com'),
881
- // Default timeout based on environment
882
- timeout: baseConfig.timeout || (Environment.isIDE ? 10000 : 15000)
883
- };
884
- }
885
- /**
886
- * Preset configurations for common scenarios
887
- */
888
- const ConfigPresets = {
889
- /**
890
- * Development configuration with local API and CLI preference
891
- */
892
- development: (apiKey) => createSmartConfig({
893
- apiUrl: 'http://localhost:3001',
894
- apiKey,
895
- timeout: 30000
896
- }, {
897
- preferCLI: true,
898
- verbose: true
899
- }),
900
- /**
901
- * Production configuration optimized for performance
902
- */
903
- production: (apiKey) => createSmartConfig({
904
- apiUrl: 'https://api.lanonasis.com',
905
- apiKey,
906
- timeout: 15000
907
- }, {
908
- preferCLI: Environment.supportsCLI,
909
- verbose: false
910
- }),
911
- /**
912
- * IDE extension configuration with MCP prioritization
913
- */
914
- ideExtension: (apiKey) => createSmartConfig({
915
- apiUrl: 'https://api.lanonasis.com',
916
- apiKey,
917
- timeout: 10000
918
- }, {
919
- preferCLI: true,
920
- enableMCP: true,
921
- cliDetectionTimeout: 2000
922
- }),
923
- /**
924
- * Browser-only configuration (no CLI support)
925
- */
926
- browserOnly: (apiKey) => createSmartConfig({
927
- apiUrl: 'https://api.lanonasis.com',
928
- apiKey,
929
- timeout: 15000
930
- }, {
931
- preferCLI: false,
932
- enableMCP: false
933
- }),
934
- /**
935
- * CLI-first configuration for server environments
936
- */
937
- serverCLI: (apiKey) => createSmartConfig({
938
- apiUrl: 'https://api.lanonasis.com',
939
- apiKey,
940
- timeout: 20000
941
- }, {
942
- preferCLI: true,
943
- enableMCP: true,
944
- verbose: false
945
- })
946
- };
947
- /**
948
- * Migration helper for existing MemoryClient users
1816
+ * @lanonasis/memory-client/node
1817
+ *
1818
+ * Node.js-enhanced client with CLI integration
1819
+ * ONLY import this in Node.js environments
1820
+ *
1821
+ * Provides intelligent CLI detection and MCP channel utilization
1822
+ * when @lanonasis/cli v1.5.2+ is available
949
1823
  */
950
- function migrateToEnhanced(existingConfig, enhancementOptions = {}) {
951
- return createSmartConfig(existingConfig, {
952
- preferCLI: Environment.supportsCLI,
953
- ...enhancementOptions
954
- });
955
- }
1824
+ // Enhanced client with CLI support
956
1825
 
957
- /**
958
- * Memory types supported by the service
959
- */
960
- const MEMORY_TYPES = ['context', 'project', 'knowledge', 'reference', 'personal', 'workflow'];
961
- /**
962
- * Memory status values
963
- */
964
- const MEMORY_STATUSES = ['active', 'archived', 'draft', 'deleted'];
965
- /**
966
- * Validation schemas using Zod
967
- */
968
- const createMemorySchema = zod.z.object({
969
- title: zod.z.string().min(1).max(500),
970
- content: zod.z.string().min(1).max(50000),
971
- summary: zod.z.string().max(1000).optional(),
972
- memory_type: zod.z.enum(MEMORY_TYPES).default('context'),
973
- topic_id: zod.z.string().uuid().optional(),
974
- project_ref: zod.z.string().max(100).optional(),
975
- tags: zod.z.array(zod.z.string().min(1).max(50)).max(20).default([]),
976
- metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
977
- });
978
- const updateMemorySchema = zod.z.object({
979
- title: zod.z.string().min(1).max(500).optional(),
980
- content: zod.z.string().min(1).max(50000).optional(),
981
- summary: zod.z.string().max(1000).optional(),
982
- memory_type: zod.z.enum(MEMORY_TYPES).optional(),
983
- status: zod.z.enum(MEMORY_STATUSES).optional(),
984
- topic_id: zod.z.string().uuid().nullable().optional(),
985
- project_ref: zod.z.string().max(100).nullable().optional(),
986
- tags: zod.z.array(zod.z.string().min(1).max(50)).max(20).optional(),
987
- metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
988
- });
989
- const searchMemorySchema = zod.z.object({
990
- query: zod.z.string().min(1).max(1000),
991
- memory_types: zod.z.array(zod.z.enum(MEMORY_TYPES)).optional(),
992
- tags: zod.z.array(zod.z.string()).optional(),
993
- topic_id: zod.z.string().uuid().optional(),
994
- project_ref: zod.z.string().optional(),
995
- status: zod.z.enum(MEMORY_STATUSES).default('active'),
996
- limit: zod.z.number().int().min(1).max(100).default(20),
997
- threshold: zod.z.number().min(0).max(1).default(0.7)
998
- });
999
- const createTopicSchema = zod.z.object({
1000
- name: zod.z.string().min(1).max(100),
1001
- description: zod.z.string().max(500).optional(),
1002
- color: zod.z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
1003
- icon: zod.z.string().max(50).optional(),
1004
- parent_topic_id: zod.z.string().uuid().optional()
1826
+ var index = /*#__PURE__*/Object.freeze({
1827
+ __proto__: null,
1828
+ CLIIntegration: CLIIntegration,
1829
+ EnhancedMemoryClient: EnhancedMemoryClient,
1830
+ createNodeMemoryClient: createNodeMemoryClient
1005
1831
  });
1006
1832
 
1007
- /**
1008
- * @lanonasis/memory-client
1009
- *
1010
- * Memory as a Service (MaaS) Client SDK for Lanonasis
1011
- * Intelligent memory management with semantic search capabilities
1012
- */
1013
- // Main client
1014
- // Constants
1015
- const VERSION = '1.0.0';
1016
- const CLIENT_NAME = '@lanonasis/memory-client';
1017
- // Environment detection
1018
- const isBrowser = typeof window !== 'undefined';
1019
- const isNode = typeof globalThis !== 'undefined' && 'process' in globalThis && globalThis.process?.versions?.node;
1020
- // Default configurations for different environments
1021
- const defaultConfigs = {
1022
- development: {
1023
- apiUrl: 'http://localhost:3001',
1024
- timeout: 30000,
1025
- useGateway: false
1026
- },
1027
- production: {
1028
- apiUrl: 'https://api.lanonasis.com',
1029
- timeout: 15000,
1030
- useGateway: true
1031
- },
1032
- gateway: {
1033
- apiUrl: 'https://api.lanonasis.com',
1034
- timeout: 10000,
1035
- useGateway: true
1036
- }
1037
- };
1038
- // Utility functions will be added in a future version to avoid circular imports
1039
-
1833
+ exports.ApiError = ApiError;
1834
+ exports.AuthenticationError = AuthenticationError;
1835
+ exports.CHUNKING_STRATEGIES = CHUNKING_STRATEGIES;
1040
1836
  exports.CLIENT_NAME = CLIENT_NAME;
1041
- exports.CLIIntegration = CLIIntegration;
1042
- exports.ConfigPresets = ConfigPresets;
1043
- exports.EnhancedMemoryClient = EnhancedMemoryClient;
1044
- exports.Environment = Environment;
1837
+ exports.CONTENT_TYPES = CONTENT_TYPES;
1838
+ exports.CoreMemoryClient = CoreMemoryClient;
1839
+ exports.ERROR_CODES = ERROR_CODES;
1045
1840
  exports.MEMORY_STATUSES = MEMORY_STATUSES;
1046
1841
  exports.MEMORY_TYPES = MEMORY_TYPES;
1047
- exports.MemoryClient = MemoryClient;
1842
+ exports.MemoryClient = CoreMemoryClient;
1843
+ exports.MemoryClientError = MemoryClientError;
1844
+ exports.NetworkError = NetworkError;
1845
+ exports.NotFoundError = NotFoundError;
1846
+ exports.RateLimitError = RateLimitError;
1847
+ exports.SEARCH_MODES = SEARCH_MODES;
1848
+ exports.ServerError = ServerError;
1849
+ exports.TimeoutError = TimeoutError;
1048
1850
  exports.VERSION = VERSION;
1049
- exports.createEnhancedMemoryClient = createEnhancedMemoryClient;
1851
+ exports.ValidationError = ValidationError;
1852
+ exports.analyticsDateRangeSchema = analyticsDateRangeSchema;
1853
+ exports.calculateRetryDelay = calculateRetryDelay;
1854
+ exports.createClient = createClient;
1855
+ exports.createErrorFromStatus = createErrorFromStatus;
1856
+ exports.createErrorResponse = createErrorResponse;
1050
1857
  exports.createMemoryClient = createMemoryClient;
1051
1858
  exports.createMemorySchema = createMemorySchema;
1052
- exports.createSmartConfig = createSmartConfig;
1053
1859
  exports.createTopicSchema = createTopicSchema;
1054
1860
  exports.defaultConfigs = defaultConfigs;
1861
+ exports.enhancedSearchSchema = enhancedSearchSchema;
1862
+ exports.getEnvironment = getEnvironment;
1863
+ exports.hasData = hasData;
1864
+ exports.hasError = hasError;
1865
+ exports.httpStatusToErrorCode = httpStatusToErrorCode;
1866
+ exports.isApiErrorResponse = isApiErrorResponse;
1055
1867
  exports.isBrowser = isBrowser;
1868
+ exports.isEdge = isEdge;
1056
1869
  exports.isNode = isNode;
1057
- exports.migrateToEnhanced = migrateToEnhanced;
1870
+ exports.isRetryableError = isRetryableError;
1871
+ exports.preprocessingOptionsSchema = preprocessingOptionsSchema;
1872
+ exports.safeJsonParse = safeJsonParse;
1058
1873
  exports.searchMemorySchema = searchMemorySchema;
1059
1874
  exports.updateMemorySchema = updateMemorySchema;
1060
1875
  //# sourceMappingURL=index.js.map