@belocal/js-sdk 0.7.0 → 1.0.1

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.mjs CHANGED
@@ -1,156 +1,11 @@
1
- import { md5 } from 'js-md5';
2
1
  import * as http2 from 'http2';
3
2
  import { URL } from 'url';
3
+ import { md5 } from 'js-md5';
4
4
 
5
- // src/transports/multi.ts
6
- function generateRequestId(texts, lang, sourceLang, context) {
7
- const sortedTexts = [...texts].sort();
8
- let sortedContext = null;
9
- if (context && Object.keys(context).length > 0) {
10
- sortedContext = {};
11
- const sortedKeys = Object.keys(context).sort();
12
- for (const key of sortedKeys) {
13
- sortedContext[key] = context[key];
14
- }
15
- }
16
- const data = [sortedTexts, lang, sourceLang || null, sortedContext];
17
- const json = JSON.stringify(data);
18
- return md5(json);
19
- }
20
- async function sendMulti(config, items, state) {
21
- if (config.debug) {
22
- console.log(`[BeLocal Multi Transport] Sending multi request with ${items.length} texts`);
23
- }
24
- try {
25
- const groups = /* @__PURE__ */ new Map();
26
- items.forEach((item) => {
27
- const groupKey = JSON.stringify({
28
- lang: item.lang,
29
- sourceLang: item.sourceLang || null,
30
- context: item.context || null
31
- });
32
- if (!groups.has(groupKey)) {
33
- groups.set(groupKey, []);
34
- }
35
- groups.get(groupKey).push(item);
36
- });
37
- const requestIdToGroupItems = /* @__PURE__ */ new Map();
38
- const requests = Array.from(groups.entries()).map(([groupKey, groupItems]) => {
39
- const firstItem = groupItems[0];
40
- const texts = groupItems.map((item) => item.text);
41
- const requestId = generateRequestId(
42
- texts,
43
- firstItem.lang,
44
- firstItem.sourceLang,
45
- firstItem.context
46
- );
47
- requestIdToGroupItems.set(requestId, groupItems);
48
- return {
49
- requestId,
50
- texts,
51
- lang: firstItem.lang,
52
- sourceLang: firstItem.sourceLang,
53
- context: firstItem.context
54
- };
55
- });
56
- const multiResponse = await config.baseTransport.post({ requests }, "/v1/translate/multi");
57
- if (config.debug) {
58
- console.log(`[BeLocal Multi Transport] Multi response received with ${multiResponse.results.length} groups`);
59
- }
60
- const resultMap = /* @__PURE__ */ new Map();
61
- multiResponse.results.forEach((result) => {
62
- resultMap.set(result.requestId, { texts: result.data.texts, status: result.data.status });
63
- });
64
- requestIdToGroupItems.forEach((groupItems, requestId) => {
65
- const result = resultMap.get(requestId);
66
- if (!result) {
67
- if (config.debug) {
68
- console.error(`[BeLocal Multi Transport] No result found for requestId: ${requestId}`);
69
- }
70
- groupItems.forEach((item) => {
71
- item.reject(new Error(`No result found for request ${requestId}`));
72
- });
73
- return;
74
- }
75
- if (result.texts.length !== groupItems.length) {
76
- const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for requestId ${requestId}`);
77
- if (config.debug) {
78
- console.error(`[BeLocal Multi Transport]`, error.message);
79
- }
80
- groupItems.forEach((item) => item.reject(error));
81
- return;
82
- }
83
- groupItems.forEach((item, index) => {
84
- const translatedText = result.texts[index];
85
- const status = result.status || "success";
86
- if (config.debug) {
87
- console.log(`[BeLocal Multi Transport] Success for requestId ${requestId}[${index}]: "${translatedText}"`);
88
- }
89
- item.resolve({ text: translatedText, status });
90
- });
91
- });
92
- } catch (error) {
93
- if (config.debug) {
94
- console.error(`[BeLocal Multi Transport] Multi request error:`, error);
95
- }
96
- const errorToReject = error instanceof Error ? error : new Error(String(error));
97
- items.forEach((item) => item.reject(errorToReject));
98
- } finally {
99
- }
100
- }
101
- function processMulti(config, state) {
102
- if (state.currentMulti.length === 0 || state.isRequestInFlight) {
103
- return;
104
- }
105
- const itemsToSend = [...state.currentMulti];
106
- state.currentMulti = [];
107
- state.multiTimer = null;
108
- state.isRequestInFlight = true;
109
- sendMulti(config, itemsToSend).finally(() => {
110
- state.isRequestInFlight = false;
111
- if (state.currentMulti.length > 0) {
112
- const windowMs = config.batchWindowMs ?? 50;
113
- state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);
114
- }
115
- });
116
- }
117
- function createMultiTransport(config) {
118
- const windowMs = config.batchWindowMs ?? 50;
119
- const state = {
120
- currentMulti: [],
121
- multiTimer: null,
122
- isRequestInFlight: false
123
- };
124
- return ({ text, lang, source_lang, ctx }) => {
125
- return new Promise((resolve, reject) => {
126
- if (config.debug) {
127
- const tempRequestId = md5(JSON.stringify([text, lang, source_lang || null, ctx || null]));
128
- console.log(`[BeLocal Multi Transport] Queuing request ${tempRequestId}: "${text}" to ${lang}`);
129
- }
130
- const requestItem = {
131
- text,
132
- lang,
133
- sourceLang: source_lang,
134
- context: ctx,
135
- resolve,
136
- reject
137
- };
138
- state.currentMulti.push(requestItem);
139
- if (state.multiTimer === null && !state.isRequestInFlight) {
140
- state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);
141
- }
142
- });
143
- };
144
- }
5
+ // src/transports/base/node.ts
145
6
 
146
7
  // src/version.ts
147
- var SDK_VERSION = (() => {
148
- try {
149
- return true ? "0.7.0" : "undefined";
150
- } catch {
151
- return "undefined";
152
- }
153
- })();
8
+ var SDK_VERSION = "1.0.1" ;
154
9
  var SDK_NAME = "js";
155
10
 
156
11
  // src/transports/base/dedupe.ts
@@ -351,6 +206,136 @@ function createBaseNodeTransport(config) {
351
206
  const transport = new BaseNodeTransport(config);
352
207
  return new DedupeTransport(transport, config.debug);
353
208
  }
209
+ function generateRequestId(texts, lang, sourceLang, context) {
210
+ const sortedTexts = [...texts].sort();
211
+ const sortedContext = context && Object.keys(context).length > 0 ? Object.fromEntries(Object.entries(context).sort(([a], [b]) => a.localeCompare(b))) : null;
212
+ const data = [sortedTexts, lang, sourceLang || null, sortedContext];
213
+ return md5(JSON.stringify(data));
214
+ }
215
+ async function sendMulti(config, items, state) {
216
+ if (config.debug) {
217
+ console.log(`[BeLocal Multi Transport] Sending multi request with ${items.length} texts`);
218
+ }
219
+ try {
220
+ const groups = /* @__PURE__ */ new Map();
221
+ items.forEach((item) => {
222
+ const groupKey = JSON.stringify({
223
+ lang: item.lang,
224
+ sourceLang: item.sourceLang || null,
225
+ context: item.context || null
226
+ });
227
+ if (!groups.has(groupKey)) {
228
+ groups.set(groupKey, []);
229
+ }
230
+ groups.get(groupKey).push(item);
231
+ });
232
+ const requestIdToGroupItems = /* @__PURE__ */ new Map();
233
+ const requests = Array.from(groups.entries()).map(([groupKey, groupItems]) => {
234
+ const firstItem = groupItems[0];
235
+ const texts = groupItems.map((item) => item.text);
236
+ const requestId = generateRequestId(
237
+ texts,
238
+ firstItem.lang,
239
+ firstItem.sourceLang,
240
+ firstItem.context
241
+ );
242
+ requestIdToGroupItems.set(requestId, groupItems);
243
+ return {
244
+ request_id: requestId,
245
+ texts,
246
+ lang: firstItem.lang,
247
+ source_lang: firstItem.sourceLang,
248
+ ctx: firstItem.context
249
+ };
250
+ });
251
+ const multiResponse = await config.baseTransport.post({ requests }, "/v1/translate/multi");
252
+ if (config.debug) {
253
+ console.log(`[BeLocal Multi Transport] Multi response received with ${multiResponse.results.length} groups`);
254
+ }
255
+ const resultMap = /* @__PURE__ */ new Map();
256
+ multiResponse.results.forEach((result) => {
257
+ resultMap.set(result.request_id, { texts: result.data.texts, status: result.data.status });
258
+ });
259
+ requestIdToGroupItems.forEach((groupItems, requestId) => {
260
+ const result = resultMap.get(requestId);
261
+ if (!result) {
262
+ if (config.debug) {
263
+ console.error(`[BeLocal Multi Transport] No result found for request_id: ${requestId}`);
264
+ }
265
+ groupItems.forEach((item) => {
266
+ item.reject(new Error(`No result found for request ${requestId}`));
267
+ });
268
+ return;
269
+ }
270
+ if (result.texts.length !== groupItems.length) {
271
+ const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for request_id ${requestId}`);
272
+ if (config.debug) {
273
+ console.error(`[BeLocal Multi Transport]`, error.message);
274
+ }
275
+ groupItems.forEach((item) => item.reject(error));
276
+ return;
277
+ }
278
+ groupItems.forEach((item, index) => {
279
+ const translatedText = result.texts[index];
280
+ const status = result.status || "success";
281
+ if (config.debug) {
282
+ console.log(`[BeLocal Multi Transport] Success for request_id ${requestId}[${index}]: "${translatedText}"`);
283
+ }
284
+ item.resolve({ text: translatedText, status });
285
+ });
286
+ });
287
+ } catch (error) {
288
+ if (config.debug) {
289
+ console.error(`[BeLocal Multi Transport] Multi request error:`, error);
290
+ }
291
+ const errorToReject = error instanceof Error ? error : new Error(String(error));
292
+ items.forEach((item) => item.reject(errorToReject));
293
+ }
294
+ }
295
+ function processMulti(config, state) {
296
+ if (state.currentMulti.length === 0 || state.isRequestInFlight) {
297
+ return;
298
+ }
299
+ const itemsToSend = [...state.currentMulti];
300
+ state.currentMulti = [];
301
+ state.multiTimer = null;
302
+ state.isRequestInFlight = true;
303
+ sendMulti(config, itemsToSend).finally(() => {
304
+ state.isRequestInFlight = false;
305
+ if (state.currentMulti.length > 0) {
306
+ const windowMs = config.batchWindowMs ?? 50;
307
+ state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);
308
+ }
309
+ });
310
+ }
311
+ function createMultiTransport(config) {
312
+ const windowMs = config.batchWindowMs ?? 50;
313
+ const state = {
314
+ currentMulti: [],
315
+ multiTimer: null,
316
+ isRequestInFlight: false
317
+ };
318
+ return ({ text, lang, source_lang, ctx }) => {
319
+ return new Promise((resolve, reject) => {
320
+ if (config.debug) {
321
+ const tempRequestId = md5(JSON.stringify([text, lang, source_lang || null, ctx || null]));
322
+ console.log(`[BeLocal Multi Transport] Queuing request ${tempRequestId}: "${text}" to ${lang}`);
323
+ }
324
+ const requestItem = {
325
+ text,
326
+ lang,
327
+ sourceLang: source_lang,
328
+ context: ctx,
329
+ resolve,
330
+ reject
331
+ };
332
+ state.currentMulti.push(requestItem);
333
+ if (state.multiTimer === null && !state.isRequestInFlight) {
334
+ state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);
335
+ }
336
+ });
337
+ };
338
+ }
354
339
 
