@belocal/js-sdk 1.0.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/README.md CHANGED
@@ -1,80 +1,61 @@
1
1
  # @belocal/js-sdk
2
2
 
3
- Runtime JS SDK for on-demand translation with platform-specific builds.
3
+ On-demand translation SDK for browser and Node.js.
4
4
 
5
5
  ## Install
6
+
6
7
  ```bash
7
8
  npm i @belocal/js-sdk
8
9
  ```
9
10
 
10
11
  ## Usage
11
12
 
12
- The SDK automatically selects the optimal build for your environment:
13
-
14
13
  ```javascript
15
14
  import { BelocalEngine } from '@belocal/js-sdk';
16
15
 
17
- const engine = new BelocalEngine({
18
- apiKey: 'your-api-key'
19
- });
16
+ const engine = new BelocalEngine({ apiKey: 'your-api-key' });
17
+
18
+ // Single translation
19
+ const text = await engine.t('Hello world', 'es');
20
20
 
21
- const translated = await engine.t('Hello world', 'es');
22
- console.log(translated); // "Hola mundo"
21
+ // With context
22
+ const text2 = await engine.t('Hello world', 'es', 'en', 'homepage greeting');
23
+
24
+ // Managed translation (cache_type: 'managed')
25
+ const text3 = await engine.t('Hello world', 'es', undefined, undefined, true);
26
+
27
+ // Batch translation
28
+ const texts = await engine.translateMany(['Hello', 'World'], 'es');
23
29
  ```
24
30
 
25
- ### Options
31
+ ## Options
26
32
 
27
33
  ```javascript
28
34
  const engine = new BelocalEngine({
29
- apiKey: 'your-api-key', // Required: Your BeLocal API key
30
- debug: false // Optional: Enable debug logging
35
+ apiKey: 'your-api-key', // required
36
+ batchWindowMs: 50, // batch window in ms (default: 50)
37
+ timeoutMs: 10000, // request timeout in ms (default: 10000)
38
+ debug: false // enable debug logging (default: false)
31
39
  });
32
40
  ```
33
41
 
34
- ## Automatic Environment Detection
35
-
36
- The SDK uses conditional exports to automatically load the optimal build:
37
-
38
- - **Browser environments**: Uses `fetch` API with optimized bundle size (~1.3KB)
39
- - **Node.js environments**: Uses HTTP/2 with connection pooling and automatic retries (~4.3KB)
40
- - **CommonJS**: Supported in both environments
41
-
42
- ## Build Outputs
42
+ ### batchWindowMs
43
43
 
44
- | File | Environment | Format | Size | Features |
45
- |------|-------------|--------|------|----------|
46
- | `browser.mjs` | Browser | ESM | ~1.3KB | Fetch API, minified |
47
- | `browser.cjs` | Browser | CJS | ~1.3KB | Fetch API, minified |
48
- | `node.mjs` | Node.js | ESM | ~4.3KB | HTTP/2, retries |
49
- | `node.cjs` | Node.js | CJS | ~4.8KB | HTTP/2, retries |
44
+ The SDK batches multiple `translate()` / `t()` calls made within a time window into a single HTTP request. When you call `t()` or `translate()`, the request is not sent immediately — instead it is queued. After `batchWindowMs` milliseconds of inactivity, all queued texts are grouped by target language and sent as one batch request to `/v1/translate/multi`. This reduces the number of HTTP round-trips when translating many strings at once (e.g. rendering a page). Default is `50`ms.
50
45
 
51
- ## TypeScript Support
52
-
53
- Full TypeScript support with exported types:
46
+ ## TypeScript
54
47
 
55
48
  ```typescript
56
- import { BelocalEngine, type BelocalEngineOptions, type Lang, type KV } from '@belocal/js-sdk';
57
-
58
- const options: BelocalEngineOptions = {
59
- apiKey: 'your-api-key',
60
- debug: true
61
- };
62
-
63
- const engine = new BelocalEngine(options);
64
- ```
65
-
66
- ## Development
67
-
68
- ### Testing
69
-
70
- ```bash
71
- # Run all tests
72
- npm test
73
-
74
- # Run tests in watch mode
75
- npm run test:watch
49
+ import {
50
+ BelocalEngine,
51
+ type BelocalEngineOptions,
52
+ type Lang,
53
+ type TranslationCtx,
54
+ USER_TYPE_PRODUCT,
55
+ USER_TYPE_CHAT
56
+ } from '@belocal/js-sdk';
76
57
  ```
77
58
 
78
59
  ## License
79
60
 
