@belocal/js-sdk 0.6.0 → 0.7.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/node.d.ts CHANGED
@@ -1,8 +1,28 @@
1
+ /**
2
+ * Language code for translation (e.g., 'en', 'es', 'fr', 'ru').
3
+ * ISO 639-1 language codes are recommended.
4
+ */
1
5
  type Lang = string;
6
+ /**
7
+ * Key-value pairs for translation context.
8
+ * Supported keys:
9
+ * - `user_ctx` (string): Descriptive context to help improve translation quality (e.g., 'greeting message on the homepage')
10
+ * - `cache_type` (string): Cache type, e.g., 'managed' (managed translations cache)
11
+ *
12
+ * Other keys will be ignored by the API.
13
+ */
2
14
  type KV = Record<string, string>;
15
+ /**
16
+ * Base transport interface for HTTP communication.
17
+ * @internal
18
+ */
3
19
  interface BaseTransport {
4
20
  post(data: any, endpointPath: string): Promise<any>;
5
21
  }
22
+ /**
23
+ * Transport function type for translation requests.
24
+ * @internal
25
+ */
6
26
  type Transport = (params: {
7
27
  text: string;
8
28
  lang: Lang;
@@ -12,16 +32,21 @@ type Transport = (params: {
12
32
  text: string;
13
33
  status: string;
14
34
  }>;
35
+ /**
36
+ * Configuration options for BelocalEngine.
37
+ */
15
38
  interface BelocalEngineOptions {
39
+ /** Required: Your BeLocal API key for authentication */
16
40
  apiKey: string;
17
- baseUrl?: string;
41
+ /** Optional: Batch window in milliseconds for grouping multiple translation requests. Defaults to 50ms */
18
42
  batchWindowMs?: number;
43
+ /** Optional: Request timeout in milliseconds. Defaults to 10000ms (10 seconds) */
19
44
  timeoutMs?: number;
45
+ /** Optional: Enable debug logging to console. Defaults to false */
20
46
  debug?: boolean;
21
47
  }
22
48
 
23
49
  interface BaseNodeTransportConfig {
24
- baseUrl: string;
25
50
  headers?: Record<string, string>;
26
51
  timeoutMs?: number;
27
52
  retries?: number;
@@ -41,13 +66,154 @@ interface MultiTransportConfig {
41
66
  }
42
67
  declare function createMultiTransport(config: MultiTransportConfig): Transport;
43
68
 
69
+ /**
70
+ * BeLocal translation engine for Node.js environments.
71
+ *
72
+ * Provides on-demand translation with automatic request batching, caching, and deduplication.
73
+ * Optimized for Node.js environments using HTTP/2 with connection pooling and automatic retries.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const engine = new BelocalEngine({
78
+ * apiKey: 'your-api-key',
79
+ * debug: true
80
+ * });
81
+ *
82
+ * const translated = await engine.translate('Hello world', 'es');
83
+ * ```
84
+ */
44
85
  declare class BelocalEngine {
45
86
  private transport;
46
87
  private debug;
47
88
  private cache;
89
+ /**
90
+ * Creates a new BelocalEngine instance.
91
+ *
92
+ * @param options - Configuration options for the engine
93
+ * @throws {Error} If apiKey is not provided or invalid
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * const engine = new BelocalEngine({
98
+ * apiKey: 'your-api-key',
99
+ * batchWindowMs: 100,
100
+ * timeoutMs: 10000,
101
+ * debug: false
102
+ * });
103
+ * ```
104
+ */
48
105
  constructor(options: BelocalEngineOptions);
106
+ /**
107
+ * Translates a single text string to the target language.
108
+ *
109
+ * Uses in-memory cache to avoid redundant API calls. Results are automatically cached
110
+ * for subsequent requests with the same parameters.
111
+ *
112
+ * @param text - The text to translate
113
+ * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
114
+ * @param source_lang - Optional source language code. If not provided, auto-detection is used
115
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
116
+ * @returns Promise resolving to the translated text
117
+ * @throws {Error} If the translation request fails (network error, API error, timeout)
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * // Simple translation
122
+ * const result = await engine.translate('Hello world', 'es');
123
+ *
124
+ * // With source language
125
+ * const result = await engine.translate('Hello world', 'es', 'en');
126
+ *
127
+ * // With context (user_ctx) - descriptive context helps improve translation quality
128
+ * const result = await engine.translate('Hello world', 'es', undefined, {
129
+ * user_ctx: 'greeting message on the homepage'
130
+ * });
131
+ *
132
+ * // With cache_type
133
+ * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'managed' });
134
+ *
135
+ * // With source language and context
136
+ * const result = await engine.translate('Hello world', 'es', 'en', {
137
+ * user_ctx: 'greeting message on the homepage'
138
+ * });
139
+ * ```
140
+ */
49
141
  translate(text: string, lang: Lang, source_lang?: string, ctx?: KV): Promise<string>;
142
+ /**
143
+ * Translates multiple text strings to the target language in a single batch.
144
+ *
145
+ * This method is more efficient than calling `translate()` multiple times as it:
146
+ * - Batches requests together to reduce API calls
147
+ * - Checks cache for each text individually
148
+ * - Only requests translations for cache misses
149
+ * - Maintains the order of input texts in the result array
150
+ *
151
+ * @param texts - Array of texts to translate
152
+ * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
153
+ * @param source_lang - Optional source language code. If not provided, auto-detection is used
154
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
155
+ * @returns Promise resolving to an array of translated texts in the same order as input
156
+ * @throws {Error} If the translation request fails (network error, API error, timeout)
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * // Translate multiple texts
161
+ * const results = await engine.translateMany(['Hello', 'World', 'Test'], 'es');
162
+ * // Returns: ['Hola', 'Mundo', 'Prueba']
163
+ *
164
+ * // With source language
165
+ * const results = await engine.translateMany(['Hello', 'World'], 'fr', 'en');
166
+ *
167
+ * // With context (user_ctx) - descriptive context helps improve translation quality
168
+ * const results = await engine.translateMany(
169
+ * ['Hello', 'World'],
170
+ * 'es',
171
+ * undefined,
172
+ * { user_ctx: 'greeting message on the homepage' }
173
+ * );
174
+ *
175
+ * // With cache_type
176
+ * const results = await engine.translateMany(
177
+ * ['Hello', 'World'],
178
+ * 'es',
179
+ * undefined,
180
+ * { cache_type: 'managed' }
181
+ * );
182
+ *
183
+ * // Empty array returns empty array
184
+ * const results = await engine.translateMany([], 'es');
185
+ * // Returns: []
186
+ * ```
187
+ */
50
188
  translateMany(texts: string[], lang: Lang, source_lang?: string, ctx?: KV): Promise<string[]>;
189
+ /**
190
+ * Shortcut method for translation with simplified API.
191
+ *
192
+ * This is a convenience method that wraps `translate()`. When `context` is provided as a string,
193
+ * it is automatically wrapped in `{user_ctx: context}` object.
194
+ *
195
+ * @param text - The text to translate
196
+ * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
197
+ * @param source_lang - Optional source language code
198
+ * @param context - Optional descriptive context string in English (will be wrapped as {user_ctx: context})
199
+ * @returns Promise resolving to the translated text
200
+ * @throws {Error} If the translation request fails (network error, API error, timeout)
201
+ *
202
+ * @example
203
+ * ```typescript
204
+ * // Simple translation
205
+ * const result = await engine.t('Hello world', 'es');
206
+ *
207
+ * // With source language
208
+ * const result = await engine.t('Hello world', 'fr', 'en');
209
+ *
210
+ * // With context string (automatically wrapped as {user_ctx: 'greeting message on the homepage'})
211
+ * const result = await engine.t('Hello world', 'es', undefined, 'greeting message on the homepage');
212
+ *
213
+ * // With source language and context
214
+ * const result = await engine.t('Hello world', 'es', 'en', 'greeting message on the homepage');
215
+ * ```
216
+ */
51
217
  t(text: string, lang: Lang, source_lang?: string, context?: string): Promise<string>;
52
218
  private generateCacheKey;
53
219
  }
package/dist/node.mjs CHANGED
@@ -146,7 +146,7 @@ function createMultiTransport(config) {
146
146
  // src/version.ts
147
147
  var SDK_VERSION = (() => {
148
148
  try {
149
- return true ? "0.6.0" : "undefined";
149
+ return true ? "0.7.0" : "undefined";
150
150
  } catch {
151
151
  return "undefined";
152
152
  }
@@ -201,23 +201,45 @@ var DedupeTransport = class {
201
201
  };
202
202
 
203
203
  // src/transports/base/node.ts
204
+ var BASE_URL = "https://dynamic.belocal.dev";
204
205
  var sessionCache = /* @__PURE__ */ new Map();
205
- function getOrCreateSession(baseUrl, debug) {
206
- if (sessionCache.has(baseUrl)) {
207
- const session2 = sessionCache.get(baseUrl);
208
- if (!session2.destroyed) {
206
+ var cleanupTimer = null;
207
+ function cleanupDeadSessions() {
208
+ for (const [url, session] of sessionCache.entries()) {
209
+ if (session.destroyed || session.closed) {
210
+ sessionCache.delete(url);
211
+ }
212
+ }
213
+ }
214
+ function startCleanupTimer() {
215
+ if (cleanupTimer === null) {
216
+ cleanupTimer = setInterval(cleanupDeadSessions, 6e4);
217
+ if (cleanupTimer.unref) {
218
+ cleanupTimer.unref();
219
+ }
220
+ }
221
+ }
222
+ function getOrCreateSession(debug) {
223
+ cleanupDeadSessions();
224
+ startCleanupTimer();
225
+ if (sessionCache.has(BASE_URL)) {
226
+ const session2 = sessionCache.get(BASE_URL);
227
+ if (!session2.destroyed && !session2.closed) {
209
228
  return session2;
210
229
  }
211
- sessionCache.delete(baseUrl);
230
+ sessionCache.delete(BASE_URL);
212
231
  }
213
- const parsedUrl = new URL(baseUrl);
232
+ const parsedUrl = new URL(BASE_URL);
214
233
  const session = http2.connect(parsedUrl.origin);
215
234
  session.socket?.unref();
216
235
  session.on("error", () => {
217
- sessionCache.delete(baseUrl);
236
+ sessionCache.delete(BASE_URL);
218
237
  });
219
238
  session.on("close", () => {
220
- sessionCache.delete(baseUrl);
239
+ sessionCache.delete(BASE_URL);
240
+ });
241
+ session.on("goaway", () => {
242
+ sessionCache.delete(BASE_URL);
221
243
  });
222
244
  if (debug) {
223
245
  session.on("connect", () => console.log("[Base Node Transport H2] new session connected"));
@@ -226,7 +248,7 @@ function getOrCreateSession(baseUrl, debug) {
226
248
  (code, lastStreamID, opaque) => console.log("[Base Node Transport H2] goaway", code, lastStreamID)
227
249
  );
228
250
  }
229
- sessionCache.set(baseUrl, session);
251
+ sessionCache.set(BASE_URL, session);
230
252
  return session;
231
253
  }
232
254
  function makeHttp2Request(session, path, headers, body, timeoutMs) {
@@ -278,25 +300,26 @@ var BaseNodeTransport = class {
278
300
  async post(data, endpointPath) {
279
301
  const maxRetries = this.config.retries || 0;
280
302
  let attempt = 0;
281
- const url = `${this.config.baseUrl}${endpointPath}`;
303
+ const url = `${BASE_URL}${endpointPath}`;
282
304
  if (this.config.debug) {
283
305
  console.log(`[Base Node Transport] POST request to ${url}`, data);
284
306
  }
285
307
  while (attempt <= maxRetries) {
286
308
  try {
287
- const session = getOrCreateSession(this.config.baseUrl, this.config.debug);
309
+ const session = getOrCreateSession(this.config.debug);
288
310
  const body = JSON.stringify(data);
289
311
  const headers = {
290
312
  "x-sdk": SDK_NAME,
291
313
  "x-sdk-version": SDK_VERSION,
292
314
  ...this.config.headers
293
315
  };
316
+ const timeoutMs = this.config.timeoutMs ?? 1e4;
294
317
  const response = await makeHttp2Request(
295
318
  session,
296
319
  endpointPath,
297
320
  headers,
298
321
  body,
299
- this.config.timeoutMs
322
+ timeoutMs
300
323
  );
301
324
  if (response.statusCode < 200 || response.statusCode >= 300) {
302
325
  const errorMsg = `HTTP ${response.statusCode}: Request failed`;
@@ -345,10 +368,25 @@ var LocalCache = class {
345
368
  }
346
369
  };
347
370
  var BelocalEngine = class {
371
+ /**
372
+ * Creates a new BelocalEngine instance.
373
+ *
374
+ * @param options - Configuration options for the engine
375
+ * @throws {Error} If apiKey is not provided or invalid
376
+ *
377
+ * @example
378
+ * ```typescript
379
+ * const engine = new BelocalEngine({
380
+ * apiKey: 'your-api-key',
381
+ * batchWindowMs: 100,
382
+ * timeoutMs: 10000,
383
+ * debug: false
384
+ * });
385
+ * ```
386
+ */
348
387
  constructor(options) {
349
388
  const {
350
389
  apiKey,
351
- baseUrl = "https://dynamic.belocal.dev",
352
390
  batchWindowMs = 50,
353
391
  timeoutMs = 1e4,
354
392
  debug = false
@@ -362,7 +400,6 @@ var BelocalEngine = class {
362
400
  "Authorization": `Bearer ${apiKey}`
363
401
  };
364
402
  const baseTransport = createBaseNodeTransport({
365
- baseUrl,
366
403
  headers: authHeaders,
367
404
  timeoutMs,
368
405
  debug: this.debug
@@ -374,16 +411,97 @@ var BelocalEngine = class {
374
411
  });
375
412
  if (this.debug) {
376
413
  console.log("[BeLocal Engine] Multi transport created with config:", {
377
- baseUrl,
414
+ baseUrl: "https://dynamic.belocal.dev",
378
415
  timeoutMs,
379
416
  batchWindowMs
380
417
  });
381
418
  }
382
419
  }
420
+ /**
421
+ * Translates a single text string to the target language.
422
+ *
423
+ * Uses in-memory cache to avoid redundant API calls. Results are automatically cached
424
+ * for subsequent requests with the same parameters.
425
+ *
426
+ * @param text - The text to translate
427
+ * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
428
+ * @param source_lang - Optional source language code. If not provided, auto-detection is used
429
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
430
+ * @returns Promise resolving to the translated text
431
+ * @throws {Error} If the translation request fails (network error, API error, timeout)
432
+ *
433
+ * @example
434
+ * ```typescript
435
+ * // Simple translation
436
+ * const result = await engine.translate('Hello world', 'es');
437
+ *
438
+ * // With source language
439
+ * const result = await engine.translate('Hello world', 'es', 'en');
440
+ *
441
+ * // With context (user_ctx) - descriptive context helps improve translation quality
442
+ * const result = await engine.translate('Hello world', 'es', undefined, {
443
+ * user_ctx: 'greeting message on the homepage'
444
+ * });
445
+ *
446
+ * // With cache_type
447
+ * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'managed' });
448
+ *
449
+ * // With source language and context
450
+ * const result = await engine.translate('Hello world', 'es', 'en', {
451
+ * user_ctx: 'greeting message on the homepage'
452
+ * });
453
+ * ```
454
+ */
383
455
  async translate(text, lang, source_lang, ctx) {
384
456
  const results = await this.translateMany([text], lang, source_lang, ctx);
385
457
  return results[0];
386
458
  }
459
+ /**
460
+ * Translates multiple text strings to the target language in a single batch.
461
+ *
462
+ * This method is more efficient than calling `translate()` multiple times as it:
463
+ * - Batches requests together to reduce API calls
464
+ * - Checks cache for each text individually
465
+ * - Only requests translations for cache misses
466
+ * - Maintains the order of input texts in the result array
467
+ *
468
+ * @param texts - Array of texts to translate
469
+ * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
470
+ * @param source_lang - Optional source language code. If not provided, auto-detection is used
471
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
472
+ * @returns Promise resolving to an array of translated texts in the same order as input
473
+ * @throws {Error} If the translation request fails (network error, API error, timeout)
474
+ *
475
+ * @example
476
+ * ```typescript
477
+ * // Translate multiple texts
478
+ * const results = await engine.translateMany(['Hello', 'World', 'Test'], 'es');
479
+ * // Returns: ['Hola', 'Mundo', 'Prueba']
480
+ *
481
+ * // With source language
482
+ * const results = await engine.translateMany(['Hello', 'World'], 'fr', 'en');
483
+ *
484
+ * // With context (user_ctx) - descriptive context helps improve translation quality
485
+ * const results = await engine.translateMany(
486
+ * ['Hello', 'World'],
487
+ * 'es',
488
+ * undefined,
489
+ * { user_ctx: 'greeting message on the homepage' }
490
+ * );
491
+ *
492
+ * // With cache_type
493
+ * const results = await engine.translateMany(
494
+ * ['Hello', 'World'],
495
+ * 'es',
496
+ * undefined,
497
+ * { cache_type: 'managed' }
498
+ * );
499
+ *
500
+ * // Empty array returns empty array
501
+ * const results = await engine.translateMany([], 'es');
502
+ * // Returns: []
503
+ * ```
504
+ */
387
505
  async translateMany(texts, lang, source_lang, ctx) {
388
506
  const results = new Array(texts.length);
389
507
  const cacheMisses = [];
@@ -425,6 +543,34 @@ var BelocalEngine = class {
425
543
  }
426
544
  return results;
427
545
  }
546
+ /**
547
+ * Shortcut method for translation with simplified API.
548
+ *
549
+ * This is a convenience method that wraps `translate()`. When `context` is provided as a string,
550
+ * it is automatically wrapped in `{user_ctx: context}` object.
551
+ *
552
+ * @param text - The text to translate
553
+ * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
554
+ * @param source_lang - Optional source language code
555
+ * @param context - Optional descriptive context string in English (will be wrapped as {user_ctx: context})
556
+ * @returns Promise resolving to the translated text
557
+ * @throws {Error} If the translation request fails (network error, API error, timeout)
558
+ *
559
+ * @example
560
+ * ```typescript
561
+ * // Simple translation
562
+ * const result = await engine.t('Hello world', 'es');
563
+ *
564
+ * // With source language
565
+ * const result = await engine.t('Hello world', 'fr', 'en');
566
+ *
567
+ * // With context string (automatically wrapped as {user_ctx: 'greeting message on the homepage'})
568
+ * const result = await engine.t('Hello world', 'es', undefined, 'greeting message on the homepage');
569
+ *
570
+ * // With source language and context
571
+ * const result = await engine.t('Hello world', 'es', 'en', 'greeting message on the homepage');
572
+ * ```
573
+ */
428
574
  async t(text, lang, source_lang, context) {
429
575
  if (context) {
430
576
  return this.translate(text, lang, source_lang, { user_ctx: context });