355
340
  // src/cache/local.ts
356
341
  var LocalCache = class {
@@ -363,28 +348,9 @@ var LocalCache = class {
363
348
  set(key, value) {
364
349
  this.storage.set(key, value);
365
350
  }
366
- isAvailable() {
367
- return true;
368
- }
369
351
  };
370
352
  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
- */
387
- constructor(options) {
353
+ constructor(options, baseTransportFactory) {
388
354
  const {
389
355
  apiKey,
390
356
  batchWindowMs = 50,
@@ -393,13 +359,10 @@ var BelocalEngine = class {
393
359
  } = options;
394
360
  this.debug = debug;
395
361
  this.cache = new LocalCache();
396
- if (this.debug) {
397
- console.log("[BeLocal Engine] Using local (memory) cache");
398
- }
399
362
  const authHeaders = {
400
363
  "Authorization": `Bearer ${apiKey}`
401
364
  };
402
- const baseTransport = createBaseNodeTransport({
365
+ const baseTransport = baseTransportFactory({
403
366
  headers: authHeaders,
404
367
  timeoutMs,
405
368
  debug: this.debug
@@ -417,91 +380,12 @@ var BelocalEngine = class {
417
380
  });
418
381
  }
419
382
  }
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
+ /** Translates a single text string to the target language. */
455
384
  async translate(text, lang, source_lang, ctx) {
456
385
  const results = await this.translateMany([text], lang, source_lang, ctx);
457
386
  return results[0];
458
387
  }
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
- */
388
+ /** Translates multiple text strings to the target language in a single batch. */
505
389
  async translateMany(texts, lang, source_lang, ctx) {
506
390
  const results = new Array(texts.length);
507
391
  const cacheMisses = [];
@@ -512,7 +396,7 @@ var BelocalEngine = class {
512
396
  if (cachedResult) {
513
397
  results[i] = cachedResult;
514
398
  if (this.debug) {
515
- console.log("[BeLocal Engine] Translation from local cache:", text);
399
+ console.log("[BeLocal Engine] Translation from cache:", text);
516
400
  }
517
401
  continue;
518
402
  }
@@ -527,7 +411,7 @@ var BelocalEngine = class {
527
411
  const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);
528
412
  this.cache.set(cacheKey, result.text);
529
413
  if (this.debug) {
530
- console.log("[BeLocal Engine] Translation from API, cached in local:", text);
414
+ console.log("[BeLocal Engine] Translation from API, cached:", text);
531
415
  }
532
416
  } else {
533
417
  if (this.debug) {
@@ -543,45 +427,13 @@ var BelocalEngine = class {
543
427
  }
544
428
  return results;
545
429
  }
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
- */
574
- async t(text, lang, source_lang, context) {
575
- if (context) {
576
- return this.translate(text, lang, source_lang, { user_ctx: context });
577
- }
578
- return this.translate(text, lang, source_lang);
430
+ /** Shortcut for translate() */
431
+ async t(text, lang, source_lang, context, managed = false) {
432
+ const ctx = context || managed ? { ...context ? { user_ctx: context } : {}, ...managed ? { cache_type: "managed" } : {} } : void 0;
433
+ return this.translate(text, lang, source_lang, ctx);
579
434
  }
580
435
  generateCacheKey(text, lang, source_lang, ctx) {
581
- const sortedCtx = ctx ? Object.keys(ctx).sort().reduce((acc, key) => {
582
- acc[key] = ctx[key];
583
- return acc;
584
- }, {}) : null;
436
+ const sortedCtx = ctx ? Object.fromEntries(Object.entries(ctx).sort(([a], [b]) => a.localeCompare(b))) : null;
585
437
  const data = {
586
438
  text,
587
439
  lang,
@@ -592,6 +444,17 @@ var BelocalEngine = class {
592
444
  }
593
445
  };
594
446
 
595
- export { BaseNodeTransport, BelocalEngine, createBaseNodeTransport, createMultiTransport };
447
+ // src/core/types.ts
448
+ var USER_TYPE_PRODUCT = "product";
449
+ var USER_TYPE_CHAT = "chat";
450
+
451
+ // src/core/engine/node.ts
452
+ var BelocalEngine2 = class extends BelocalEngine {
453
+ constructor(options) {
454
+ super(options, createBaseNodeTransport);
455
+ }
456
+ };
457
+
458
+ export { BaseNodeTransport, BelocalEngine2 as BelocalEngine, USER_TYPE_CHAT, USER_TYPE_PRODUCT, createBaseNodeTransport, createMultiTransport };
596
459
  //# sourceMappingURL=node.mjs.map
597
460
  //# sourceMappingURL=node.mjs.map