80
- MIT
61
+ MIT
package/dist/browser.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';var jsMd5=require('js-md5');var B=(()=>{try{return "1.0.0"}catch{return "undefined"}})(),b="js";function E(a,t){let s=r=>{if(r==null)return r;if(Array.isArray(r))return r.map(s);if(typeof r=="object"){let i={};for(let c of Object.keys(r).sort())i[c]=s(r[c]);return i}return r},e=JSON.stringify(s(t));return `${a}:${e}`}var h=class{constructor(t,s){this.wrappedTransport=t;this.debug=s;this.inFlightRequests=new Map;}async post(t,s){let e=E(s,t),r=this.inFlightRequests.get(e);if(r)return this.debug&&console.log(`[DedupeTransport] Deduplicating request to ${s}`),r;let i=this.wrappedTransport.post(t,s).finally(()=>{this.inFlightRequests.delete(e);});return this.inFlightRequests.set(e,i),this.debug&&console.log(`[DedupeTransport] New request to ${s} (${this.inFlightRequests.size} in-flight)`),i}};var S="https://dynamic.belocal.dev",f=class{constructor(t){this.config=t;}async post(t,s){let e=`${S}${s}`,r=new AbortController,i=this.config.timeoutMs??1e4,c=setTimeout(()=>r.abort(),i);this.config.debug&&console.log(`[Base Browser Transport] POST request to ${e}`,t);try{let u=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","x-sdk":b,"x-sdk-version":B,...this.config.headers},body:JSON.stringify(t),signal:r.signal});if(!u.ok){let o=`HTTP ${u.status}: ${u.statusText}`;throw this.config.debug&&console.error("[Base Browser Transport] Request failed:",o),new Error(o)}let n=await u.json();return this.config.debug&&console.log("[Base Browser Transport] Request successful:",n),n}finally{c&&clearTimeout(c);}}};function m(a){let t=new f(a);return new h(t,a.debug)}function _(a,t,s,e){let r=[...a].sort(),i=null;if(e&&Object.keys(e).length>0){i={};let n=Object.keys(e).sort();for(let o of n)i[o]=e[o];}let u=JSON.stringify([r,t,s||null,i]);return jsMd5.md5(u)}async function K(a,t,s){a.debug&&console.log(`[BeLocal Multi Transport] Sending multi request with ${t.length} texts`);try{let e=new Map;t.forEach(n=>{let o=JSON.stringify({lang:n.lang,sourceLang:n.sourceLang||null,context:n.context||null});e.has(o)||e.set(o,[]),e.get(o).push(n);});let r=new Map,i=Array.from(e.entries()).map(([n,o])=>{let l=o[0],g=o.map(d=>d.text),p=_(g,l.lang,l.sourceLang,l.context);return r.set(p,o),{request_id:p,texts:g,lang:l.lang,source_lang:l.sourceLang,ctx:l.context}}),c=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${c.results.length} groups`);let u=new Map;c.results.forEach(n=>{u.set(n.request_id,{texts:n.data.texts,status:n.data.status});}),r.forEach((n,o)=>{let l=u.get(o);if(!l){a.debug&&console.error(`[BeLocal Multi Transport] No result found for request_id: ${o}`),n.forEach(g=>{g.reject(new Error(`No result found for request ${o}`));});return}if(l.texts.length!==n.length){let g=new Error(`Mismatch: expected ${n.length} texts, got ${l.texts.length} for request_id ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",g.message),n.forEach(p=>p.reject(g));return}n.forEach((g,p)=>{let d=l.texts[p],q=l.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for request_id ${o}[${p}]: "${d}"`),g.resolve({text:d,status:q});});});}catch(e){a.debug&&console.error("[BeLocal Multi Transport] Multi request error:",e);let r=e instanceof Error?e:new Error(String(e));t.forEach(i=>i.reject(r));}finally{}}function x(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let s=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,K(a,s).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let e=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>x(a,t),e);}});}function M(a){let t=a.batchWindowMs??50,s={currentMulti:[],multiTimer:null,isRequestInFlight:false};return ({text:e,lang:r,source_lang:i,ctx:c})=>new Promise((u,n)=>{if(a.debug){let l=jsMd5.md5(JSON.stringify([e,r,i||null,c||null]));console.log(`[BeLocal Multi Transport] Queuing request ${l}: "${e}" to ${r}`);}let o={text:e,lang:r,sourceLang:i,context:c,resolve:u,reject:n};s.currentMulti.push(o),s.multiTimer===null&&!s.isRequestInFlight&&(s.multiTimer=setTimeout(()=>x(a,s),t));})}var y=class{constructor(){this.storage=new Map;}get(t){return this.storage.get(t)||null}set(t,s){this.storage.set(t,s);}isAvailable(){return true}};var T=class{constructor(t,s){let{apiKey:e,batchWindowMs:r=50,timeoutMs:i=1e4,debug:c=false}=t;this.debug=c,this.cache=new y;let u={Authorization:`Bearer ${e}`},n=s({headers:u,timeoutMs:i,debug:this.debug});this.transport=M({baseTransport:n,debug:this.debug,batchWindowMs:r}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:i,batchWindowMs:r});}async translate(t,s,e,r){return (await this.translateMany([t],s,e,r))[0]}async translateMany(t,s,e,r){let i=new Array(t.length),c=[];for(let u=0;u<t.length;u++){let n=t[u],o=this.generateCacheKey(n,s,e,r),l=this.cache.get(o);if(l){i[u]=l,this.debug&&console.log("[BeLocal Engine] Translation from cache:",n);continue}i[u]=null,c.push({index:u,text:n});}return c.length>0&&(await Promise.all(c.map(async({index:n,text:o})=>{let l=await this.transport({text:o,lang:s,source_lang:e,ctx:r});if(l.status!=="error"){let g=this.generateCacheKey(o,s,e,r);this.cache.set(g,l.text),this.debug&&console.log("[BeLocal Engine] Translation from API, cached:",o);}else this.debug&&console.log("[BeLocal Engine] Translation from API (not cached due to error status):",o);return {index:n,translation:l.text}}))).forEach(({index:n,translation:o})=>{i[n]=o;}),i}async t(t,s,e,r){return r?this.translate(t,s,e,{user_ctx:r}):this.translate(t,s,e)}generateCacheKey(t,s,e,r){let i=r?Object.keys(r).sort().reduce((u,n)=>(u[n]=r[n],u),{}):null;return jsMd5.md5(JSON.stringify({text:t,lang:s,source_lang:e||null,ctx:i}))}};var R=class extends T{constructor(t){super(t,m);}};
2
- exports.BaseBrowserTransport=f;exports.BelocalEngine=R;exports.createBaseBrowserTransport=m;exports.createMultiTransport=M;//# sourceMappingURL=browser.cjs.map
1
+ 'use strict';var jsMd5=require('js-md5');var x="1.0.1";function _(i,t){let s=e=>{if(e==null)return e;if(Array.isArray(e))return e.map(s);if(typeof e=="object"){let o={};for(let c of Object.keys(e).sort())o[c]=s(e[c]);return o}return e},r=JSON.stringify(s(t));return `${i}:${r}`}var m=class{constructor(t,s){this.wrappedTransport=t;this.debug=s;this.inFlightRequests=new Map;}async post(t,s){let r=_(s,t),e=this.inFlightRequests.get(r);if(e)return this.debug&&console.log(`[DedupeTransport] Deduplicating request to ${s}`),e;let o=this.wrappedTransport.post(t,s).finally(()=>{this.inFlightRequests.delete(r);});return this.inFlightRequests.set(r,o),this.debug&&console.log(`[DedupeTransport] New request to ${s} (${this.inFlightRequests.size} in-flight)`),o}};var q="https://dynamic.belocal.dev",f=class{constructor(t){this.config=t;}async post(t,s){let r=`${q}${s}`,e=new AbortController,o=this.config.timeoutMs??1e4,c=setTimeout(()=>e.abort(),o);this.config.debug&&console.log(`[Base Browser Transport] POST request to ${r}`,t);try{let u=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json","x-sdk":"js","x-sdk-version":x,...this.config.headers},body:JSON.stringify(t),signal:e.signal});if(!u.ok){let a=`HTTP ${u.status}: ${u.statusText}`;throw this.config.debug&&console.error("[Base Browser Transport] Request failed:",a),new Error(a)}let n=await u.json();return this.config.debug&&console.log("[Base Browser Transport] Request successful:",n),n}finally{c&&clearTimeout(c);}}};function h(i){let t=new f(i);return new m(t,i.debug)}function S(i,t,s,r){let e=[...i].sort(),o=r&&Object.keys(r).length>0?Object.fromEntries(Object.entries(r).sort(([u],[n])=>u.localeCompare(n))):null;return jsMd5.md5(JSON.stringify([e,t,s||null,o]))}async function K(i,t,s){i.debug&&console.log(`[BeLocal Multi Transport] Sending multi request with ${t.length} texts`);try{let r=new Map;t.forEach(n=>{let a=JSON.stringify({lang:n.lang,sourceLang:n.sourceLang||null,context:n.context||null});r.has(a)||r.set(a,[]),r.get(a).push(n);});let e=new Map,o=Array.from(r.entries()).map(([n,a])=>{let l=a[0],p=a.map(d=>d.text),g=S(p,l.lang,l.sourceLang,l.context);return e.set(g,a),{request_id:g,texts:p,lang:l.lang,source_lang:l.sourceLang,ctx:l.context}}),c=await i.baseTransport.post({requests:o},"/v1/translate/multi");i.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${c.results.length} groups`);let u=new Map;c.results.forEach(n=>{u.set(n.request_id,{texts:n.data.texts,status:n.data.status});}),e.forEach((n,a)=>{let l=u.get(a);if(!l){i.debug&&console.error(`[BeLocal Multi Transport] No result found for request_id: ${a}`),n.forEach(p=>{p.reject(new Error(`No result found for request ${a}`));});return}if(l.texts.length!==n.length){let p=new Error(`Mismatch: expected ${n.length} texts, got ${l.texts.length} for request_id ${a}`);i.debug&&console.error("[BeLocal Multi Transport]",p.message),n.forEach(g=>g.reject(p));return}n.forEach((p,g)=>{let d=l.texts[g],R=l.status||"success";i.debug&&console.log(`[BeLocal Multi Transport] Success for request_id ${a}[${g}]: "${d}"`),p.resolve({text:d,status:R});});});}catch(r){i.debug&&console.error("[BeLocal Multi Transport] Multi request error:",r);let e=r instanceof Error?r:new Error(String(r));t.forEach(o=>o.reject(e));}}function B(i,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let s=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,K(i,s).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let r=i.batchWindowMs??50;t.multiTimer=setTimeout(()=>B(i,t),r);}});}function M(i){let t=i.batchWindowMs??50,s={currentMulti:[],multiTimer:null,isRequestInFlight:false};return ({text:r,lang:e,source_lang:o,ctx:c})=>new Promise((u,n)=>{if(i.debug){let l=jsMd5.md5(JSON.stringify([r,e,o||null,c||null]));console.log(`[BeLocal Multi Transport] Queuing request ${l}: "${r}" to ${e}`);}let a={text:r,lang:e,sourceLang:o,context:c,resolve:u,reject:n};s.currentMulti.push(a),s.multiTimer===null&&!s.isRequestInFlight&&(s.multiTimer=setTimeout(()=>B(i,s),t));})}var T=class{constructor(){this.storage=new Map;}get(t){return this.storage.get(t)||null}set(t,s){this.storage.set(t,s);}};var y=class{constructor(t,s){let{apiKey:r,batchWindowMs:e=50,timeoutMs:o=1e4,debug:c=false}=t;this.debug=c,this.cache=new T;let u={Authorization:`Bearer ${r}`},n=s({headers:u,timeoutMs:o,debug:this.debug});this.transport=M({baseTransport:n,debug:this.debug,batchWindowMs:e}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:o,batchWindowMs:e});}async translate(t,s,r,e){return (await this.translateMany([t],s,r,e))[0]}async translateMany(t,s,r,e){let o=new Array(t.length),c=[];for(let u=0;u<t.length;u++){let n=t[u],a=this.generateCacheKey(n,s,r,e),l=this.cache.get(a);if(l){o[u]=l,this.debug&&console.log("[BeLocal Engine] Translation from cache:",n);continue}o[u]=null,c.push({index:u,text:n});}return c.length>0&&(await Promise.all(c.map(async({index:n,text:a})=>{let l=await this.transport({text:a,lang:s,source_lang:r,ctx:e});if(l.status!=="error"){let p=this.generateCacheKey(a,s,r,e);this.cache.set(p,l.text),this.debug&&console.log("[BeLocal Engine] Translation from API, cached:",a);}else this.debug&&console.log("[BeLocal Engine] Translation from API (not cached due to error status):",a);return {index:n,translation:l.text}}))).forEach(({index:n,translation:a})=>{o[n]=a;}),o}async t(t,s,r,e,o=false){let c=e||o?{...e?{user_ctx:e}:{},...o?{cache_type:"managed"}:{}}:void 0;return this.translate(t,s,r,c)}generateCacheKey(t,s,r,e){let o=e?Object.fromEntries(Object.entries(e).sort(([u],[n])=>u.localeCompare(n))):null;return jsMd5.md5(JSON.stringify({text:t,lang:s,source_lang:r||null,ctx:o}))}};var O="product",L="chat";var w=class extends y{constructor(t){super(t,h);}};
2
+ exports.BaseBrowserTransport=f;exports.BelocalEngine=w;exports.USER_TYPE_CHAT=L;exports.USER_TYPE_PRODUCT=O;exports.createBaseBrowserTransport=h;exports.createMultiTransport=M;//# sourceMappingURL=browser.cjs.map
3
3
  //# sourceMappingURL=browser.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/browser.ts","../src/transports/multi.ts","../src/cache/local.ts","../src/core/engine/engine.ts","../src/core/engine/browser.ts"],"names":["SDK_VERSION","SDK_NAME","generateRequestKey","endpointPath","data","normalize","value","sorted","key","normalizedData","DedupeTransport","wrappedTransport","debug","requestKey","existingRequest","requestPromise","BASE_URL","BaseBrowserTransport","config","url","controller","timeoutMs","timeoutId","response","errorMsg","result","createBaseBrowserTransport","transport","generateRequestId","texts","lang","sourceLang","context","sortedTexts","sortedContext","sortedKeys","json","md5","sendMulti","items","state","groups","item","groupKey","requestIdToGroupItems","requests","groupItems","firstItem","requestId","multiResponse","resultMap","error","index","translatedText","status","errorToReject","processMulti","itemsToSend","windowMs","createMultiTransport","text","source_lang","ctx","resolve","reject","tempRequestId","requestItem","LocalCache","BelocalEngine","options","baseTransportFactory","apiKey","batchWindowMs","authHeaders","baseTransport","results","cacheMisses","i","cacheKey","cachedResult","translation","sortedCtx","acc"],"mappings":"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","file":"browser.cjs","sourcesContent":["// SDK version - will be replaced during build with tsup define\ndeclare const __SDK_VERSION__: string | undefined;\n\n// Safely check if __SDK_VERSION__ is defined (replaced during build)\nexport const SDK_VERSION: string = \n (() => { try { return typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : 'undefined'; } catch { return 'undefined'; } })();\n\nexport const SDK_NAME = 'js';\n\n","import type { BaseTransport } from '../../core/types';\n\n/**\n * Generates a deterministic key for request deduplication.\n * Uses sorted keys to ensure consistent key generation regardless of object property order.\n */\nfunction generateRequestKey(endpointPath: string, data: any): string {\n // Normalize data by sorting object keys for consistent serialization\n const normalize = (value: any): any => {\n if (value === null || value === undefined) {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map(normalize);\n }\n if (typeof value === 'object') {\n const sorted: Record<string, any> = {};\n for (const key of Object.keys(value).sort()) {\n sorted[key] = normalize(value[key]);\n }\n return sorted;\n }\n return value;\n };\n \n const normalizedData = JSON.stringify(normalize(data));\n return `${endpointPath}:${normalizedData}`;\n}\n\n/**\n * DedupeTransport wraps a BaseTransport instance and deduplicates in-flight requests.\n * If multiple identical requests (same endpointPath + same request body) are made\n * concurrently, they will share the same underlying HTTP request and promise.\n */\nexport class DedupeTransport implements BaseTransport {\n private inFlightRequests = new Map<string, Promise<any>>();\n\n constructor(\n private wrappedTransport: BaseTransport,\n private debug?: boolean\n ) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const requestKey = generateRequestKey(endpointPath, data);\n\n // Check if an identical request is already in-flight\n const existingRequest = this.inFlightRequests.get(requestKey);\n if (existingRequest) {\n if (this.debug) {\n console.log(`[DedupeTransport] Deduplicating request to ${endpointPath}`);\n }\n return existingRequest;\n }\n\n // Create new request and store it\n const requestPromise = this.wrappedTransport.post(data, endpointPath)\n .finally(() => {\n // Clean up when request completes (success or failure)\n this.inFlightRequests.delete(requestKey);\n });\n\n this.inFlightRequests.set(requestKey, requestPromise);\n\n if (this.debug) {\n console.log(`[DedupeTransport] New request to ${endpointPath} (${this.inFlightRequests.size} in-flight)`);\n }\n\n return requestPromise;\n }\n}\n","import type { BaseTransport } from '../../core/types';\nimport { SDK_NAME, SDK_VERSION } from '../../version';\nimport { DedupeTransport } from './dedupe';\n\nconst BASE_URL = 'https://dynamic.belocal.dev';\n\nexport interface BaseBrowserTransportConfig {\n headers?: Record<string, string>;\n timeoutMs?: number;\n debug?: boolean;\n}\n\nexport class BaseBrowserTransport implements BaseTransport {\n constructor(private config: BaseBrowserTransportConfig) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const url = `${BASE_URL}${endpointPath}`;\n const controller = new AbortController();\n const timeoutMs = this.config.timeoutMs ?? 10000;\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n if (this.config.debug) {\n console.log(`[Base Browser Transport] POST request to ${url}`, data);\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk': SDK_NAME,\n 'x-sdk-version': SDK_VERSION,\n ...this.config.headers,\n },\n body: JSON.stringify(data),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorMsg = `HTTP ${response.status}: ${response.statusText}`;\n if (this.config.debug) {\n console.error(`[Base Browser Transport] Request failed:`, errorMsg);\n }\n throw new Error(errorMsg);\n }\n\n const result = await response.json();\n if (this.config.debug) {\n console.log(`[Base Browser Transport] Request successful:`, result);\n }\n \n return result;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n}\n\nexport function createBaseBrowserTransport(config: BaseBrowserTransportConfig): BaseTransport {\n const transport = new BaseBrowserTransport(config);\n return new DedupeTransport(transport, config.debug);\n}\n","import type { Transport, BaseTransport } from '../core/types';\nimport { md5 } from 'js-md5';\n\nexport interface MultiTransportConfig {\n baseTransport: BaseTransport;\n debug?: boolean;\n batchWindowMs?: number;\n}\n\ninterface MultiRequest {\n request_id: string;\n texts: string[];\n lang: string;\n source_lang?: string;\n ctx?: Record<string, string>;\n}\n\ninterface MultiRequestItem {\n text: string;\n lang: string;\n sourceLang?: string;\n context?: Record<string, string>;\n resolve: (value: { text: string; status: string }) => void;\n reject: (error: Error) => void;\n}\n\ninterface MultiResponse {\n results: Array<{\n request_id: string;\n data: { texts: string[]; status: string };\n }>;\n}\n\ninterface MultiState {\n currentMulti: MultiRequestItem[];\n multiTimer: ReturnType<typeof setTimeout> | null;\n isRequestInFlight: boolean;\n}\n\nfunction generateRequestId(texts: string[], lang: string, sourceLang?: string, context?: Record<string, string>): string {\n const sortedTexts = [...texts].sort();\n \n let sortedContext: Record<string, string> | null = null;\n if (context && Object.keys(context).length > 0) {\n sortedContext = {};\n const sortedKeys = Object.keys(context).sort();\n for (const key of sortedKeys) {\n sortedContext[key] = context[key];\n }\n }\n \n // Include sourceLang for proper grouping of requests with different source languages\n const data = [sortedTexts, lang, sourceLang || null, sortedContext];\n const json = JSON.stringify(data);\n \n return md5(json);\n}\n\nasync function sendMulti(\n config: MultiTransportConfig,\n items: MultiRequestItem[],\n state: MultiState\n): Promise<void> {\n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Sending multi request with ${items.length} texts`);\n }\n\n try {\n // Group texts by translation parameters\n const groups = new Map<string, MultiRequestItem[]>();\n \n items.forEach(item => {\n const groupKey = JSON.stringify({\n lang: item.lang,\n sourceLang: item.sourceLang || null,\n context: item.context || null\n });\n \n if (!groups.has(groupKey)) {\n groups.set(groupKey, []);\n }\n groups.get(groupKey)!.push(item);\n });\n\n const requestIdToGroupItems = new Map<string, MultiRequestItem[]>();\n const requests: MultiRequest[] = Array.from(groups.entries()).map(([groupKey, groupItems]) => {\n const firstItem = groupItems[0];\n const texts = groupItems.map(item => item.text);\n \n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n \n requestIdToGroupItems.set(requestId, groupItems);\n \n return {\n request_id: requestId,\n texts,\n lang: firstItem.lang,\n source_lang: firstItem.sourceLang,\n ctx: firstItem.context\n };\n });\n\n const multiResponse: MultiResponse = await config.baseTransport.post({ requests }, '/v1/translate/multi');\n \n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Multi response received with ${multiResponse.results.length} groups`);\n }\n\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.request_id, { texts: result.data.texts, status: result.data.status });\n });\n\n requestIdToGroupItems.forEach((groupItems, requestId) => {\n const result = resultMap.get(requestId);\n \n if (!result) {\n if (config.debug) {\n console.error(`[BeLocal Multi Transport] No result found for request_id: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for request_id ${requestId}`);\n if (config.debug) {\n console.error(`[BeLocal Multi Transport]`, error.message);\n }\n groupItems.forEach(item => item.reject(error));\n return;\n }\n\n groupItems.forEach((item, index) => {\n const translatedText = result.texts[index];\n const status = result.status || 'success';\n \n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Success for request_id ${requestId}[${index}]: \"${translatedText}\"`);\n }\n \n item.resolve({ text: translatedText, status });\n });\n });\n\n } catch (error) {\n if (config.debug) {\n console.error(`[BeLocal Multi Transport] Multi request error:`, error);\n }\n \n const errorToReject = error instanceof Error ? error : new Error(String(error));\n items.forEach(item => item.reject(errorToReject));\n } finally {\n // Cleanup handled by base transport\n }\n}\n\nfunction processMulti(config: MultiTransportConfig, state: MultiState): void {\n if (state.currentMulti.length === 0 || state.isRequestInFlight) {\n return;\n }\n\n const itemsToSend = [...state.currentMulti];\n state.currentMulti = [];\n state.multiTimer = null;\n state.isRequestInFlight = true;\n\n sendMulti(config, itemsToSend, state).finally(() => {\n state.isRequestInFlight = false;\n \n if (state.currentMulti.length > 0) {\n const windowMs = config.batchWindowMs ?? 50;\n state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);\n }\n });\n}\n\nexport function createMultiTransport(config: MultiTransportConfig): Transport {\n const windowMs = config.batchWindowMs ?? 50;\n\n const state: MultiState = {\n currentMulti: [],\n multiTimer: null,\n isRequestInFlight: false,\n };\n \n return ({ text, lang, source_lang, ctx }) => {\n return new Promise<{ text: string; status: string }>((resolve, reject) => {\n if (config.debug) {\n const tempRequestId = md5(JSON.stringify([text, lang, source_lang || null, ctx || null]));\n console.log(`[BeLocal Multi Transport] Queuing request ${tempRequestId}: \"${text}\" to ${lang}`);\n }\n\n const requestItem: MultiRequestItem = {\n text,\n lang,\n sourceLang: source_lang,\n context: ctx,\n resolve,\n reject,\n };\n\n state.currentMulti.push(requestItem);\n\n if (state.multiTimer === null && !state.isRequestInFlight) {\n state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);\n }\n });\n };\n}\n\n","import type { Cache } from './types';\n\nexport class LocalCache implements Cache {\n private storage = new Map<string, string>();\n\n get(key: string): string | null {\n return this.storage.get(key) || null;\n }\n\n set(key: string, value: string): void {\n this.storage.set(key, value);\n }\n\n isAvailable(): boolean {\n return true;\n }\n}\n","import type { BelocalEngineOptions, KV, Lang, Transport, BaseTransport } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\n\n/**\n * Base engine implementation shared between browser and Node.js environments.\n */\nexport class BelocalEngine {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n constructor(options: BelocalEngineOptions, baseTransportFactory: (opts: any) => BaseTransport) {\n const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n this.cache = new LocalCache();\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n const baseTransport = baseTransportFactory({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n this.transport = createMultiTransport({\n baseTransport,\n debug: this.debug,\n batchWindowMs\n });\n \n if (this.debug) {\n console.log('[BeLocal Engine] Multi transport created with config:', {\n baseUrl: 'https://dynamic.belocal.dev',\n timeoutMs,\n batchWindowMs\n });\n }\n }\n\n /**\n * Translates a single text string to the target language.\n * \n * Uses in-memory cache to avoid redundant API calls. Results are automatically cached\n * for subsequent requests with the same parameters.\n * \n * @param text - The text to translate\n * @param lang - Target language code (e.g., 'es', 'fr', 'ru')\n * @param source_lang - Optional source language code. If not provided, auto-detection is used\n * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context), `cache_type` ('managed')\n * @returns Promise resolving to the translated text\n * @throws {Error} If the translation request fails (network error, API error, timeout)\n * \n * @example\n * ```typescript\n * // Simple translation\n * const result = await engine.translate('Hello world', 'es');\n * \n * // With source language\n * const result = await engine.translate('Hello world', 'es', 'en');\n * \n * // With context (user_ctx) - descriptive context helps improve translation quality\n * const result = await engine.translate('Hello world', 'es', undefined, { \n * user_ctx: 'greeting message on the homepage' \n * });\n * \n * // With cache_type\n * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'managed' });\n * \n * // With source language and context\n * const result = await engine.translate('Hello world', 'es', 'en', { \n * user_ctx: 'greeting message on the homepage' \n * });\n * ```\n */\n async translate(text: string, lang: Lang, source_lang?: string, ctx?: KV): Promise<string> {\n const results = await this.translateMany([text], lang, source_lang, ctx);\n return results[0];\n }\n\n /**\n * Translates multiple text strings to the target language in a single batch.\n * \n * This method is more efficient than calling `translate()` multiple times as it:\n * - Batches requests together to reduce API calls\n * - Checks cache for each text individually\n * - Only requests translations for cache misses\n * - Maintains the order of input texts in the result array\n * \n * @param texts - Array of texts to translate\n * @param lang - Target language code (e.g., 'es', 'fr', 'ru')\n * @param source_lang - Optional source language code. If not provided, auto-detection is used\n * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)\n * @returns Promise resolving to an array of translated texts in the same order as input\n * @throws {Error} If the translation request fails (network error, API error, timeout)\n * \n * @example\n * ```typescript\n * // Translate multiple texts\n * const results = await engine.translateMany(['Hello', 'World', 'Test'], 'es');\n * // Returns: ['Hola', 'Mundo', 'Prueba']\n * \n * // With source language\n * const results = await engine.translateMany(['Hello', 'World'], 'fr', 'en');\n * \n * // With context (user_ctx) - descriptive context helps improve translation quality\n * const results = await engine.translateMany(\n * ['Hello', 'World'], \n * 'es', \n * undefined, \n * { user_ctx: 'greeting message on the homepage' }\n * );\n * \n * // With cache_type\n * const results = await engine.translateMany(\n * ['Hello', 'World'], \n * 'es', \n * undefined, \n * { cache_type: 'managed' }\n * );\n * \n * // Empty array returns empty array\n * const results = await engine.translateMany([], 'es');\n * // Returns: []\n * ```\n */\n async translateMany(texts: string[], lang: Lang, source_lang?: string, ctx?: KV): Promise<string[]> {\n const results: (string | null)[] = new Array(texts.length);\n const cacheMisses: Array<{ index: number; text: string }> = [];\n\n for (let i = 0; i < texts.length; i++) {\n const text = texts[i];\n const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);\n \n const cachedResult = this.cache.get(cacheKey);\n if (cachedResult) {\n results[i] = cachedResult;\n if (this.debug) {\n console.log('[BeLocal Engine] Translation from cache:', text);\n }\n continue;\n }\n \n results[i] = null;\n cacheMisses.push({ index: i, text });\n }\n\n if (cacheMisses.length > 0) {\n const translations = await Promise.all(\n cacheMisses.map(async ({ index, text }) => {\n const result = await this.transport({ text, lang, source_lang, ctx });\n \n if (result.status !== 'error') {\n const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);\n this.cache.set(cacheKey, result.text);\n if (this.debug) {\n console.log('[BeLocal Engine] Translation from API, cached:', text);\n }\n } else {\n if (this.debug) {\n console.log('[BeLocal Engine] Translation from API (not cached due to error status):', text);\n }\n }\n \n return { index, translation: result.text };\n })\n );\n\n translations.forEach(({ index, translation }) => {\n results[index] = translation;\n });\n }\n\n return results as string[];\n }\n\n /**\n * Shortcut method for translation with simplified API.\n * \n * This is a convenience method that wraps `translate()`. When `context` is provided as a string,\n * it is automatically wrapped in `{user_ctx: context}` object.\n * \n * @param text - The text to translate\n * @param lang - Target language code (e.g., 'es', 'fr', 'ru')\n * @param source_lang - Optional source language code\n * @param context - Optional descriptive context string in English (will be wrapped as {user_ctx: context})\n * @returns Promise resolving to the translated text\n * @throws {Error} If the translation request fails (network error, API error, timeout)\n * \n * @example\n * ```typescript\n * // Simple translation\n * const result = await engine.t('Hello world', 'es');\n * \n * // With source language\n * const result = await engine.t('Hello world', 'fr', 'en');\n * \n * // With context string (automatically wrapped as {user_ctx: 'greeting message on the homepage'})\n * const result = await engine.t('Hello world', 'es', undefined, 'greeting message on the homepage');\n * \n * // With source language and context\n * const result = await engine.t('Hello world', 'es', 'en', 'greeting message on the homepage');\n * ```\n */\n async t(text: string, lang: Lang, source_lang?: string, context?: string): Promise<string> {\n if (context) {\n return this.translate(text, lang, source_lang, {user_ctx: context});\n }\n return this.translate(text, lang, source_lang);\n }\n\n private generateCacheKey(text: string, lang: Lang, source_lang?: string, ctx?: KV): string {\n const sortedCtx = ctx ? Object.keys(ctx)\n .sort()\n .reduce((acc, key) => {\n acc[key] = ctx[key];\n return acc;\n }, {} as KV) : null;\n\n const data = {\n text,\n lang,\n source_lang: source_lang || null,\n ctx: sortedCtx\n };\n return md5(JSON.stringify(data));\n }\n}\n","import type { BelocalEngineOptions } from '../types';\nimport { createBaseBrowserTransport } from '../../transports/base/browser';\nimport { BelocalEngine as BaseEngine } from './engine';\n\n/**\n * BeLocal translation engine for browser environments.\n * \n * Provides on-demand translation with automatic request batching, caching, and deduplication.\n * Optimized for browser environments using the Fetch API.\n * \n * @example\n * ```typescript\n * const engine = new BelocalEngine({\n * apiKey: 'your-api-key',\n * debug: true\n * });\n * \n * const translated = await engine.translate('Hello world', 'es');\n * ```\n */\nexport class BelocalEngine extends BaseEngine {\n /**\n * Creates a new BelocalEngine instance for browser environments.\n * \n * @param options - Configuration options for the engine\n * @throws {Error} If apiKey is not provided or invalid\n * \n * @example\n * ```typescript\n * const engine = new BelocalEngine({\n * apiKey: 'your-api-key',\n * batchWindowMs: 100,\n * timeoutMs: 10000,\n * debug: false\n * });\n * ```\n */\n constructor(options: BelocalEngineOptions) {\n super(options, createBaseBrowserTransport);\n }\n}\n\n// Re-export types and transports\nexport type { BelocalEngineOptions, Lang, KV, BaseTransport } from '../types';\nexport { createMultiTransport } from '../../transports/multi';\nexport { BaseBrowserTransport, createBaseBrowserTransport } from '../../transports/base';\n"]}
1
+ {"version":3,"sources":["../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/browser.ts","../src/transports/multi.ts","../src/cache/local.ts","../src/core/engine/engine.ts","../src/core/types.ts","../src/core/engine/browser.ts"],"names":["SDK_VERSION","generateRequestKey","endpointPath","data","normalize","value","sorted","key","normalizedData","DedupeTransport","wrappedTransport","debug","requestKey","existingRequest","requestPromise","BASE_URL","BaseBrowserTransport","config","url","controller","timeoutMs","timeoutId","response","errorMsg","result","createBaseBrowserTransport","transport","generateRequestId","texts","lang","sourceLang","context","sortedTexts","sortedContext","a","b","md5","sendMulti","items","state","groups","item","groupKey","requestIdToGroupItems","requests","groupItems","firstItem","requestId","multiResponse","resultMap","error","index","translatedText","status","errorToReject","processMulti","itemsToSend","windowMs","createMultiTransport","text","source_lang","ctx","resolve","reject","tempRequestId","requestItem","LocalCache","BelocalEngine","options","baseTransportFactory","apiKey","batchWindowMs","authHeaders","baseTransport","results","cacheMisses","i","cacheKey","cachedResult","translation","managed","sortedCtx","USER_TYPE_PRODUCT","USER_TYPE_CHAT"],"mappings":"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","file":"browser.cjs","sourcesContent":["declare const __SDK_VERSION__: string;\nexport const SDK_VERSION = typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : 'undefined';\nexport const SDK_NAME = 'js';\n","import type { BaseTransport } from '../../core/types';\n\nfunction generateRequestKey(endpointPath: string, data: any): string {\n const normalize = (value: any): any => {\n if (value === null || value === undefined) {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map(normalize);\n }\n if (typeof value === 'object') {\n const sorted: Record<string, any> = {};\n for (const key of Object.keys(value).sort()) {\n sorted[key] = normalize(value[key]);\n }\n return sorted;\n }\n return value;\n };\n\n const normalizedData = JSON.stringify(normalize(data));\n return `${endpointPath}:${normalizedData}`;\n}\n\n/** Deduplicates identical in-flight requests so they share a single HTTP call. */\nexport class DedupeTransport implements BaseTransport {\n private inFlightRequests = new Map<string, Promise<any>>();\n\n constructor(\n private wrappedTransport: BaseTransport,\n private debug?: boolean\n ) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const requestKey = generateRequestKey(endpointPath, data);\n\n const existingRequest = this.inFlightRequests.get(requestKey);\n if (existingRequest) {\n if (this.debug) {\n console.log(`[DedupeTransport] Deduplicating request to ${endpointPath}`);\n }\n return existingRequest;\n }\n\n const requestPromise = this.wrappedTransport.post(data, endpointPath)\n .finally(() => {\n this.inFlightRequests.delete(requestKey);\n });\n\n this.inFlightRequests.set(requestKey, requestPromise);\n\n if (this.debug) {\n console.log(`[DedupeTransport] New request to ${endpointPath} (${this.inFlightRequests.size} in-flight)`);\n }\n\n return requestPromise;\n }\n}\n","import type { BaseTransport } from '../../core/types';\nimport { SDK_NAME, SDK_VERSION } from '../../version';\nimport { DedupeTransport } from './dedupe';\n\nconst BASE_URL = 'https://dynamic.belocal.dev';\n\nexport interface BaseBrowserTransportConfig {\n headers?: Record<string, string>;\n timeoutMs?: number;\n debug?: boolean;\n}\n\nexport class BaseBrowserTransport implements BaseTransport {\n constructor(private config: BaseBrowserTransportConfig) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const url = `${BASE_URL}${endpointPath}`;\n const controller = new AbortController();\n const timeoutMs = this.config.timeoutMs ?? 10000;\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n if (this.config.debug) {\n console.log(`[Base Browser Transport] POST request to ${url}`, data);\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk': SDK_NAME,\n 'x-sdk-version': SDK_VERSION,\n ...this.config.headers,\n },\n body: JSON.stringify(data),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorMsg = `HTTP ${response.status}: ${response.statusText}`;\n if (this.config.debug) {\n console.error(`[Base Browser Transport] Request failed:`, errorMsg);\n }\n throw new Error(errorMsg);\n }\n\n const result = await response.json();\n if (this.config.debug) {\n console.log(`[Base Browser Transport] Request successful:`, result);\n }\n \n return result;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n}\n\nexport function createBaseBrowserTransport(config: BaseBrowserTransportConfig): BaseTransport {\n const transport = new BaseBrowserTransport(config);\n return new DedupeTransport(transport, config.debug);\n}\n","import type { Transport, BaseTransport, KV } from '../core/types';\nimport { md5 } from 'js-md5';\n\nexport interface MultiTransportConfig {\n baseTransport: BaseTransport;\n debug?: boolean;\n batchWindowMs?: number;\n}\n\ninterface MultiRequest {\n request_id: string;\n texts: string[];\n lang: string;\n source_lang?: string;\n ctx?: KV;\n}\n\ninterface MultiRequestItem {\n text: string;\n lang: string;\n sourceLang?: string;\n context?: KV;\n resolve: (value: { text: string; status: string }) => void;\n reject: (error: Error) => void;\n}\n\ninterface MultiResponse {\n results: Array<{\n request_id: string;\n data: { texts: string[]; status: string };\n }>;\n}\n\ninterface MultiState {\n currentMulti: MultiRequestItem[];\n multiTimer: ReturnType<typeof setTimeout> | null;\n isRequestInFlight: boolean;\n}\n\nfunction generateRequestId(texts: string[], lang: string, sourceLang?: string, context?: KV): string {\n const sortedTexts = [...texts].sort();\n\n const sortedContext = context && Object.keys(context).length > 0\n ? Object.fromEntries(Object.entries(context).sort(([a], [b]) => a.localeCompare(b)))\n : null;\n\n const data = [sortedTexts, lang, sourceLang || null, sortedContext];\n return md5(JSON.stringify(data));\n}\n\nasync function sendMulti(\n config: MultiTransportConfig,\n items: MultiRequestItem[],\n state: MultiState\n): Promise<void> {\n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Sending multi request with ${items.length} texts`);\n }\n\n try {\n const groups = new Map<string, MultiRequestItem[]>();\n\n items.forEach(item => {\n const groupKey = JSON.stringify({\n lang: item.lang,\n sourceLang: item.sourceLang || null,\n context: item.context || null\n });\n\n if (!groups.has(groupKey)) {\n groups.set(groupKey, []);\n }\n groups.get(groupKey)!.push(item);\n });\n\n const requestIdToGroupItems = new Map<string, MultiRequestItem[]>();\n const requests: MultiRequest[] = Array.from(groups.entries()).map(([groupKey, groupItems]) => {\n const firstItem = groupItems[0];\n const texts = groupItems.map(item => item.text);\n\n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n\n requestIdToGroupItems.set(requestId, groupItems);\n\n return {\n request_id: requestId,\n texts,\n lang: firstItem.lang,\n source_lang: firstItem.sourceLang,\n ctx: firstItem.context\n };\n });\n\n const multiResponse: MultiResponse = await config.baseTransport.post({ requests }, '/v1/translate/multi');\n\n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Multi response received with ${multiResponse.results.length} groups`);\n }\n\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.request_id, { texts: result.data.texts, status: result.data.status });\n });\n\n requestIdToGroupItems.forEach((groupItems, requestId) => {\n const result = resultMap.get(requestId);\n\n if (!result) {\n if (config.debug) {\n console.error(`[BeLocal Multi Transport] No result found for request_id: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for request_id ${requestId}`);\n if (config.debug) {\n console.error(`[BeLocal Multi Transport]`, error.message);\n }\n groupItems.forEach(item => item.reject(error));\n return;\n }\n\n groupItems.forEach((item, index) => {\n const translatedText = result.texts[index];\n const status = result.status || 'success';\n\n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Success for request_id ${requestId}[${index}]: \"${translatedText}\"`);\n }\n\n item.resolve({ text: translatedText, status });\n });\n });\n } catch (error) {\n if (config.debug) {\n console.error(`[BeLocal Multi Transport] Multi request error:`, error);\n }\n\n const errorToReject = error instanceof Error ? error : new Error(String(error));\n items.forEach(item => item.reject(errorToReject));\n }\n}\n\nfunction processMulti(config: MultiTransportConfig, state: MultiState): void {\n if (state.currentMulti.length === 0 || state.isRequestInFlight) {\n return;\n }\n\n const itemsToSend = [...state.currentMulti];\n state.currentMulti = [];\n state.multiTimer = null;\n state.isRequestInFlight = true;\n\n sendMulti(config, itemsToSend, state).finally(() => {\n state.isRequestInFlight = false;\n\n if (state.currentMulti.length > 0) {\n const windowMs = config.batchWindowMs ?? 50;\n state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);\n }\n });\n}\n\nexport function createMultiTransport(config: MultiTransportConfig): Transport {\n const windowMs = config.batchWindowMs ?? 50;\n\n const state: MultiState = {\n currentMulti: [],\n multiTimer: null,\n isRequestInFlight: false,\n };\n\n return ({ text, lang, source_lang, ctx }) => {\n return new Promise<{ text: string; status: string }>((resolve, reject) => {\n if (config.debug) {\n const tempRequestId = md5(JSON.stringify([text, lang, source_lang || null, ctx || null]));\n console.log(`[BeLocal Multi Transport] Queuing request ${tempRequestId}: \"${text}\" to ${lang}`);\n }\n\n const requestItem: MultiRequestItem = {\n text,\n lang,\n sourceLang: source_lang,\n context: ctx,\n resolve,\n reject,\n };\n\n state.currentMulti.push(requestItem);\n\n if (state.multiTimer === null && !state.isRequestInFlight) {\n state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);\n }\n });\n };\n}\n","import type { Cache } from './types';\n\nexport class LocalCache implements Cache {\n private storage = new Map<string, string>();\n\n get(key: string): string | null {\n return this.storage.get(key) || null;\n }\n\n set(key: string, value: string): void {\n this.storage.set(key, value);\n }\n}\n","import type { BelocalEngineOptions, KV, Lang, Transport, BaseTransport } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\n\nexport class BelocalEngine {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n constructor(options: BelocalEngineOptions, baseTransportFactory: (opts: any) => BaseTransport) {\n const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n this.cache = new LocalCache();\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n const baseTransport = baseTransportFactory({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n this.transport = createMultiTransport({\n baseTransport,\n debug: this.debug,\n batchWindowMs\n });\n\n if (this.debug) {\n console.log('[BeLocal Engine] Multi transport created with config:', {\n baseUrl: 'https://dynamic.belocal.dev',\n timeoutMs,\n batchWindowMs\n });\n }\n }\n\n /** Translates a single text string to the target language. */\n async translate(text: string, lang: Lang, source_lang?: string, ctx?: KV): Promise<string> {\n const results = await this.translateMany([text], lang, source_lang, ctx);\n return results[0];\n }\n\n /** Translates multiple text strings to the target language in a single batch. */\n async translateMany(texts: string[], lang: Lang, source_lang?: string, ctx?: KV): Promise<string[]> {\n const results: (string | null)[] = new Array(texts.length);\n const cacheMisses: Array<{ index: number; text: string }> = [];\n\n for (let i = 0; i < texts.length; i++) {\n const text = texts[i];\n const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);\n\n const cachedResult = this.cache.get(cacheKey);\n if (cachedResult) {\n results[i] = cachedResult;\n if (this.debug) {\n console.log('[BeLocal Engine] Translation from cache:', text);\n }\n continue;\n }\n\n results[i] = null;\n cacheMisses.push({ index: i, text });\n }\n\n if (cacheMisses.length > 0) {\n const translations = await Promise.all(\n cacheMisses.map(async ({ index, text }) => {\n const result = await this.transport({ text, lang, source_lang, ctx });\n\n if (result.status !== 'error') {\n const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);\n this.cache.set(cacheKey, result.text);\n if (this.debug) {\n console.log('[BeLocal Engine] Translation from API, cached:', text);\n }\n } else {\n if (this.debug) {\n console.log('[BeLocal Engine] Translation from API (not cached due to error status):', text);\n }\n }\n\n return { index, translation: result.text };\n })\n );\n\n translations.forEach(({ index, translation }) => {\n results[index] = translation;\n });\n }\n\n return results as string[];\n }\n\n /** Shortcut for translate() */\n async t(text: string, lang: Lang, source_lang?: string, context?: string, managed = false): Promise<string> {\n const ctx: KV | undefined = (context || managed)\n ? { ...(context ? { user_ctx: context } : {}), ...(managed ? { cache_type: 'managed' } : {}) }\n : undefined;\n return this.translate(text, lang, source_lang, ctx);\n }\n\n private generateCacheKey(text: string, lang: Lang, source_lang?: string, ctx?: KV): string {\n const sortedCtx = ctx\n ? Object.fromEntries(Object.entries(ctx).sort(([a], [b]) => a.localeCompare(b)))\n : null;\n\n const data = {\n text,\n lang,\n source_lang: source_lang || null,\n ctx: sortedCtx\n };\n return md5(JSON.stringify(data));\n }\n}\n","export type Lang = string;\n\nexport type TranslationCtx = {\n user_type?: string;\n user_ctx?: string;\n cache_type?: string;\n entity_key?: string;\n entity_id?: string;\n}\n\nexport const USER_TYPE_PRODUCT = 'product';\nexport const USER_TYPE_CHAT = 'chat';\n\nexport type KV = TranslationCtx;\n\nexport interface BaseTransport {\n post(data: any, endpointPath: string): Promise<any>;\n}\n\nexport type Transport = (params: { text: string; lang: Lang; source_lang?: string; ctx?: KV }) => Promise<{ text: string; status: string }>;\n\nexport interface BelocalEngineOptions {\n apiKey: string;\n batchWindowMs?: number;\n timeoutMs?: number;\n debug?: boolean;\n}\n","import type { BelocalEngineOptions } from '../types';\nimport { createBaseBrowserTransport } from '../../transports/base/browser';\nimport { BelocalEngine as BaseEngine } from './engine';\n\nexport class BelocalEngine extends BaseEngine {\n constructor(options: BelocalEngineOptions) {\n super(options, createBaseBrowserTransport);\n }\n}\n\nexport type { BelocalEngineOptions, Lang, KV, BaseTransport, TranslationCtx } from '../types';\nexport { USER_TYPE_PRODUCT, USER_TYPE_CHAT } from '../types';\nexport { createMultiTransport } from '../../transports/multi';\nexport { BaseBrowserTransport, createBaseBrowserTransport } from '../../transports/base';\n"]}
package/dist/browser.d.ts CHANGED
@@ -1,28 +1,17 @@
1
- /**
2
- * Language code for translation (e.g., 'en', 'es', 'fr', 'ru').
3
- * ISO 639-1 language codes are recommended.
4
- */
5
1
  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
- */
14
- type KV = Record<string, string>;
15
- /**
16
- * Base transport interface for HTTP communication.
17
- * @internal
18
- */
2
+ type TranslationCtx = {
3
+ user_type?: string;
4
+ user_ctx?: string;
5
+ cache_type?: string;
6
+ entity_key?: string;
7
+ entity_id?: string;
8
+ };
9
+ declare const USER_TYPE_PRODUCT = "product";
10
+ declare const USER_TYPE_CHAT = "chat";
11
+ type KV = TranslationCtx;
19
12
  interface BaseTransport {
20
13
  post(data: any, endpointPath: string): Promise<any>;
21
14
  }
22
- /**
23
- * Transport function type for translation requests.
24
- * @internal
25
- */
26
15
  type Transport = (params: {
27
16
  text: string;
28
17
  lang: Lang;
@@ -32,140 +21,24 @@ type Transport = (params: {
32
21
  text: string;
33
22
  status: string;
34
23
  }>;
35
- /**
36
- * Configuration options for BelocalEngine.
37
- */
38
24
  interface BelocalEngineOptions {
39
- /** Required: Your BeLocal API key for authentication */
40
25
  apiKey: string;
41
- /** Optional: Batch window in milliseconds for grouping multiple translation requests. Defaults to 50ms */
42
26
  batchWindowMs?: number;
43
- /** Optional: Request timeout in milliseconds. Defaults to 10000ms (10 seconds) */
44
27
  timeoutMs?: number;
45
- /** Optional: Enable debug logging to console. Defaults to false */
46
28
  debug?: boolean;
47
29
  }
48
30
 
49
- /**
50
- * Base engine implementation shared between browser and Node.js environments.
51
- */
52
31
  declare class BelocalEngine$1 {
53
32
  private transport;
54
33
  private debug;
55
34
  private cache;
56
35
  constructor(options: BelocalEngineOptions, baseTransportFactory: (opts: any) => BaseTransport);
57
- /**
58
- * Translates a single text string to the target language.
59
- *
60
- * Uses in-memory cache to avoid redundant API calls. Results are automatically cached
61
- * for subsequent requests with the same parameters.
62
- *
63
- * @param text - The text to translate
64
- * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
65
- * @param source_lang - Optional source language code. If not provided, auto-detection is used
66
- * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context), `cache_type` ('managed')
67
- * @returns Promise resolving to the translated text
68
- * @throws {Error} If the translation request fails (network error, API error, timeout)
69
- *
70
- * @example
71
- * ```typescript
72
- * // Simple translation
73
- * const result = await engine.translate('Hello world', 'es');
74
- *
75
- * // With source language
76
- * const result = await engine.translate('Hello world', 'es', 'en');
77
- *
78
- * // With context (user_ctx) - descriptive context helps improve translation quality
79
- * const result = await engine.translate('Hello world', 'es', undefined, {
80
- * user_ctx: 'greeting message on the homepage'
81
- * });
82
- *
83
- * // With cache_type
84
- * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'managed' });
85
- *
86
- * // With source language and context
87
- * const result = await engine.translate('Hello world', 'es', 'en', {
88
- * user_ctx: 'greeting message on the homepage'
89
- * });
90
- * ```
91
- */
36
+ /** Translates a single text string to the target language. */
92
37
  translate(text: string, lang: Lang, source_lang?: string, ctx?: KV): Promise<string>;
93
- /**
94
- * Translates multiple text strings to the target language in a single batch.
95
- *
96
- * This method is more efficient than calling `translate()` multiple times as it:
97
- * - Batches requests together to reduce API calls
98
- * - Checks cache for each text individually
99
- * - Only requests translations for cache misses
100
- * - Maintains the order of input texts in the result array
101
- *
102
- * @param texts - Array of texts to translate
103
- * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
104
- * @param source_lang - Optional source language code. If not provided, auto-detection is used
105
- * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
106
- * @returns Promise resolving to an array of translated texts in the same order as input
107
- * @throws {Error} If the translation request fails (network error, API error, timeout)
108
- *
109
- * @example
110
- * ```typescript
111
- * // Translate multiple texts
112
- * const results = await engine.translateMany(['Hello', 'World', 'Test'], 'es');
113
- * // Returns: ['Hola', 'Mundo', 'Prueba']
114
- *
115
- * // With source language
116
- * const results = await engine.translateMany(['Hello', 'World'], 'fr', 'en');
117
- *
118
- * // With context (user_ctx) - descriptive context helps improve translation quality
119
- * const results = await engine.translateMany(
120
- * ['Hello', 'World'],
121
- * 'es',
122
- * undefined,
123
- * { user_ctx: 'greeting message on the homepage' }
124
- * );
125
- *
126
- * // With cache_type
127
- * const results = await engine.translateMany(
128
- * ['Hello', 'World'],
129
- * 'es',
130
- * undefined,
131
- * { cache_type: 'managed' }
132
- * );
133
- *
134
- * // Empty array returns empty array
135
- * const results = await engine.translateMany([], 'es');
136
- * // Returns: []
137
- * ```
138
- */
38
+ /** Translates multiple text strings to the target language in a single batch. */
139
39
  translateMany(texts: string[], lang: Lang, source_lang?: string, ctx?: KV): Promise<string[]>;
140
- /**
141
- * Shortcut method for translation with simplified API.
142
- *
143
- * This is a convenience method that wraps `translate()`. When `context` is provided as a string,
144
- * it is automatically wrapped in `{user_ctx: context}` object.
145
- *
146
- * @param text - The text to translate
147
- * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
148
- * @param source_lang - Optional source language code
149
- * @param context - Optional descriptive context string in English (will be wrapped as {user_ctx: context})
150
- * @returns Promise resolving to the translated text
151
- * @throws {Error} If the translation request fails (network error, API error, timeout)
152
- *
153
- * @example
154
- * ```typescript
155
- * // Simple translation
156
- * const result = await engine.t('Hello world', 'es');
157
- *
158
- * // With source language
159
- * const result = await engine.t('Hello world', 'fr', 'en');
160
- *
161
- * // With context string (automatically wrapped as {user_ctx: 'greeting message on the homepage'})
162
- * const result = await engine.t('Hello world', 'es', undefined, 'greeting message on the homepage');
163
- *
164
- * // With source language and context
165
- * const result = await engine.t('Hello world', 'es', 'en', 'greeting message on the homepage');
166
- * ```
167
- */
168
- t(text: string, lang: Lang, source_lang?: string, context?: string): Promise<string>;
40
+ /** Shortcut for translate() */
41
+ t(text: string, lang: Lang, source_lang?: string, context?: string, managed?: boolean): Promise<string>;
169
42
  private generateCacheKey;
170
43
  }
171
44
 
@@ -188,40 +61,8 @@ declare class BaseBrowserTransport implements BaseTransport {
188
61
  }
189
62
  declare function createBaseBrowserTransport(config: BaseBrowserTransportConfig): BaseTransport;
190
63
 
191
- /**
192
- * BeLocal translation engine for browser environments.
193
- *
194
- * Provides on-demand translation with automatic request batching, caching, and deduplication.
195
- * Optimized for browser environments using the Fetch API.
196
- *
197
- * @example
198
- * ```typescript
199
- * const engine = new BelocalEngine({
200
- * apiKey: 'your-api-key',
201
- * debug: true
202
- * });
203
- *
204
- * const translated = await engine.translate('Hello world', 'es');
205
- * ```
206
- */
207
64
  declare class BelocalEngine extends BelocalEngine$1 {
208
- /**
209
- * Creates a new BelocalEngine instance for browser environments.
210
- *
211
- * @param options - Configuration options for the engine
212
- * @throws {Error} If apiKey is not provided or invalid
213
- *
214
- * @example
215
- * ```typescript
216
- * const engine = new BelocalEngine({
217
- * apiKey: 'your-api-key',
218
- * batchWindowMs: 100,
219
- * timeoutMs: 10000,
220
- * debug: false
221
- * });
222
- * ```
223
- */
224
65
  constructor(options: BelocalEngineOptions);
225
66
  }
226
67
 
227
- export { BaseBrowserTransport, type BaseTransport, BelocalEngine, type BelocalEngineOptions, type KV, type Lang, createBaseBrowserTransport, createMultiTransport };
68
+ export { BaseBrowserTransport, type BaseTransport, BelocalEngine, type BelocalEngineOptions, type KV, type Lang, type TranslationCtx, USER_TYPE_CHAT, USER_TYPE_PRODUCT, createBaseBrowserTransport, createMultiTransport };
package/dist/browser.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import {md5}from'js-md5';var B=(()=>{try{return "1.0.0"}catch{return "undefined"}})(),b="js";function E(a,t){let s=r=>{if(r==null)return r;if(Array.isArray(r))return r.map(s);if(typeof r=="object"){let i={};for(let c of Object.keys(r).sort())i[c]=s(r[c]);return i}return r},e=JSON.stringify(s(t));return `${a}:${e}`}var h=class{constructor(t,s){this.wrappedTransport=t;this.debug=s;this.inFlightRequests=new Map;}async post(t,s){let e=E(s,t),r=this.inFlightRequests.get(e);if(r)return this.debug&&console.log(`[DedupeTransport] Deduplicating request to ${s}`),r;let i=this.wrappedTransport.post(t,s).finally(()=>{this.inFlightRequests.delete(e);});return this.inFlightRequests.set(e,i),this.debug&&console.log(`[DedupeTransport] New request to ${s} (${this.inFlightRequests.size} in-flight)`),i}};var S="https://dynamic.belocal.dev",f=class{constructor(t){this.config=t;}async post(t,s){let e=`${S}${s}`,r=new AbortController,i=this.config.timeoutMs??1e4,c=setTimeout(()=>r.abort(),i);this.config.debug&&console.log(`[Base Browser Transport] POST request to ${e}`,t);try{let u=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","x-sdk":b,"x-sdk-version":B,...this.config.headers},body:JSON.stringify(t),signal:r.signal});if(!u.ok){let o=`HTTP ${u.status}: ${u.statusText}`;throw this.config.debug&&console.error("[Base Browser Transport] Request failed:",o),new Error(o)}let n=await u.json();return this.config.debug&&console.log("[Base Browser Transport] Request successful:",n),n}finally{c&&clearTimeout(c);}}};function m(a){let t=new f(a);return new h(t,a.debug)}function _(a,t,s,e){let r=[...a].sort(),i=null;if(e&&Object.keys(e).length>0){i={};let n=Object.keys(e).sort();for(let o of n)i[o]=e[o];}let u=JSON.stringify([r,t,s||null,i]);return md5(u)}async function K(a,t,s){a.debug&&console.log(`[BeLocal Multi Transport] Sending multi request with ${t.length} texts`);try{let e=new Map;t.forEach(n=>{let o=JSON.stringify({lang:n.lang,sourceLang:n.sourceLang||null,context:n.context||null});e.has(o)||e.set(o,[]),e.get(o).push(n);});let r=new Map,i=Array.from(e.entries()).map(([n,o])=>{let l=o[0],g=o.map(d=>d.text),p=_(g,l.lang,l.sourceLang,l.context);return r.set(p,o),{request_id:p,texts:g,lang:l.lang,source_lang:l.sourceLang,ctx:l.context}}),c=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${c.results.length} groups`);let u=new Map;c.results.forEach(n=>{u.set(n.request_id,{texts:n.data.texts,status:n.data.status});}),r.forEach((n,o)=>{let l=u.get(o);if(!l){a.debug&&console.error(`[BeLocal Multi Transport] No result found for request_id: ${o}`),n.forEach(g=>{g.reject(new Error(`No result found for request ${o}`));});return}if(l.texts.length!==n.length){let g=new Error(`Mismatch: expected ${n.length} texts, got ${l.texts.length} for request_id ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",g.message),n.forEach(p=>p.reject(g));return}n.forEach((g,p)=>{let d=l.texts[p],q=l.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for request_id ${o}[${p}]: "${d}"`),g.resolve({text:d,status:q});});});}catch(e){a.debug&&console.error("[BeLocal Multi Transport] Multi request error:",e);let r=e instanceof Error?e:new Error(String(e));t.forEach(i=>i.reject(r));}finally{}}function x(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let s=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,K(a,s).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let e=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>x(a,t),e);}});}function M(a){let t=a.batchWindowMs??50,s={currentMulti:[],multiTimer:null,isRequestInFlight:false};return ({text:e,lang:r,source_lang:i,ctx:c})=>new Promise((u,n)=>{if(a.debug){let l=md5(JSON.stringify([e,r,i||null,c||null]));console.log(`[BeLocal Multi Transport] Queuing request ${l}: "${e}" to ${r}`);}let o={text:e,lang:r,sourceLang:i,context:c,resolve:u,reject:n};s.currentMulti.push(o),s.multiTimer===null&&!s.isRequestInFlight&&(s.multiTimer=setTimeout(()=>x(a,s),t));})}var y=class{constructor(){this.storage=new Map;}get(t){return this.storage.get(t)||null}set(t,s){this.storage.set(t,s);}isAvailable(){return true}};var T=class{constructor(t,s){let{apiKey:e,batchWindowMs:r=50,timeoutMs:i=1e4,debug:c=false}=t;this.debug=c,this.cache=new y;let u={Authorization:`Bearer ${e}`},n=s({headers:u,timeoutMs:i,debug:this.debug});this.transport=M({baseTransport:n,debug:this.debug,batchWindowMs:r}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:i,batchWindowMs:r});}async translate(t,s,e,r){return (await this.translateMany([t],s,e,r))[0]}async translateMany(t,s,e,r){let i=new Array(t.length),c=[];for(let u=0;u<t.length;u++){let n=t[u],o=this.generateCacheKey(n,s,e,r),l=this.cache.get(o);if(l){i[u]=l,this.debug&&console.log("[BeLocal Engine] Translation from cache:",n);continue}i[u]=null,c.push({index:u,text:n});}return c.length>0&&(await Promise.all(c.map(async({index:n,text:o})=>{let l=await this.transport({text:o,lang:s,source_lang:e,ctx:r});if(l.status!=="error"){let g=this.generateCacheKey(o,s,e,r);this.cache.set(g,l.text),this.debug&&console.log("[BeLocal Engine] Translation from API, cached:",o);}else this.debug&&console.log("[BeLocal Engine] Translation from API (not cached due to error status):",o);return {index:n,translation:l.text}}))).forEach(({index:n,translation:o})=>{i[n]=o;}),i}async t(t,s,e,r){return r?this.translate(t,s,e,{user_ctx:r}):this.translate(t,s,e)}generateCacheKey(t,s,e,r){let i=r?Object.keys(r).sort().reduce((u,n)=>(u[n]=r[n],u),{}):null;return md5(JSON.stringify({text:t,lang:s,source_lang:e||null,ctx:i}))}};var R=class extends T{constructor(t){super(t,m);}};
2
- export{f as BaseBrowserTransport,R as BelocalEngine,m as createBaseBrowserTransport,M as createMultiTransport};//# sourceMappingURL=browser.mjs.map
1
+ import {md5}from'js-md5';var x="1.0.1";function _(i,t){let s=e=>{if(e==null)return e;if(Array.isArray(e))return e.map(s);if(typeof e=="object"){let o={};for(let c of Object.keys(e).sort())o[c]=s(e[c]);return o}return e},r=JSON.stringify(s(t));return `${i}:${r}`}var m=class{constructor(t,s){this.wrappedTransport=t;this.debug=s;this.inFlightRequests=new Map;}async post(t,s){let r=_(s,t),e=this.inFlightRequests.get(r);if(e)return this.debug&&console.log(`[DedupeTransport] Deduplicating request to ${s}`),e;let o=this.wrappedTransport.post(t,s).finally(()=>{this.inFlightRequests.delete(r);});return this.inFlightRequests.set(r,o),this.debug&&console.log(`[DedupeTransport] New request to ${s} (${this.inFlightRequests.size} in-flight)`),o}};var q="https://dynamic.belocal.dev",f=class{constructor(t){this.config=t;}async post(t,s){let r=`${q}${s}`,e=new AbortController,o=this.config.timeoutMs??1e4,c=setTimeout(()=>e.abort(),o);this.config.debug&&console.log(`[Base Browser Transport] POST request to ${r}`,t);try{let u=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json","x-sdk":"js","x-sdk-version":x,...this.config.headers},body:JSON.stringify(t),signal:e.signal});if(!u.ok){let a=`HTTP ${u.status}: ${u.statusText}`;throw this.config.debug&&console.error("[Base Browser Transport] Request failed:",a),new Error(a)}let n=await u.json();return this.config.debug&&console.log("[Base Browser Transport] Request successful:",n),n}finally{c&&clearTimeout(c);}}};function h(i){let t=new f(i);return new m(t,i.debug)}function S(i,t,s,r){let e=[...i].sort(),o=r&&Object.keys(r).length>0?Object.fromEntries(Object.entries(r).sort(([u],[n])=>u.localeCompare(n))):null;return md5(JSON.stringify([e,t,s||null,o]))}async function K(i,t,s){i.debug&&console.log(`[BeLocal Multi Transport] Sending multi request with ${t.length} texts`);try{let r=new Map;t.forEach(n=>{let a=JSON.stringify({lang:n.lang,sourceLang:n.sourceLang||null,context:n.context||null});r.has(a)||r.set(a,[]),r.get(a).push(n);});let e=new Map,o=Array.from(r.entries()).map(([n,a])=>{let l=a[0],p=a.map(d=>d.text),g=S(p,l.lang,l.sourceLang,l.context);return e.set(g,a),{request_id:g,texts:p,lang:l.lang,source_lang:l.sourceLang,ctx:l.context}}),c=await i.baseTransport.post({requests:o},"/v1/translate/multi");i.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${c.results.length} groups`);let u=new Map;c.results.forEach(n=>{u.set(n.request_id,{texts:n.data.texts,status:n.data.status});}),e.forEach((n,a)=>{let l=u.get(a);if(!l){i.debug&&console.error(`[BeLocal Multi Transport] No result found for request_id: ${a}`),n.forEach(p=>{p.reject(new Error(`No result found for request ${a}`));});return}if(l.texts.length!==n.length){let p=new Error(`Mismatch: expected ${n.length} texts, got ${l.texts.length} for request_id ${a}`);i.debug&&console.error("[BeLocal Multi Transport]",p.message),n.forEach(g=>g.reject(p));return}n.forEach((p,g)=>{let d=l.texts[g],R=l.status||"success";i.debug&&console.log(`[BeLocal Multi Transport] Success for request_id ${a}[${g}]: "${d}"`),p.resolve({text:d,status:R});});});}catch(r){i.debug&&console.error("[BeLocal Multi Transport] Multi request error:",r);let e=r instanceof Error?r:new Error(String(r));t.forEach(o=>o.reject(e));}}function B(i,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let s=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,K(i,s).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let r=i.batchWindowMs??50;t.multiTimer=setTimeout(()=>B(i,t),r);}});}function M(i){let t=i.batchWindowMs??50,s={currentMulti:[],multiTimer:null,isRequestInFlight:false};return ({text:r,lang:e,source_lang:o,ctx:c})=>new Promise((u,n)=>{if(i.debug){let l=md5(JSON.stringify([r,e,o||null,c||null]));console.log(`[BeLocal Multi Transport] Queuing request ${l}: "${r}" to ${e}`);}let a={text:r,lang:e,sourceLang:o,context:c,resolve:u,reject:n};s.currentMulti.push(a),s.multiTimer===null&&!s.isRequestInFlight&&(s.multiTimer=setTimeout(()=>B(i,s),t));})}var T=class{constructor(){this.storage=new Map;}get(t){return this.storage.get(t)||null}set(t,s){this.storage.set(t,s);}};var y=class{constructor(t,s){let{apiKey:r,batchWindowMs:e=50,timeoutMs:o=1e4,debug:c=false}=t;this.debug=c,this.cache=new T;let u={Authorization:`Bearer ${r}`},n=s({headers:u,timeoutMs:o,debug:this.debug});this.transport=M({baseTransport:n,debug:this.debug,batchWindowMs:e}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:o,batchWindowMs:e});}async translate(t,s,r,e){return (await this.translateMany([t],s,r,e))[0]}async translateMany(t,s,r,e){let o=new Array(t.length),c=[];for(let u=0;u<t.length;u++){let n=t[u],a=this.generateCacheKey(n,s,r,e),l=this.cache.get(a);if(l){o[u]=l,this.debug&&console.log("[BeLocal Engine] Translation from cache:",n);continue}o[u]=null,c.push({index:u,text:n});}return c.length>0&&(await Promise.all(c.map(async({index:n,text:a})=>{let l=await this.transport({text:a,lang:s,source_lang:r,ctx:e});if(l.status!=="error"){let p=this.generateCacheKey(a,s,r,e);this.cache.set(p,l.text),this.debug&&console.log("[BeLocal Engine] Translation from API, cached:",a);}else this.debug&&console.log("[BeLocal Engine] Translation from API (not cached due to error status):",a);return {index:n,translation:l.text}}))).forEach(({index:n,translation:a})=>{o[n]=a;}),o}async t(t,s,r,e,o=false){let c=e||o?{...e?{user_ctx:e}:{},...o?{cache_type:"managed"}:{}}:void 0;return this.translate(t,s,r,c)}generateCacheKey(t,s,r,e){let o=e?Object.fromEntries(Object.entries(e).sort(([u],[n])=>u.localeCompare(n))):null;return md5(JSON.stringify({text:t,lang:s,source_lang:r||null,ctx:o}))}};var O="product",L="chat";var w=class extends y{constructor(t){super(t,h);}};
2
+ export{f as BaseBrowserTransport,w as BelocalEngine,L as USER_TYPE_CHAT,O as USER_TYPE_PRODUCT,h as createBaseBrowserTransport,M as createMultiTransport};//# sourceMappingURL=browser.mjs.map
3
3
  //# sourceMappingURL=browser.mjs.map