@belocal/js-sdk 1.0.3 → 1.0.4

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
@@ -74,15 +74,14 @@ Translates multiple texts in a single batch.
74
74
  | source_lang | string | Source language of the text |
75
75
  | context | string | Context for better understanding of what is being translated |
76
76
  | managed | boolean | Use managed translations cache (available on the website in Managed Translations section) |
77
- | ctx | object | User context, see [ctx properties](#ctx-properties) below |
77
+ | ctx | object | User context, see [Ctx Properties](#ctx-properties) below |
78
78
 
79
- #### ctx properties
79
+ #### Ctx Properties
80
80
 
81
81
  | Property | Description |
82
82
  |----------|-------------|
83
83
  | user_type | `product` or `chat`. Correct type improves translation quality |
84
- | entity_key | Used together with entity_id. When passed with the same values, a shared context is always used |
85
- | entity_id | Used together with entity_key. When passed with the same values, a shared context is always used |
84
+ | entity_key, entity_id | Used together. When passed, a shared context is created across all phrases for the key entity_key + entity_id. This context is used for all translations for such a pair of values and is constantly updated |
86
85
 
87
86
  ## Options
88
87
 
@@ -91,7 +90,6 @@ const engine = new BelocalEngine({
91
90
  apiKey: 'your-api-key', // required
92
91
  batchWindowMs: 50, // batch window in ms (default: 50)
93
92
  timeoutMs: 10000, // request timeout in ms (default: 10000)
94
- debug: false // enable debug logging (default: false)
95
93
  });
96
94
  ```
97
95
 
package/dist/browser.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';var jsMd5=require('js-md5');var M="1.0.3";function E(a,t){let e=s=>{if(s==null)return s;if(Array.isArray(s))return s.map(e);if(typeof s=="object"){let i={};for(let u of Object.keys(s).sort())i[u]=e(s[u]);return i}return s},r=JSON.stringify(e(t));return `${a}:${r}`}var m=class{constructor(t,e){this.wrappedTransport=t;this.debug=e;this.inFlightRequests=new Map;}async post(t,e){let r=E(e,t),s=this.inFlightRequests.get(r);if(s)return this.debug&&console.log(`[DedupeTransport] Deduplicating request to ${e}`),s;let i=this.wrappedTransport.post(t,e).finally(()=>{this.inFlightRequests.delete(r);});return this.inFlightRequests.set(r,i),this.debug&&console.log(`[DedupeTransport] New request to ${e} (${this.inFlightRequests.size} in-flight)`),i}};var R="https://dynamic.belocal.dev",d=class{constructor(t){this.config=t;}async post(t,e){let r=`${R}${e}`,s=new AbortController,i=this.config.timeoutMs??1e4,u=setTimeout(()=>s.abort(),i);this.config.debug&&console.log(`[Base Browser Transport] POST request to ${r}`,t);try{let g=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json","x-sdk":"js","x-sdk-version":M,...this.config.headers},body:JSON.stringify(t),signal:s.signal});if(!g.ok){let o=`HTTP ${g.status}: ${g.statusText}`;throw this.config.debug&&console.error("[Base Browser Transport] Request failed:",o),new Error(o)}let n=await g.json();return this.config.debug&&console.log("[Base Browser Transport] Request successful:",n),n}finally{u&&clearTimeout(u);}}};function h(a){let t=new d(a);return new m(t,a.debug)}function q(a,t,e,r){let s=[...a].sort(),i=r&&Object.keys(r).length>0?Object.fromEntries(Object.entries(r).sort(([g],[n])=>g.localeCompare(n))):null;return jsMd5.md5(JSON.stringify([s,t,e||null,i]))}async function S(a,t,e){a.debug&&console.log(`[BeLocal Multi Transport] Sending multi request with ${t.length} texts`);try{let r=new Map;t.forEach(n=>{let o=JSON.stringify({lang:n.lang,sourceLang:n.sourceLang||null,context:n.context||null});r.has(o)||r.set(o,[]),r.get(o).push(n);});let s=new Map,i=Array.from(r.entries()).map(([n,o])=>{let c=o[0],l=o.map(f=>f.text),p=q(l,c.lang,c.sourceLang,c.context);return s.set(p,o),{request_id:p,texts:l,lang:c.lang,source_lang:c.sourceLang,ctx:c.context}}),u=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${u.results.length} groups`);let g=new Map;u.results.forEach(n=>{g.set(n.request_id,{texts:n.data.texts,status:n.data.status});}),s.forEach((n,o)=>{let c=g.get(o);if(!c){a.debug&&console.error(`[BeLocal Multi Transport] No result found for request_id: ${o}`),n.forEach(l=>{l.reject(new Error(`No result found for request ${o}`));});return}if(c.texts.length!==n.length){let l=new Error(`Mismatch: expected ${n.length} texts, got ${c.texts.length} for request_id ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",l.message),n.forEach(p=>p.reject(l));return}n.forEach((l,p)=>{let f=c.texts[p],_=c.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for request_id ${o}[${p}]: "${f}"`),l.resolve({text:f,status:_});});});}catch(r){a.debug&&console.error("[BeLocal Multi Transport] Multi request error:",r);let s=r instanceof Error?r:new Error(String(r));t.forEach(i=>i.reject(s));}}function B(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let e=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,S(a,e).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let r=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>B(a,t),r);}});}function x(a){let t=a.batchWindowMs??50,e={currentMulti:[],multiTimer:null,isRequestInFlight:false},r=(({text:s,lang:i,source_lang:u,ctx:g})=>new Promise((n,o)=>{if(a.debug){let l=jsMd5.md5(JSON.stringify([s,i,u||null,g||null]));console.log(`[BeLocal Multi Transport] Queuing request ${l}: "${s}" to ${i}`);}let c={text:s,lang:i,sourceLang:u,context:g,resolve:n,reject:o};e.currentMulti.push(c),e.multiTimer===null&&!e.isRequestInFlight&&(e.multiTimer=setTimeout(()=>B(a,e),t));}));return r.destroy=()=>{e.multiTimer&&(clearTimeout(e.multiTimer),e.multiTimer=null),e.currentMulti.forEach(s=>s.reject(new Error("Transport destroyed"))),e.currentMulti=[];},r}var T=class{constructor(t={}){this.storage=new Map;this.maxSize=t.maxSize??1e4,this.ttlMs=t.ttlMs??36e5;}get(t){let e=this.storage.get(t);return e===void 0?null:Date.now()>e.expiresAt?(this.storage.delete(t),null):(this.storage.delete(t),this.storage.set(t,e),e.value)}set(t,e){if(this.storage.delete(t),this.storage.size>=this.maxSize){let r=this.storage.keys().next().value;r!==void 0&&this.storage.delete(r);}this.storage.set(t,{value:e,expiresAt:Date.now()+this.ttlMs});}clear(){this.storage.clear();}};var y=class{constructor(t,e){let{apiKey:r,batchWindowMs:s=50,timeoutMs:i=1e4,debug:u=false,cacheTtlMs:g=36e5,cacheMaxSize:n=1e4}=t;if(!r||typeof r!="string")throw new Error("[BeLocal] apiKey is required and must be a non-empty string");if(typeof i!="number"||!Number.isFinite(i)||i<=0)throw new Error("[BeLocal] timeoutMs must be a positive finite number");let o=Math.max(0,s);this.debug=u,this.cache=new T({ttlMs:g,maxSize:n});let c={Authorization:`Bearer ${r}`},l=e({headers:c,timeoutMs:i,debug:this.debug});this.transport=x({baseTransport:l,debug:this.debug,batchWindowMs:o}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:i,batchWindowMs:o});}async translate(t,e,r){return (await this.translateMany([t],e,r))[0]}async translateMany(t,e,r){let{source_lang:s,ctx:i}=this.resolveOptions(r),u=new Array(t.length),g=[];for(let n=0;n<t.length;n++){let o=t[n],c=this.generateCacheKey(o,e,s,i),l=this.cache.get(c);if(l!==null){u[n]=l,this.debug&&console.log("[BeLocal Engine] Translation from cache:",o);continue}u[n]=null,g.push({index:n,text:o});}return g.length>0&&(await Promise.allSettled(g.map(async({index:o,text:c})=>{let l=await this.transport({text:c,lang:e,source_lang:s,ctx:i});if(l.status!=="error"){let p=this.generateCacheKey(c,e,s,i);this.cache.set(p,l.text),this.debug&&console.log("[BeLocal Engine] Translation from API, cached:",c);}else this.debug&&console.log("[BeLocal Engine] Translation from API (not cached due to error status):",c);return {index:o,translation:l.text}}))).forEach((o,c)=>{let{index:l,text:p}=g[c];if(o.status==="fulfilled")u[l]=o.value.translation;else if(u[l]=p,this.debug){let f=o.reason;console.error("[BeLocal Engine] Translation failed, using original text:",p,f instanceof Error?f.message:String(f));}}),u}resolveOptions(t){if(!t)return {};let e={...t.ctx,...t.context&&{user_ctx:t.context},...t.managed&&{cache_type:"managed"}},r=Object.values(e).some(Boolean)?e:void 0;return {source_lang:t.source_lang,ctx:r}}destroy(){this.transport.destroy?.(),this.cache.clear();}generateCacheKey(t,e,r,s){let i=s?Object.fromEntries(Object.entries(s).sort(([g],[n])=>g.localeCompare(n))):null;return jsMd5.md5(JSON.stringify({text:t,lang:e,source_lang:r||null,ctx:i}))}};var v="product",L="chat";var w=class extends y{constructor(t){super(t,h);}};
1
+ 'use strict';var jsMd5=require('js-md5');var M="1.0.4";function E(a,t){let e=s=>{if(s==null)return s;if(Array.isArray(s))return s.map(e);if(typeof s=="object"){let i={};for(let u of Object.keys(s).sort())i[u]=e(s[u]);return i}return s},r=JSON.stringify(e(t));return `${a}:${r}`}var m=class{constructor(t,e){this.wrappedTransport=t;this.debug=e;this.inFlightRequests=new Map;}async post(t,e){let r=E(e,t),s=this.inFlightRequests.get(r);if(s)return this.debug&&console.log(`[DedupeTransport] Deduplicating request to ${e}`),s;let i=this.wrappedTransport.post(t,e).finally(()=>{this.inFlightRequests.delete(r);});return this.inFlightRequests.set(r,i),this.debug&&console.log(`[DedupeTransport] New request to ${e} (${this.inFlightRequests.size} in-flight)`),i}};var R="https://dynamic.belocal.dev",d=class{constructor(t){this.config=t;}async post(t,e){let r=`${R}${e}`,s=new AbortController,i=this.config.timeoutMs??1e4,u=setTimeout(()=>s.abort(),i);this.config.debug&&console.log(`[Base Browser Transport] POST request to ${r}`,t);try{let g=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json","x-sdk":"js","x-sdk-version":M,...this.config.headers},body:JSON.stringify(t),signal:s.signal});if(!g.ok){let o=`HTTP ${g.status}: ${g.statusText}`;throw this.config.debug&&console.error("[Base Browser Transport] Request failed:",o),new Error(o)}let n=await g.json();return this.config.debug&&console.log("[Base Browser Transport] Request successful:",n),n}finally{u&&clearTimeout(u);}}};function h(a){let t=new d(a);return new m(t,a.debug)}function q(a,t,e,r){let s=[...a].sort(),i=r&&Object.keys(r).length>0?Object.fromEntries(Object.entries(r).sort(([g],[n])=>g.localeCompare(n))):null;return jsMd5.md5(JSON.stringify([s,t,e||null,i]))}async function S(a,t,e){a.debug&&console.log(`[BeLocal Multi Transport] Sending multi request with ${t.length} texts`);try{let r=new Map;t.forEach(n=>{let o=JSON.stringify({lang:n.lang,sourceLang:n.sourceLang||null,context:n.context||null});r.has(o)||r.set(o,[]),r.get(o).push(n);});let s=new Map,i=Array.from(r.entries()).map(([n,o])=>{let c=o[0],l=o.map(f=>f.text),p=q(l,c.lang,c.sourceLang,c.context);return s.set(p,o),{request_id:p,texts:l,lang:c.lang,source_lang:c.sourceLang,ctx:c.context}}),u=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${u.results.length} groups`);let g=new Map;u.results.forEach(n=>{g.set(n.request_id,{texts:n.data.texts,status:n.data.status});}),s.forEach((n,o)=>{let c=g.get(o);if(!c){a.debug&&console.error(`[BeLocal Multi Transport] No result found for request_id: ${o}`),n.forEach(l=>{l.reject(new Error(`No result found for request ${o}`));});return}if(c.texts.length!==n.length){let l=new Error(`Mismatch: expected ${n.length} texts, got ${c.texts.length} for request_id ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",l.message),n.forEach(p=>p.reject(l));return}n.forEach((l,p)=>{let f=c.texts[p],_=c.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for request_id ${o}[${p}]: "${f}"`),l.resolve({text:f,status:_});});});}catch(r){a.debug&&console.error("[BeLocal Multi Transport] Multi request error:",r);let s=r instanceof Error?r:new Error(String(r));t.forEach(i=>i.reject(s));}}function B(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let e=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,S(a,e).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let r=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>B(a,t),r);}});}function x(a){let t=a.batchWindowMs??50,e={currentMulti:[],multiTimer:null,isRequestInFlight:false},r=(({text:s,lang:i,source_lang:u,ctx:g})=>new Promise((n,o)=>{if(a.debug){let l=jsMd5.md5(JSON.stringify([s,i,u||null,g||null]));console.log(`[BeLocal Multi Transport] Queuing request ${l}: "${s}" to ${i}`);}let c={text:s,lang:i,sourceLang:u,context:g,resolve:n,reject:o};e.currentMulti.push(c),e.multiTimer===null&&!e.isRequestInFlight&&(e.multiTimer=setTimeout(()=>B(a,e),t));}));return r.destroy=()=>{e.multiTimer&&(clearTimeout(e.multiTimer),e.multiTimer=null),e.currentMulti.forEach(s=>s.reject(new Error("Transport destroyed"))),e.currentMulti=[];},r}var T=class{constructor(t={}){this.storage=new Map;this.maxSize=t.maxSize??1e4,this.ttlMs=t.ttlMs??36e5;}get(t){let e=this.storage.get(t);return e===void 0?null:Date.now()>e.expiresAt?(this.storage.delete(t),null):(this.storage.delete(t),this.storage.set(t,e),e.value)}set(t,e){if(this.storage.delete(t),this.storage.size>=this.maxSize){let r=this.storage.keys().next().value;r!==void 0&&this.storage.delete(r);}this.storage.set(t,{value:e,expiresAt:Date.now()+this.ttlMs});}clear(){this.storage.clear();}};var y=class{constructor(t,e){let{apiKey:r,batchWindowMs:s=50,timeoutMs:i=1e4,debug:u=false,cacheTtlMs:g=36e5,cacheMaxSize:n=1e4}=t;if(!r||typeof r!="string")throw new Error("[BeLocal] apiKey is required and must be a non-empty string");if(typeof i!="number"||!Number.isFinite(i)||i<=0)throw new Error("[BeLocal] timeoutMs must be a positive finite number");let o=Math.max(0,s);this.debug=u,this.cache=new T({ttlMs:g,maxSize:n});let c={Authorization:`Bearer ${r}`},l=e({headers:c,timeoutMs:i,debug:this.debug});this.transport=x({baseTransport:l,debug:this.debug,batchWindowMs:o}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:i,batchWindowMs:o});}async translate(t,e,r){return (await this.translateMany([t],e,r))[0]}async translateMany(t,e,r){let{source_lang:s,ctx:i}=this.resolveOptions(r),u=new Array(t.length),g=[];for(let n=0;n<t.length;n++){let o=t[n],c=this.generateCacheKey(o,e,s,i),l=this.cache.get(c);if(l!==null){u[n]=l,this.debug&&console.log("[BeLocal Engine] Translation from cache:",o);continue}u[n]=null,g.push({index:n,text:o});}return g.length>0&&(await Promise.allSettled(g.map(async({index:o,text:c})=>{let l=await this.transport({text:c,lang:e,source_lang:s,ctx:i});if(l.status!=="error"){let p=this.generateCacheKey(c,e,s,i);this.cache.set(p,l.text),this.debug&&console.log("[BeLocal Engine] Translation from API, cached:",c);}else this.debug&&console.log("[BeLocal Engine] Translation from API (not cached due to error status):",c);return {index:o,translation:l.text}}))).forEach((o,c)=>{let{index:l,text:p}=g[c];if(o.status==="fulfilled")u[l]=o.value.translation;else if(u[l]=p,this.debug){let f=o.reason;console.error("[BeLocal Engine] Translation failed, using original text:",p,f instanceof Error?f.message:String(f));}}),u}resolveOptions(t){if(!t)return {};let e={...t.ctx,...t.context&&{user_ctx:t.context},...t.managed&&{cache_type:"managed"}},r=Object.values(e).some(Boolean)?e:void 0;return {source_lang:t.source_lang,ctx:r}}destroy(){this.transport.destroy?.(),this.cache.clear();}generateCacheKey(t,e,r,s){let i=s?Object.fromEntries(Object.entries(s).sort(([g],[n])=>g.localeCompare(n))):null;return jsMd5.md5(JSON.stringify({text:t,lang:e,source_lang:r||null,ctx:i}))}};var v="product",L="chat";var w=class extends y{constructor(t){super(t,h);}};
2
2
  exports.BaseBrowserTransport=d;exports.BelocalEngine=w;exports.USER_TYPE_CHAT=L;exports.USER_TYPE_PRODUCT=v;exports.createBaseBrowserTransport=h;exports.createMultiTransport=x;//# sourceMappingURL=browser.cjs.map
3
3
  //# sourceMappingURL=browser.cjs.map
package/dist/browser.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import {md5}from'js-md5';var M="1.0.3";function E(a,t){let e=s=>{if(s==null)return s;if(Array.isArray(s))return s.map(e);if(typeof s=="object"){let i={};for(let u of Object.keys(s).sort())i[u]=e(s[u]);return i}return s},r=JSON.stringify(e(t));return `${a}:${r}`}var m=class{constructor(t,e){this.wrappedTransport=t;this.debug=e;this.inFlightRequests=new Map;}async post(t,e){let r=E(e,t),s=this.inFlightRequests.get(r);if(s)return this.debug&&console.log(`[DedupeTransport] Deduplicating request to ${e}`),s;let i=this.wrappedTransport.post(t,e).finally(()=>{this.inFlightRequests.delete(r);});return this.inFlightRequests.set(r,i),this.debug&&console.log(`[DedupeTransport] New request to ${e} (${this.inFlightRequests.size} in-flight)`),i}};var R="https://dynamic.belocal.dev",d=class{constructor(t){this.config=t;}async post(t,e){let r=`${R}${e}`,s=new AbortController,i=this.config.timeoutMs??1e4,u=setTimeout(()=>s.abort(),i);this.config.debug&&console.log(`[Base Browser Transport] POST request to ${r}`,t);try{let g=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json","x-sdk":"js","x-sdk-version":M,...this.config.headers},body:JSON.stringify(t),signal:s.signal});if(!g.ok){let o=`HTTP ${g.status}: ${g.statusText}`;throw this.config.debug&&console.error("[Base Browser Transport] Request failed:",o),new Error(o)}let n=await g.json();return this.config.debug&&console.log("[Base Browser Transport] Request successful:",n),n}finally{u&&clearTimeout(u);}}};function h(a){let t=new d(a);return new m(t,a.debug)}function q(a,t,e,r){let s=[...a].sort(),i=r&&Object.keys(r).length>0?Object.fromEntries(Object.entries(r).sort(([g],[n])=>g.localeCompare(n))):null;return md5(JSON.stringify([s,t,e||null,i]))}async function S(a,t,e){a.debug&&console.log(`[BeLocal Multi Transport] Sending multi request with ${t.length} texts`);try{let r=new Map;t.forEach(n=>{let o=JSON.stringify({lang:n.lang,sourceLang:n.sourceLang||null,context:n.context||null});r.has(o)||r.set(o,[]),r.get(o).push(n);});let s=new Map,i=Array.from(r.entries()).map(([n,o])=>{let c=o[0],l=o.map(f=>f.text),p=q(l,c.lang,c.sourceLang,c.context);return s.set(p,o),{request_id:p,texts:l,lang:c.lang,source_lang:c.sourceLang,ctx:c.context}}),u=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${u.results.length} groups`);let g=new Map;u.results.forEach(n=>{g.set(n.request_id,{texts:n.data.texts,status:n.data.status});}),s.forEach((n,o)=>{let c=g.get(o);if(!c){a.debug&&console.error(`[BeLocal Multi Transport] No result found for request_id: ${o}`),n.forEach(l=>{l.reject(new Error(`No result found for request ${o}`));});return}if(c.texts.length!==n.length){let l=new Error(`Mismatch: expected ${n.length} texts, got ${c.texts.length} for request_id ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",l.message),n.forEach(p=>p.reject(l));return}n.forEach((l,p)=>{let f=c.texts[p],_=c.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for request_id ${o}[${p}]: "${f}"`),l.resolve({text:f,status:_});});});}catch(r){a.debug&&console.error("[BeLocal Multi Transport] Multi request error:",r);let s=r instanceof Error?r:new Error(String(r));t.forEach(i=>i.reject(s));}}function B(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let e=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,S(a,e).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let r=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>B(a,t),r);}});}function x(a){let t=a.batchWindowMs??50,e={currentMulti:[],multiTimer:null,isRequestInFlight:false},r=(({text:s,lang:i,source_lang:u,ctx:g})=>new Promise((n,o)=>{if(a.debug){let l=md5(JSON.stringify([s,i,u||null,g||null]));console.log(`[BeLocal Multi Transport] Queuing request ${l}: "${s}" to ${i}`);}let c={text:s,lang:i,sourceLang:u,context:g,resolve:n,reject:o};e.currentMulti.push(c),e.multiTimer===null&&!e.isRequestInFlight&&(e.multiTimer=setTimeout(()=>B(a,e),t));}));return r.destroy=()=>{e.multiTimer&&(clearTimeout(e.multiTimer),e.multiTimer=null),e.currentMulti.forEach(s=>s.reject(new Error("Transport destroyed"))),e.currentMulti=[];},r}var T=class{constructor(t={}){this.storage=new Map;this.maxSize=t.maxSize??1e4,this.ttlMs=t.ttlMs??36e5;}get(t){let e=this.storage.get(t);return e===void 0?null:Date.now()>e.expiresAt?(this.storage.delete(t),null):(this.storage.delete(t),this.storage.set(t,e),e.value)}set(t,e){if(this.storage.delete(t),this.storage.size>=this.maxSize){let r=this.storage.keys().next().value;r!==void 0&&this.storage.delete(r);}this.storage.set(t,{value:e,expiresAt:Date.now()+this.ttlMs});}clear(){this.storage.clear();}};var y=class{constructor(t,e){let{apiKey:r,batchWindowMs:s=50,timeoutMs:i=1e4,debug:u=false,cacheTtlMs:g=36e5,cacheMaxSize:n=1e4}=t;if(!r||typeof r!="string")throw new Error("[BeLocal] apiKey is required and must be a non-empty string");if(typeof i!="number"||!Number.isFinite(i)||i<=0)throw new Error("[BeLocal] timeoutMs must be a positive finite number");let o=Math.max(0,s);this.debug=u,this.cache=new T({ttlMs:g,maxSize:n});let c={Authorization:`Bearer ${r}`},l=e({headers:c,timeoutMs:i,debug:this.debug});this.transport=x({baseTransport:l,debug:this.debug,batchWindowMs:o}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:i,batchWindowMs:o});}async translate(t,e,r){return (await this.translateMany([t],e,r))[0]}async translateMany(t,e,r){let{source_lang:s,ctx:i}=this.resolveOptions(r),u=new Array(t.length),g=[];for(let n=0;n<t.length;n++){let o=t[n],c=this.generateCacheKey(o,e,s,i),l=this.cache.get(c);if(l!==null){u[n]=l,this.debug&&console.log("[BeLocal Engine] Translation from cache:",o);continue}u[n]=null,g.push({index:n,text:o});}return g.length>0&&(await Promise.allSettled(g.map(async({index:o,text:c})=>{let l=await this.transport({text:c,lang:e,source_lang:s,ctx:i});if(l.status!=="error"){let p=this.generateCacheKey(c,e,s,i);this.cache.set(p,l.text),this.debug&&console.log("[BeLocal Engine] Translation from API, cached:",c);}else this.debug&&console.log("[BeLocal Engine] Translation from API (not cached due to error status):",c);return {index:o,translation:l.text}}))).forEach((o,c)=>{let{index:l,text:p}=g[c];if(o.status==="fulfilled")u[l]=o.value.translation;else if(u[l]=p,this.debug){let f=o.reason;console.error("[BeLocal Engine] Translation failed, using original text:",p,f instanceof Error?f.message:String(f));}}),u}resolveOptions(t){if(!t)return {};let e={...t.ctx,...t.context&&{user_ctx:t.context},...t.managed&&{cache_type:"managed"}},r=Object.values(e).some(Boolean)?e:void 0;return {source_lang:t.source_lang,ctx:r}}destroy(){this.transport.destroy?.(),this.cache.clear();}generateCacheKey(t,e,r,s){let i=s?Object.fromEntries(Object.entries(s).sort(([g],[n])=>g.localeCompare(n))):null;return md5(JSON.stringify({text:t,lang:e,source_lang:r||null,ctx:i}))}};var v="product",L="chat";var w=class extends y{constructor(t){super(t,h);}};
1
+ import {md5}from'js-md5';var M="1.0.4";function E(a,t){let e=s=>{if(s==null)return s;if(Array.isArray(s))return s.map(e);if(typeof s=="object"){let i={};for(let u of Object.keys(s).sort())i[u]=e(s[u]);return i}return s},r=JSON.stringify(e(t));return `${a}:${r}`}var m=class{constructor(t,e){this.wrappedTransport=t;this.debug=e;this.inFlightRequests=new Map;}async post(t,e){let r=E(e,t),s=this.inFlightRequests.get(r);if(s)return this.debug&&console.log(`[DedupeTransport] Deduplicating request to ${e}`),s;let i=this.wrappedTransport.post(t,e).finally(()=>{this.inFlightRequests.delete(r);});return this.inFlightRequests.set(r,i),this.debug&&console.log(`[DedupeTransport] New request to ${e} (${this.inFlightRequests.size} in-flight)`),i}};var R="https://dynamic.belocal.dev",d=class{constructor(t){this.config=t;}async post(t,e){let r=`${R}${e}`,s=new AbortController,i=this.config.timeoutMs??1e4,u=setTimeout(()=>s.abort(),i);this.config.debug&&console.log(`[Base Browser Transport] POST request to ${r}`,t);try{let g=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json","x-sdk":"js","x-sdk-version":M,...this.config.headers},body:JSON.stringify(t),signal:s.signal});if(!g.ok){let o=`HTTP ${g.status}: ${g.statusText}`;throw this.config.debug&&console.error("[Base Browser Transport] Request failed:",o),new Error(o)}let n=await g.json();return this.config.debug&&console.log("[Base Browser Transport] Request successful:",n),n}finally{u&&clearTimeout(u);}}};function h(a){let t=new d(a);return new m(t,a.debug)}function q(a,t,e,r){let s=[...a].sort(),i=r&&Object.keys(r).length>0?Object.fromEntries(Object.entries(r).sort(([g],[n])=>g.localeCompare(n))):null;return md5(JSON.stringify([s,t,e||null,i]))}async function S(a,t,e){a.debug&&console.log(`[BeLocal Multi Transport] Sending multi request with ${t.length} texts`);try{let r=new Map;t.forEach(n=>{let o=JSON.stringify({lang:n.lang,sourceLang:n.sourceLang||null,context:n.context||null});r.has(o)||r.set(o,[]),r.get(o).push(n);});let s=new Map,i=Array.from(r.entries()).map(([n,o])=>{let c=o[0],l=o.map(f=>f.text),p=q(l,c.lang,c.sourceLang,c.context);return s.set(p,o),{request_id:p,texts:l,lang:c.lang,source_lang:c.sourceLang,ctx:c.context}}),u=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${u.results.length} groups`);let g=new Map;u.results.forEach(n=>{g.set(n.request_id,{texts:n.data.texts,status:n.data.status});}),s.forEach((n,o)=>{let c=g.get(o);if(!c){a.debug&&console.error(`[BeLocal Multi Transport] No result found for request_id: ${o}`),n.forEach(l=>{l.reject(new Error(`No result found for request ${o}`));});return}if(c.texts.length!==n.length){let l=new Error(`Mismatch: expected ${n.length} texts, got ${c.texts.length} for request_id ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",l.message),n.forEach(p=>p.reject(l));return}n.forEach((l,p)=>{let f=c.texts[p],_=c.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for request_id ${o}[${p}]: "${f}"`),l.resolve({text:f,status:_});});});}catch(r){a.debug&&console.error("[BeLocal Multi Transport] Multi request error:",r);let s=r instanceof Error?r:new Error(String(r));t.forEach(i=>i.reject(s));}}function B(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let e=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,S(a,e).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let r=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>B(a,t),r);}});}function x(a){let t=a.batchWindowMs??50,e={currentMulti:[],multiTimer:null,isRequestInFlight:false},r=(({text:s,lang:i,source_lang:u,ctx:g})=>new Promise((n,o)=>{if(a.debug){let l=md5(JSON.stringify([s,i,u||null,g||null]));console.log(`[BeLocal Multi Transport] Queuing request ${l}: "${s}" to ${i}`);}let c={text:s,lang:i,sourceLang:u,context:g,resolve:n,reject:o};e.currentMulti.push(c),e.multiTimer===null&&!e.isRequestInFlight&&(e.multiTimer=setTimeout(()=>B(a,e),t));}));return r.destroy=()=>{e.multiTimer&&(clearTimeout(e.multiTimer),e.multiTimer=null),e.currentMulti.forEach(s=>s.reject(new Error("Transport destroyed"))),e.currentMulti=[];},r}var T=class{constructor(t={}){this.storage=new Map;this.maxSize=t.maxSize??1e4,this.ttlMs=t.ttlMs??36e5;}get(t){let e=this.storage.get(t);return e===void 0?null:Date.now()>e.expiresAt?(this.storage.delete(t),null):(this.storage.delete(t),this.storage.set(t,e),e.value)}set(t,e){if(this.storage.delete(t),this.storage.size>=this.maxSize){let r=this.storage.keys().next().value;r!==void 0&&this.storage.delete(r);}this.storage.set(t,{value:e,expiresAt:Date.now()+this.ttlMs});}clear(){this.storage.clear();}};var y=class{constructor(t,e){let{apiKey:r,batchWindowMs:s=50,timeoutMs:i=1e4,debug:u=false,cacheTtlMs:g=36e5,cacheMaxSize:n=1e4}=t;if(!r||typeof r!="string")throw new Error("[BeLocal] apiKey is required and must be a non-empty string");if(typeof i!="number"||!Number.isFinite(i)||i<=0)throw new Error("[BeLocal] timeoutMs must be a positive finite number");let o=Math.max(0,s);this.debug=u,this.cache=new T({ttlMs:g,maxSize:n});let c={Authorization:`Bearer ${r}`},l=e({headers:c,timeoutMs:i,debug:this.debug});this.transport=x({baseTransport:l,debug:this.debug,batchWindowMs:o}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:i,batchWindowMs:o});}async translate(t,e,r){return (await this.translateMany([t],e,r))[0]}async translateMany(t,e,r){let{source_lang:s,ctx:i}=this.resolveOptions(r),u=new Array(t.length),g=[];for(let n=0;n<t.length;n++){let o=t[n],c=this.generateCacheKey(o,e,s,i),l=this.cache.get(c);if(l!==null){u[n]=l,this.debug&&console.log("[BeLocal Engine] Translation from cache:",o);continue}u[n]=null,g.push({index:n,text:o});}return g.length>0&&(await Promise.allSettled(g.map(async({index:o,text:c})=>{let l=await this.transport({text:c,lang:e,source_lang:s,ctx:i});if(l.status!=="error"){let p=this.generateCacheKey(c,e,s,i);this.cache.set(p,l.text),this.debug&&console.log("[BeLocal Engine] Translation from API, cached:",c);}else this.debug&&console.log("[BeLocal Engine] Translation from API (not cached due to error status):",c);return {index:o,translation:l.text}}))).forEach((o,c)=>{let{index:l,text:p}=g[c];if(o.status==="fulfilled")u[l]=o.value.translation;else if(u[l]=p,this.debug){let f=o.reason;console.error("[BeLocal Engine] Translation failed, using original text:",p,f instanceof Error?f.message:String(f));}}),u}resolveOptions(t){if(!t)return {};let e={...t.ctx,...t.context&&{user_ctx:t.context},...t.managed&&{cache_type:"managed"}},r=Object.values(e).some(Boolean)?e:void 0;return {source_lang:t.source_lang,ctx:r}}destroy(){this.transport.destroy?.(),this.cache.clear();}generateCacheKey(t,e,r,s){let i=s?Object.fromEntries(Object.entries(s).sort(([g],[n])=>g.localeCompare(n))):null;return md5(JSON.stringify({text:t,lang:e,source_lang:r||null,ctx:i}))}};var v="product",L="chat";var w=class extends y{constructor(t){super(t,h);}};
2
2
  export{d as BaseBrowserTransport,w as BelocalEngine,L as USER_TYPE_CHAT,v as USER_TYPE_PRODUCT,h as createBaseBrowserTransport,x as createMultiTransport};//# sourceMappingURL=browser.mjs.map
3
3
  //# sourceMappingURL=browser.mjs.map
package/dist/node.cjs CHANGED
@@ -27,7 +27,7 @@ var http2__namespace = /*#__PURE__*/_interopNamespace(http2);
27
27
  // src/transports/base/node.ts
28
28
 
29
29
  // src/version.ts
30
- var SDK_VERSION = "1.0.3" ;
30
+ var SDK_VERSION = "1.0.4" ;
31
31
  var SDK_NAME = "js";
32
32
 
33
33
  // src/transports/base/dedupe.ts
package/dist/node.mjs CHANGED
@@ -5,7 +5,7 @@ import { md5 } from 'js-md5';
5
5
  // src/transports/base/node.ts
6
6
 
7
7
  // src/version.ts
8
- var SDK_VERSION = "1.0.3" ;
8
+ var SDK_VERSION = "1.0.4" ;
9
9
  var SDK_NAME = "js";
10
10
 
11
11
  // src/transports/base/dedupe.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@belocal/js-sdk",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "BeLocal runtime JS SDK for on-demand translation (browser + node)",
5
5
  "scripts": {
6
6
  "test": "vitest run",