@belocal/js-sdk 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';var jsMd5=require('js-md5');function q(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 S(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 c=o[0],g=o.map(d=>d.text),p=q(g,c.lang,c.sourceLang,c.context);return r.set(p,o),{requestId:p,texts:g,lang:c.lang,sourceLang:c.sourceLang,context:c.context}}),l=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${l.results.length} groups`);let u=new Map;l.results.forEach(n=>{u.set(n.requestId,{texts:n.data.texts,status:n.data.status});}),r.forEach((n,o)=>{let c=u.get(o);if(!c){a.debug&&console.error(`[BeLocal Multi Transport] No result found for requestId: ${o}`),n.forEach(g=>{g.reject(new Error(`No result found for request ${o}`));});return}if(c.texts.length!==n.length){let g=new Error(`Mismatch: expected ${n.length} texts, got ${c.texts.length} for requestId ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",g.message),n.forEach(p=>p.reject(g));return}n.forEach((g,p)=>{let d=c.texts[p],x=c.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for requestId ${o}[${p}]: "${d}"`),g.resolve({text:d,status:x});});});}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 b(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let s=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,S(a,s).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let e=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>b(a,t),e);}});}function T(a){let t=a.batchWindowMs??50,s={currentMulti:[],multiTimer:null,isRequestInFlight:false};return ({text:e,lang:r,source_lang:i,ctx:l})=>new Promise((u,n)=>{if(a.debug){let c=jsMd5.md5(JSON.stringify([e,r,i||null,l||null]));console.log(`[BeLocal Multi Transport] Queuing request ${c}: "${e}" to ${r}`);}let o={text:e,lang:r,sourceLang:i,context:l,resolve:u,reject:n};s.currentMulti.push(o),s.multiTimer===null&&!s.isRequestInFlight&&(s.multiTimer=setTimeout(()=>b(a,s),t));})}var B=(()=>{try{return "0.6.1"}catch{return "undefined"}})(),w="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 l of Object.keys(r).sort())i[l]=s(r[l]);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 L="https://dynamic.belocal.dev",f=class{constructor(t){this.config=t;}async post(t,s){let e=`${L}${s}`,r=new AbortController,i=this.config.timeoutMs??1e4,l=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":w,"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{l&&clearTimeout(l);}}};function m(a){let t=new f(a);return new h(t,a.debug)}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 R=class{constructor(t){let{apiKey:s,batchWindowMs:e=50,timeoutMs:r=1e4,debug:i=false}=t;this.debug=i,this.cache=new y;let l={Authorization:`Bearer ${s}`},u=m({headers:l,timeoutMs:r,debug:this.debug});this.transport=T({baseTransport:u,debug:this.debug,batchWindowMs:e}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:r,batchWindowMs:e});}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),l=[];for(let u=0;u<t.length;u++){let n=t[u],o=this.generateCacheKey(n,s,e,r),c=this.cache.get(o);if(c){i[u]=c,this.debug&&console.log("[BeLocal Engine] Translation from cache:",n);continue}i[u]=null,l.push({index:u,text:n});}return l.length>0&&(await Promise.all(l.map(async({index:n,text:o})=>{let c=await this.transport({text:o,lang:s,source_lang:e,ctx:r});if(c.status!=="error"){let g=this.generateCacheKey(o,s,e,r);this.cache.set(g,c.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:c.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}))}};
1
+ 'use strict';var jsMd5=require('js-md5');function q(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 S(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 c=o[0],g=o.map(d=>d.text),p=q(g,c.lang,c.sourceLang,c.context);return r.set(p,o),{requestId:p,texts:g,lang:c.lang,sourceLang:c.sourceLang,context:c.context}}),l=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${l.results.length} groups`);let u=new Map;l.results.forEach(n=>{u.set(n.requestId,{texts:n.data.texts,status:n.data.status});}),r.forEach((n,o)=>{let c=u.get(o);if(!c){a.debug&&console.error(`[BeLocal Multi Transport] No result found for requestId: ${o}`),n.forEach(g=>{g.reject(new Error(`No result found for request ${o}`));});return}if(c.texts.length!==n.length){let g=new Error(`Mismatch: expected ${n.length} texts, got ${c.texts.length} for requestId ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",g.message),n.forEach(p=>p.reject(g));return}n.forEach((g,p)=>{let d=c.texts[p],x=c.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for requestId ${o}[${p}]: "${d}"`),g.resolve({text:d,status:x});});});}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 b(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let s=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,S(a,s).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let e=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>b(a,t),e);}});}function T(a){let t=a.batchWindowMs??50,s={currentMulti:[],multiTimer:null,isRequestInFlight:false};return ({text:e,lang:r,source_lang:i,ctx:l})=>new Promise((u,n)=>{if(a.debug){let c=jsMd5.md5(JSON.stringify([e,r,i||null,l||null]));console.log(`[BeLocal Multi Transport] Queuing request ${c}: "${e}" to ${r}`);}let o={text:e,lang:r,sourceLang:i,context:l,resolve:u,reject:n};s.currentMulti.push(o),s.multiTimer===null&&!s.isRequestInFlight&&(s.multiTimer=setTimeout(()=>b(a,s),t));})}var B=(()=>{try{return "0.7.0"}catch{return "undefined"}})(),w="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 l of Object.keys(r).sort())i[l]=s(r[l]);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 L="https://dynamic.belocal.dev",f=class{constructor(t){this.config=t;}async post(t,s){let e=`${L}${s}`,r=new AbortController,i=this.config.timeoutMs??1e4,l=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":w,"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{l&&clearTimeout(l);}}};function m(a){let t=new f(a);return new h(t,a.debug)}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 R=class{constructor(t){let{apiKey:s,batchWindowMs:e=50,timeoutMs:r=1e4,debug:i=false}=t;this.debug=i,this.cache=new y;let l={Authorization:`Bearer ${s}`},u=m({headers:l,timeoutMs:r,debug:this.debug});this.transport=T({baseTransport:u,debug:this.debug,batchWindowMs:e}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:r,batchWindowMs:e});}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),l=[];for(let u=0;u<t.length;u++){let n=t[u],o=this.generateCacheKey(n,s,e,r),c=this.cache.get(o);if(c){i[u]=c,this.debug&&console.log("[BeLocal Engine] Translation from cache:",n);continue}i[u]=null,l.push({index:u,text:n});}return l.length>0&&(await Promise.all(l.map(async({index:n,text:o})=>{let c=await this.transport({text:o,lang:s,source_lang:e,ctx:r});if(c.status!=="error"){let g=this.generateCacheKey(o,s,e,r);this.cache.set(g,c.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:c.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}))}};
2
2
  exports.BaseBrowserTransport=f;exports.BelocalEngine=R;exports.createBaseBrowserTransport=m;exports.createMultiTransport=T;//# sourceMappingURL=browser.cjs.map
3
3
  //# sourceMappingURL=browser.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transports/multi.ts","../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/browser.ts","../src/cache/local.ts","../src/core/engine/browser.ts"],"names":["generateRequestId","texts","lang","sourceLang","context","sortedTexts","sortedContext","sortedKeys","key","json","md5","sendMulti","config","items","state","groups","item","groupKey","requestIdToGroupItems","requests","groupItems","firstItem","requestId","multiResponse","resultMap","result","error","index","translatedText","status","errorToReject","processMulti","itemsToSend","windowMs","createMultiTransport","text","source_lang","ctx","resolve","reject","tempRequestId","requestItem","SDK_VERSION","SDK_NAME","generateRequestKey","endpointPath","data","normalize","value","sorted","normalizedData","DedupeTransport","wrappedTransport","debug","requestKey","existingRequest","requestPromise","BASE_URL","BaseBrowserTransport","url","controller","timeoutMs","timeoutId","response","errorMsg","createBaseBrowserTransport","transport","LocalCache","BelocalEngine","options","apiKey","batchWindowMs","authHeaders","baseTransport","results","cacheMisses","i","cacheKey","cachedResult","translation","sortedCtx","acc"],"mappings":"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","file":"browser.cjs","sourcesContent":["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 requestId: string;\n texts: string[];\n lang: string;\n sourceLang?: string;\n context?: 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 requestId: 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 // Сортируем тексты (создаем копию, чтобы не изменять оригинал)\n const sortedTexts = [...texts].sort();\n \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 // Создаем JSON массив: [sortedTexts, lang, sourceLang, context]\n // В PHP примере: [$sortedTexts, $this->lang, $context] (sourceLang не включен)\n // Но мы включаем sourceLang для правильной группировки запросов с разными sourceLang\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 // Группируем тексты по параметрам перевода (lang, sourceLang, context)\n const groups = new Map<string, MultiRequestItem[]>();\n \n items.forEach(item => {\n // Используем ключ для группировки без текстов (только параметры перевода)\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 // Создаем запросы для каждой группы и сохраняем соответствие requestId -> groupItems\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 // Генерируем requestId включая отсортированные тексты группы\n // Включаем sourceLang для правильной группировки запросов с разными sourceLang\n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n \n // Сохраняем соответствие requestId -> groupItems\n requestIdToGroupItems.set(requestId, groupItems);\n \n return {\n requestId,\n texts,\n lang: firstItem.lang,\n sourceLang: firstItem.sourceLang,\n context: 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 // Создаем map для быстрого поиска результатов по requestId\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.requestId, { texts: result.data.texts, status: result.data.status });\n });\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 requestId: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n // Маппим тексты обратно на промисы по индексу\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for requestId ${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 requestId ${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 // При ошибке сети отклоняем все промисы\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 // Для отладки показываем временный requestId (будет пересчитан при группировке)\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","// 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 { 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 } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { createBaseBrowserTransport } from '../../transports/base/browser';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\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 {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n /**\n * Creates a new BelocalEngine instance.\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 const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n \n this.cache = new LocalCache();\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n const baseTransport = createBaseBrowserTransport({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n // Always use multi transport\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` ('editable')\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: 'editable' });\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` ('editable' | 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: 'editable' }\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\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/transports/multi.ts","../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/browser.ts","../src/cache/local.ts","../src/core/engine/browser.ts"],"names":["generateRequestId","texts","lang","sourceLang","context","sortedTexts","sortedContext","sortedKeys","key","json","md5","sendMulti","config","items","state","groups","item","groupKey","requestIdToGroupItems","requests","groupItems","firstItem","requestId","multiResponse","resultMap","result","error","index","translatedText","status","errorToReject","processMulti","itemsToSend","windowMs","createMultiTransport","text","source_lang","ctx","resolve","reject","tempRequestId","requestItem","SDK_VERSION","SDK_NAME","generateRequestKey","endpointPath","data","normalize","value","sorted","normalizedData","DedupeTransport","wrappedTransport","debug","requestKey","existingRequest","requestPromise","BASE_URL","BaseBrowserTransport","url","controller","timeoutMs","timeoutId","response","errorMsg","createBaseBrowserTransport","transport","LocalCache","BelocalEngine","options","apiKey","batchWindowMs","authHeaders","baseTransport","results","cacheMisses","i","cacheKey","cachedResult","translation","sortedCtx","acc"],"mappings":"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","file":"browser.cjs","sourcesContent":["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 requestId: string;\n texts: string[];\n lang: string;\n sourceLang?: string;\n context?: 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 requestId: 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 // Сортируем тексты (создаем копию, чтобы не изменять оригинал)\n const sortedTexts = [...texts].sort();\n \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 // Создаем JSON массив: [sortedTexts, lang, sourceLang, context]\n // В PHP примере: [$sortedTexts, $this->lang, $context] (sourceLang не включен)\n // Но мы включаем sourceLang для правильной группировки запросов с разными sourceLang\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 // Группируем тексты по параметрам перевода (lang, sourceLang, context)\n const groups = new Map<string, MultiRequestItem[]>();\n \n items.forEach(item => {\n // Используем ключ для группировки без текстов (только параметры перевода)\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 // Создаем запросы для каждой группы и сохраняем соответствие requestId -> groupItems\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 // Генерируем requestId включая отсортированные тексты группы\n // Включаем sourceLang для правильной группировки запросов с разными sourceLang\n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n \n // Сохраняем соответствие requestId -> groupItems\n requestIdToGroupItems.set(requestId, groupItems);\n \n return {\n requestId,\n texts,\n lang: firstItem.lang,\n sourceLang: firstItem.sourceLang,\n context: 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 // Создаем map для быстрого поиска результатов по requestId\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.requestId, { texts: result.data.texts, status: result.data.status });\n });\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 requestId: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n // Маппим тексты обратно на промисы по индексу\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for requestId ${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 requestId ${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 // При ошибке сети отклоняем все промисы\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 // Для отладки показываем временный requestId (будет пересчитан при группировке)\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","// 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 { 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 } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { createBaseBrowserTransport } from '../../transports/base/browser';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\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 {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n /**\n * Creates a new BelocalEngine instance.\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 const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n \n this.cache = new LocalCache();\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n const baseTransport = createBaseBrowserTransport({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n // Always use multi transport\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\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"]}
package/dist/browser.d.ts CHANGED
@@ -7,7 +7,7 @@ type Lang = string;
7
7
  * Key-value pairs for translation context.
8
8
  * Supported keys:
9
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., 'editable'
10
+ * - `cache_type` (string): Cache type, e.g., 'managed' (managed translations cache)
11
11
  *
12
12
  * Other keys will be ignored by the API.
13
13
  */
@@ -111,7 +111,7 @@ declare class BelocalEngine {
111
111
  * @param text - The text to translate
112
112
  * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
113
113
  * @param source_lang - Optional source language code. If not provided, auto-detection is used
114
- * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context), `cache_type` ('editable')
114
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context), `cache_type` ('managed')
115
115
  * @returns Promise resolving to the translated text
116
116
  * @throws {Error} If the translation request fails (network error, API error, timeout)
117
117
  *
@@ -129,7 +129,7 @@ declare class BelocalEngine {
129
129
  * });
130
130
  *
131
131
  * // With cache_type
132
- * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'editable' });
132
+ * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'managed' });
133
133
  *
134
134
  * // With source language and context
135
135
  * const result = await engine.translate('Hello world', 'es', 'en', {
@@ -150,7 +150,7 @@ declare class BelocalEngine {
150
150
  * @param texts - Array of texts to translate
151
151
  * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
152
152
  * @param source_lang - Optional source language code. If not provided, auto-detection is used
153
- * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('editable' | string)
153
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
154
154
  * @returns Promise resolving to an array of translated texts in the same order as input
155
155
  * @throws {Error} If the translation request fails (network error, API error, timeout)
156
156
  *
@@ -176,7 +176,7 @@ declare class BelocalEngine {
176
176
  * ['Hello', 'World'],
177
177
  * 'es',
178
178
  * undefined,
179
- * { cache_type: 'editable' }
179
+ * { cache_type: 'managed' }
180
180
  * );
181
181
  *
182
182
  * // Empty array returns empty array
package/dist/browser.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import {md5}from'js-md5';function q(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 S(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 c=o[0],g=o.map(d=>d.text),p=q(g,c.lang,c.sourceLang,c.context);return r.set(p,o),{requestId:p,texts:g,lang:c.lang,sourceLang:c.sourceLang,context:c.context}}),l=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${l.results.length} groups`);let u=new Map;l.results.forEach(n=>{u.set(n.requestId,{texts:n.data.texts,status:n.data.status});}),r.forEach((n,o)=>{let c=u.get(o);if(!c){a.debug&&console.error(`[BeLocal Multi Transport] No result found for requestId: ${o}`),n.forEach(g=>{g.reject(new Error(`No result found for request ${o}`));});return}if(c.texts.length!==n.length){let g=new Error(`Mismatch: expected ${n.length} texts, got ${c.texts.length} for requestId ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",g.message),n.forEach(p=>p.reject(g));return}n.forEach((g,p)=>{let d=c.texts[p],x=c.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for requestId ${o}[${p}]: "${d}"`),g.resolve({text:d,status:x});});});}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 b(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let s=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,S(a,s).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let e=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>b(a,t),e);}});}function T(a){let t=a.batchWindowMs??50,s={currentMulti:[],multiTimer:null,isRequestInFlight:false};return ({text:e,lang:r,source_lang:i,ctx:l})=>new Promise((u,n)=>{if(a.debug){let c=md5(JSON.stringify([e,r,i||null,l||null]));console.log(`[BeLocal Multi Transport] Queuing request ${c}: "${e}" to ${r}`);}let o={text:e,lang:r,sourceLang:i,context:l,resolve:u,reject:n};s.currentMulti.push(o),s.multiTimer===null&&!s.isRequestInFlight&&(s.multiTimer=setTimeout(()=>b(a,s),t));})}var B=(()=>{try{return "0.6.1"}catch{return "undefined"}})(),w="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 l of Object.keys(r).sort())i[l]=s(r[l]);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 L="https://dynamic.belocal.dev",f=class{constructor(t){this.config=t;}async post(t,s){let e=`${L}${s}`,r=new AbortController,i=this.config.timeoutMs??1e4,l=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":w,"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{l&&clearTimeout(l);}}};function m(a){let t=new f(a);return new h(t,a.debug)}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 R=class{constructor(t){let{apiKey:s,batchWindowMs:e=50,timeoutMs:r=1e4,debug:i=false}=t;this.debug=i,this.cache=new y;let l={Authorization:`Bearer ${s}`},u=m({headers:l,timeoutMs:r,debug:this.debug});this.transport=T({baseTransport:u,debug:this.debug,batchWindowMs:e}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:r,batchWindowMs:e});}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),l=[];for(let u=0;u<t.length;u++){let n=t[u],o=this.generateCacheKey(n,s,e,r),c=this.cache.get(o);if(c){i[u]=c,this.debug&&console.log("[BeLocal Engine] Translation from cache:",n);continue}i[u]=null,l.push({index:u,text:n});}return l.length>0&&(await Promise.all(l.map(async({index:n,text:o})=>{let c=await this.transport({text:o,lang:s,source_lang:e,ctx:r});if(c.status!=="error"){let g=this.generateCacheKey(o,s,e,r);this.cache.set(g,c.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:c.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}))}};
1
+ import {md5}from'js-md5';function q(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 S(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 c=o[0],g=o.map(d=>d.text),p=q(g,c.lang,c.sourceLang,c.context);return r.set(p,o),{requestId:p,texts:g,lang:c.lang,sourceLang:c.sourceLang,context:c.context}}),l=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${l.results.length} groups`);let u=new Map;l.results.forEach(n=>{u.set(n.requestId,{texts:n.data.texts,status:n.data.status});}),r.forEach((n,o)=>{let c=u.get(o);if(!c){a.debug&&console.error(`[BeLocal Multi Transport] No result found for requestId: ${o}`),n.forEach(g=>{g.reject(new Error(`No result found for request ${o}`));});return}if(c.texts.length!==n.length){let g=new Error(`Mismatch: expected ${n.length} texts, got ${c.texts.length} for requestId ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",g.message),n.forEach(p=>p.reject(g));return}n.forEach((g,p)=>{let d=c.texts[p],x=c.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for requestId ${o}[${p}]: "${d}"`),g.resolve({text:d,status:x});});});}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 b(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let s=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,S(a,s).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let e=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>b(a,t),e);}});}function T(a){let t=a.batchWindowMs??50,s={currentMulti:[],multiTimer:null,isRequestInFlight:false};return ({text:e,lang:r,source_lang:i,ctx:l})=>new Promise((u,n)=>{if(a.debug){let c=md5(JSON.stringify([e,r,i||null,l||null]));console.log(`[BeLocal Multi Transport] Queuing request ${c}: "${e}" to ${r}`);}let o={text:e,lang:r,sourceLang:i,context:l,resolve:u,reject:n};s.currentMulti.push(o),s.multiTimer===null&&!s.isRequestInFlight&&(s.multiTimer=setTimeout(()=>b(a,s),t));})}var B=(()=>{try{return "0.7.0"}catch{return "undefined"}})(),w="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 l of Object.keys(r).sort())i[l]=s(r[l]);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 L="https://dynamic.belocal.dev",f=class{constructor(t){this.config=t;}async post(t,s){let e=`${L}${s}`,r=new AbortController,i=this.config.timeoutMs??1e4,l=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":w,"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{l&&clearTimeout(l);}}};function m(a){let t=new f(a);return new h(t,a.debug)}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 R=class{constructor(t){let{apiKey:s,batchWindowMs:e=50,timeoutMs:r=1e4,debug:i=false}=t;this.debug=i,this.cache=new y;let l={Authorization:`Bearer ${s}`},u=m({headers:l,timeoutMs:r,debug:this.debug});this.transport=T({baseTransport:u,debug:this.debug,batchWindowMs:e}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:r,batchWindowMs:e});}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),l=[];for(let u=0;u<t.length;u++){let n=t[u],o=this.generateCacheKey(n,s,e,r),c=this.cache.get(o);if(c){i[u]=c,this.debug&&console.log("[BeLocal Engine] Translation from cache:",n);continue}i[u]=null,l.push({index:u,text:n});}return l.length>0&&(await Promise.all(l.map(async({index:n,text:o})=>{let c=await this.transport({text:o,lang:s,source_lang:e,ctx:r});if(c.status!=="error"){let g=this.generateCacheKey(o,s,e,r);this.cache.set(g,c.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:c.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}))}};
2
2
  export{f as BaseBrowserTransport,R as BelocalEngine,m as createBaseBrowserTransport,T as createMultiTransport};//# sourceMappingURL=browser.mjs.map
3
3
  //# sourceMappingURL=browser.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transports/multi.ts","../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/browser.ts","../src/cache/local.ts","../src/core/engine/browser.ts"],"names":["generateRequestId","texts","lang","sourceLang","context","sortedTexts","sortedContext","sortedKeys","key","json","md5","sendMulti","config","items","state","groups","item","groupKey","requestIdToGroupItems","requests","groupItems","firstItem","requestId","multiResponse","resultMap","result","error","index","translatedText","status","errorToReject","processMulti","itemsToSend","windowMs","createMultiTransport","text","source_lang","ctx","resolve","reject","tempRequestId","requestItem","SDK_VERSION","SDK_NAME","generateRequestKey","endpointPath","data","normalize","value","sorted","normalizedData","DedupeTransport","wrappedTransport","debug","requestKey","existingRequest","requestPromise","BASE_URL","BaseBrowserTransport","url","controller","timeoutMs","timeoutId","response","errorMsg","createBaseBrowserTransport","transport","LocalCache","BelocalEngine","options","apiKey","batchWindowMs","authHeaders","baseTransport","results","cacheMisses","i","cacheKey","cachedResult","translation","sortedCtx","acc"],"mappings":"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","file":"browser.mjs","sourcesContent":["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 requestId: string;\n texts: string[];\n lang: string;\n sourceLang?: string;\n context?: 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 requestId: 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 // Сортируем тексты (создаем копию, чтобы не изменять оригинал)\n const sortedTexts = [...texts].sort();\n \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 // Создаем JSON массив: [sortedTexts, lang, sourceLang, context]\n // В PHP примере: [$sortedTexts, $this->lang, $context] (sourceLang не включен)\n // Но мы включаем sourceLang для правильной группировки запросов с разными sourceLang\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 // Группируем тексты по параметрам перевода (lang, sourceLang, context)\n const groups = new Map<string, MultiRequestItem[]>();\n \n items.forEach(item => {\n // Используем ключ для группировки без текстов (только параметры перевода)\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 // Создаем запросы для каждой группы и сохраняем соответствие requestId -> groupItems\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 // Генерируем requestId включая отсортированные тексты группы\n // Включаем sourceLang для правильной группировки запросов с разными sourceLang\n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n \n // Сохраняем соответствие requestId -> groupItems\n requestIdToGroupItems.set(requestId, groupItems);\n \n return {\n requestId,\n texts,\n lang: firstItem.lang,\n sourceLang: firstItem.sourceLang,\n context: 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 // Создаем map для быстрого поиска результатов по requestId\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.requestId, { texts: result.data.texts, status: result.data.status });\n });\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 requestId: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n // Маппим тексты обратно на промисы по индексу\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for requestId ${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 requestId ${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 // При ошибке сети отклоняем все промисы\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 // Для отладки показываем временный requestId (будет пересчитан при группировке)\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","// 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 { 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 } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { createBaseBrowserTransport } from '../../transports/base/browser';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\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 {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n /**\n * Creates a new BelocalEngine instance.\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 const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n \n this.cache = new LocalCache();\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n const baseTransport = createBaseBrowserTransport({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n // Always use multi transport\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` ('editable')\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: 'editable' });\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` ('editable' | 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: 'editable' }\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\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/transports/multi.ts","../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/browser.ts","../src/cache/local.ts","../src/core/engine/browser.ts"],"names":["generateRequestId","texts","lang","sourceLang","context","sortedTexts","sortedContext","sortedKeys","key","json","md5","sendMulti","config","items","state","groups","item","groupKey","requestIdToGroupItems","requests","groupItems","firstItem","requestId","multiResponse","resultMap","result","error","index","translatedText","status","errorToReject","processMulti","itemsToSend","windowMs","createMultiTransport","text","source_lang","ctx","resolve","reject","tempRequestId","requestItem","SDK_VERSION","SDK_NAME","generateRequestKey","endpointPath","data","normalize","value","sorted","normalizedData","DedupeTransport","wrappedTransport","debug","requestKey","existingRequest","requestPromise","BASE_URL","BaseBrowserTransport","url","controller","timeoutMs","timeoutId","response","errorMsg","createBaseBrowserTransport","transport","LocalCache","BelocalEngine","options","apiKey","batchWindowMs","authHeaders","baseTransport","results","cacheMisses","i","cacheKey","cachedResult","translation","sortedCtx","acc"],"mappings":"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","file":"browser.mjs","sourcesContent":["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 requestId: string;\n texts: string[];\n lang: string;\n sourceLang?: string;\n context?: 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 requestId: 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 // Сортируем тексты (создаем копию, чтобы не изменять оригинал)\n const sortedTexts = [...texts].sort();\n \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 // Создаем JSON массив: [sortedTexts, lang, sourceLang, context]\n // В PHP примере: [$sortedTexts, $this->lang, $context] (sourceLang не включен)\n // Но мы включаем sourceLang для правильной группировки запросов с разными sourceLang\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 // Группируем тексты по параметрам перевода (lang, sourceLang, context)\n const groups = new Map<string, MultiRequestItem[]>();\n \n items.forEach(item => {\n // Используем ключ для группировки без текстов (только параметры перевода)\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 // Создаем запросы для каждой группы и сохраняем соответствие requestId -> groupItems\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 // Генерируем requestId включая отсортированные тексты группы\n // Включаем sourceLang для правильной группировки запросов с разными sourceLang\n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n \n // Сохраняем соответствие requestId -> groupItems\n requestIdToGroupItems.set(requestId, groupItems);\n \n return {\n requestId,\n texts,\n lang: firstItem.lang,\n sourceLang: firstItem.sourceLang,\n context: 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 // Создаем map для быстрого поиска результатов по requestId\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.requestId, { texts: result.data.texts, status: result.data.status });\n });\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 requestId: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n // Маппим тексты обратно на промисы по индексу\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for requestId ${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 requestId ${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 // При ошибке сети отклоняем все промисы\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 // Для отладки показываем временный requestId (будет пересчитан при группировке)\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","// 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 { 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 } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { createBaseBrowserTransport } from '../../transports/base/browser';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\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 {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n /**\n * Creates a new BelocalEngine instance.\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 const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n \n this.cache = new LocalCache();\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n const baseTransport = createBaseBrowserTransport({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n // Always use multi transport\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\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"]}
package/dist/node.cjs CHANGED
@@ -168,7 +168,7 @@ function createMultiTransport(config) {
168
168
  // src/version.ts
169
169
  var SDK_VERSION = (() => {
170
170
  try {
171
- return true ? "0.6.1" : "undefined";
171
+ return true ? "0.7.0" : "undefined";
172
172
  } catch {
173
173
  return "undefined";
174
174
  }
@@ -448,7 +448,7 @@ var BelocalEngine = class {
448
448
  * @param text - The text to translate
449
449
  * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
450
450
  * @param source_lang - Optional source language code. If not provided, auto-detection is used
451
- * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('editable' | string)
451
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
452
452
  * @returns Promise resolving to the translated text
453
453
  * @throws {Error} If the translation request fails (network error, API error, timeout)
454
454
  *
@@ -466,7 +466,7 @@ var BelocalEngine = class {
466
466
  * });
467
467
  *
468
468
  * // With cache_type
469
- * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'editable' });
469
+ * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'managed' });
470
470
  *
471
471
  * // With source language and context
472
472
  * const result = await engine.translate('Hello world', 'es', 'en', {
@@ -490,7 +490,7 @@ var BelocalEngine = class {
490
490
  * @param texts - Array of texts to translate
491
491
  * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
492
492
  * @param source_lang - Optional source language code. If not provided, auto-detection is used
493
- * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('editable' | string)
493
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
494
494
  * @returns Promise resolving to an array of translated texts in the same order as input
495
495
  * @throws {Error} If the translation request fails (network error, API error, timeout)
496
496
  *
@@ -516,7 +516,7 @@ var BelocalEngine = class {
516
516
  * ['Hello', 'World'],
517
517
  * 'es',
518
518
  * undefined,
519
- * { cache_type: 'editable' }
519
+ * { cache_type: 'managed' }
520
520
  * );
521
521
  *
522
522
  * // Empty array returns empty array
package/dist/node.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transports/multi.ts","../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/node.ts","../src/cache/local.ts","../src/core/engine/node.ts"],"names":["md5","session","URL","http2","headers"],"mappings":"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","file":"node.cjs","sourcesContent":["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 requestId: string;\n texts: string[];\n lang: string;\n sourceLang?: string;\n context?: 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 requestId: 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 // Сортируем тексты (создаем копию, чтобы не изменять оригинал)\n const sortedTexts = [...texts].sort();\n \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 // Создаем JSON массив: [sortedTexts, lang, sourceLang, context]\n // В PHP примере: [$sortedTexts, $this->lang, $context] (sourceLang не включен)\n // Но мы включаем sourceLang для правильной группировки запросов с разными sourceLang\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 // Группируем тексты по параметрам перевода (lang, sourceLang, context)\n const groups = new Map<string, MultiRequestItem[]>();\n \n items.forEach(item => {\n // Используем ключ для группировки без текстов (только параметры перевода)\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 // Создаем запросы для каждой группы и сохраняем соответствие requestId -> groupItems\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 // Генерируем requestId включая отсортированные тексты группы\n // Включаем sourceLang для правильной группировки запросов с разными sourceLang\n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n \n // Сохраняем соответствие requestId -> groupItems\n requestIdToGroupItems.set(requestId, groupItems);\n \n return {\n requestId,\n texts,\n lang: firstItem.lang,\n sourceLang: firstItem.sourceLang,\n context: 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 // Создаем map для быстрого поиска результатов по requestId\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.requestId, { texts: result.data.texts, status: result.data.status });\n });\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 requestId: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n // Маппим тексты обратно на промисы по индексу\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for requestId ${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 requestId ${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 // При ошибке сети отклоняем все промисы\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 // Для отладки показываем временный requestId (будет пересчитан при группировке)\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","// 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 * as http2 from 'node:http2';\nimport { URL } from 'node:url';\nimport 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 BaseNodeTransportConfig {\n headers?: Record<string, string>;\n timeoutMs?: number;\n retries?: number;\n debug?: boolean;\n}\n\nconst sessionCache = new Map<string, http2.ClientHttp2Session>();\n\nlet cleanupTimer: NodeJS.Timeout | null = null;\n\nfunction cleanupDeadSessions(): void {\n for (const [url, session] of sessionCache.entries()) {\n if (session.destroyed || session.closed) {\n sessionCache.delete(url);\n }\n }\n}\n\nfunction startCleanupTimer(): void {\n if (cleanupTimer === null) {\n cleanupTimer = setInterval(cleanupDeadSessions, 60000);\n if (cleanupTimer.unref) {\n cleanupTimer.unref();\n }\n }\n}\n\nfunction getOrCreateSession(debug?: boolean): http2.ClientHttp2Session {\n cleanupDeadSessions();\n \n startCleanupTimer();\n \n if (sessionCache.has(BASE_URL)) {\n const session = sessionCache.get(BASE_URL)!;\n if (!session.destroyed && !session.closed) {\n return session;\n }\n sessionCache.delete(BASE_URL);\n }\n\n const parsedUrl = new URL(BASE_URL);\n const session = http2.connect(parsedUrl.origin);\n\n session.socket?.unref();\n \n session.on('error', () => {\n sessionCache.delete(BASE_URL);\n });\n \n session.on('close', () => {\n sessionCache.delete(BASE_URL);\n });\n \n session.on('goaway', () => {\n sessionCache.delete(BASE_URL);\n });\n\n if (debug) {\n session.on('connect', () => console.log('[Base Node Transport H2] new session connected'));\n session.on('goaway', (code, lastStreamID, opaque) =>\n console.log('[Base Node Transport H2] goaway', code, lastStreamID)\n );\n }\n \n sessionCache.set(BASE_URL, session);\n return session;\n}\n\nfunction makeHttp2Request(\n session: http2.ClientHttp2Session,\n path: string,\n headers: Record<string, string>,\n body: string,\n timeoutMs?: number\n): Promise<{ statusCode: number; headers: Record<string, string>; body: string }> {\n return new Promise((resolve, reject) => {\n const req = session.request({\n ':method': 'POST',\n ':path': path,\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(body),\n ...headers,\n });\n\n let timeout: NodeJS.Timeout | null = null;\n if (timeoutMs) {\n timeout = setTimeout(() => {\n req.destroy();\n reject(new Error(`Request timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n }\n\n let responseData = '';\n let statusCode = 0;\n let responseHeaders: Record<string, string> = {};\n\n req.on('response', (headers) => {\n statusCode = headers[':status'] as number;\n responseHeaders = headers as Record<string, string>;\n });\n\n req.on('data', (chunk) => {\n responseData += chunk;\n });\n\n req.on('end', () => {\n if (timeout) clearTimeout(timeout);\n resolve({\n statusCode,\n headers: responseHeaders,\n body: responseData,\n });\n });\n\n req.on('error', (error) => {\n if (timeout) clearTimeout(timeout);\n reject(error);\n });\n\n req.write(body);\n req.end();\n });\n}\n\nexport class BaseNodeTransport implements BaseTransport {\n constructor(private config: BaseNodeTransportConfig) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const maxRetries = this.config.retries || 0;\n let attempt = 0;\n const url = `${BASE_URL}${endpointPath}`;\n\n if (this.config.debug) {\n console.log(`[Base Node Transport] POST request to ${url}`, data);\n }\n\n while (attempt <= maxRetries) {\n try {\n const session = getOrCreateSession(this.config.debug);\n const body = JSON.stringify(data);\n \n // Add SDK headers\n const headers = {\n 'x-sdk': SDK_NAME,\n 'x-sdk-version': SDK_VERSION,\n ...this.config.headers,\n };\n\n const timeoutMs = this.config.timeoutMs ?? 10000; // Default 10 seconds\n const response = await makeHttp2Request(\n session,\n endpointPath,\n headers,\n body,\n timeoutMs\n );\n\n if (response.statusCode < 200 || response.statusCode >= 300) {\n const errorMsg = `HTTP ${response.statusCode}: Request failed`;\n if (this.config.debug) {\n console.error(`[Base Node Transport] Request failed:`, errorMsg);\n }\n throw new Error(errorMsg);\n }\n\n const result = JSON.parse(response.body);\n if (this.config.debug) {\n console.log(`[Base Node Transport] Request successful:`, result);\n }\n \n return result;\n } catch (error) {\n attempt++;\n if (this.config.debug) {\n console.error(`[Base Node Transport] Attempt ${attempt} failed:`, error instanceof Error ? error.message : String(error));\n }\n if (attempt > maxRetries) {\n throw error;\n }\n\n // Exponential backoff\n await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));\n }\n }\n\n // This should never be reached, but TypeScript requires it\n throw new Error('Max retries exceeded');\n }\n}\n\nexport function createBaseNodeTransport(config: BaseNodeTransportConfig): BaseTransport {\n const transport = new BaseNodeTransport(config);\n return new DedupeTransport(transport, config.debug);\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 } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { createBaseNodeTransport } from '../../transports/base/node';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\n\n/**\n * BeLocal translation engine for Node.js environments.\n * \n * Provides on-demand translation with automatic request batching, caching, and deduplication.\n * Optimized for Node.js environments using HTTP/2 with connection pooling and automatic retries.\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 {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n /**\n * Creates a new BelocalEngine instance.\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 const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n \n // Use local cache for Node.js\n this.cache = new LocalCache();\n \n if (this.debug) {\n console.log('[BeLocal Engine] Using local (memory) cache');\n }\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n // Create base node transport\n const baseTransport = createBaseNodeTransport({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n // Always use multi transport\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 in English), `cache_type` ('editable' | string)\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: 'editable' });\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` ('editable' | 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: 'editable' }\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 local 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 in local:', 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\n// Re-export types and transports\nexport type { BelocalEngineOptions, Lang, KV, BaseTransport } from '../types';\nexport { BaseNodeTransport, createBaseNodeTransport } from '../../transports/base';\nexport { createMultiTransport } from '../../transports/multi';\n"]}
1
+ {"version":3,"sources":["../src/transports/multi.ts","../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/node.ts","../src/cache/local.ts","../src/core/engine/node.ts"],"names":["md5","session","URL","http2","headers"],"mappings":"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","file":"node.cjs","sourcesContent":["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 requestId: string;\n texts: string[];\n lang: string;\n sourceLang?: string;\n context?: 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 requestId: 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 // Сортируем тексты (создаем копию, чтобы не изменять оригинал)\n const sortedTexts = [...texts].sort();\n \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 // Создаем JSON массив: [sortedTexts, lang, sourceLang, context]\n // В PHP примере: [$sortedTexts, $this->lang, $context] (sourceLang не включен)\n // Но мы включаем sourceLang для правильной группировки запросов с разными sourceLang\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 // Группируем тексты по параметрам перевода (lang, sourceLang, context)\n const groups = new Map<string, MultiRequestItem[]>();\n \n items.forEach(item => {\n // Используем ключ для группировки без текстов (только параметры перевода)\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 // Создаем запросы для каждой группы и сохраняем соответствие requestId -> groupItems\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 // Генерируем requestId включая отсортированные тексты группы\n // Включаем sourceLang для правильной группировки запросов с разными sourceLang\n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n \n // Сохраняем соответствие requestId -> groupItems\n requestIdToGroupItems.set(requestId, groupItems);\n \n return {\n requestId,\n texts,\n lang: firstItem.lang,\n sourceLang: firstItem.sourceLang,\n context: 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 // Создаем map для быстрого поиска результатов по requestId\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.requestId, { texts: result.data.texts, status: result.data.status });\n });\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 requestId: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n // Маппим тексты обратно на промисы по индексу\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for requestId ${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 requestId ${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 // При ошибке сети отклоняем все промисы\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 // Для отладки показываем временный requestId (будет пересчитан при группировке)\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","// 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 * as http2 from 'node:http2';\nimport { URL } from 'node:url';\nimport 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 BaseNodeTransportConfig {\n headers?: Record<string, string>;\n timeoutMs?: number;\n retries?: number;\n debug?: boolean;\n}\n\nconst sessionCache = new Map<string, http2.ClientHttp2Session>();\n\nlet cleanupTimer: NodeJS.Timeout | null = null;\n\nfunction cleanupDeadSessions(): void {\n for (const [url, session] of sessionCache.entries()) {\n if (session.destroyed || session.closed) {\n sessionCache.delete(url);\n }\n }\n}\n\nfunction startCleanupTimer(): void {\n if (cleanupTimer === null) {\n cleanupTimer = setInterval(cleanupDeadSessions, 60000);\n if (cleanupTimer.unref) {\n cleanupTimer.unref();\n }\n }\n}\n\nfunction getOrCreateSession(debug?: boolean): http2.ClientHttp2Session {\n cleanupDeadSessions();\n \n startCleanupTimer();\n \n if (sessionCache.has(BASE_URL)) {\n const session = sessionCache.get(BASE_URL)!;\n if (!session.destroyed && !session.closed) {\n return session;\n }\n sessionCache.delete(BASE_URL);\n }\n\n const parsedUrl = new URL(BASE_URL);\n const session = http2.connect(parsedUrl.origin);\n\n session.socket?.unref();\n \n session.on('error', () => {\n sessionCache.delete(BASE_URL);\n });\n \n session.on('close', () => {\n sessionCache.delete(BASE_URL);\n });\n \n session.on('goaway', () => {\n sessionCache.delete(BASE_URL);\n });\n\n if (debug) {\n session.on('connect', () => console.log('[Base Node Transport H2] new session connected'));\n session.on('goaway', (code, lastStreamID, opaque) =>\n console.log('[Base Node Transport H2] goaway', code, lastStreamID)\n );\n }\n \n sessionCache.set(BASE_URL, session);\n return session;\n}\n\nfunction makeHttp2Request(\n session: http2.ClientHttp2Session,\n path: string,\n headers: Record<string, string>,\n body: string,\n timeoutMs?: number\n): Promise<{ statusCode: number; headers: Record<string, string>; body: string }> {\n return new Promise((resolve, reject) => {\n const req = session.request({\n ':method': 'POST',\n ':path': path,\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(body),\n ...headers,\n });\n\n let timeout: NodeJS.Timeout | null = null;\n if (timeoutMs) {\n timeout = setTimeout(() => {\n req.destroy();\n reject(new Error(`Request timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n }\n\n let responseData = '';\n let statusCode = 0;\n let responseHeaders: Record<string, string> = {};\n\n req.on('response', (headers) => {\n statusCode = headers[':status'] as number;\n responseHeaders = headers as Record<string, string>;\n });\n\n req.on('data', (chunk) => {\n responseData += chunk;\n });\n\n req.on('end', () => {\n if (timeout) clearTimeout(timeout);\n resolve({\n statusCode,\n headers: responseHeaders,\n body: responseData,\n });\n });\n\n req.on('error', (error) => {\n if (timeout) clearTimeout(timeout);\n reject(error);\n });\n\n req.write(body);\n req.end();\n });\n}\n\nexport class BaseNodeTransport implements BaseTransport {\n constructor(private config: BaseNodeTransportConfig) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const maxRetries = this.config.retries || 0;\n let attempt = 0;\n const url = `${BASE_URL}${endpointPath}`;\n\n if (this.config.debug) {\n console.log(`[Base Node Transport] POST request to ${url}`, data);\n }\n\n while (attempt <= maxRetries) {\n try {\n const session = getOrCreateSession(this.config.debug);\n const body = JSON.stringify(data);\n \n // Add SDK headers\n const headers = {\n 'x-sdk': SDK_NAME,\n 'x-sdk-version': SDK_VERSION,\n ...this.config.headers,\n };\n\n const timeoutMs = this.config.timeoutMs ?? 10000; // Default 10 seconds\n const response = await makeHttp2Request(\n session,\n endpointPath,\n headers,\n body,\n timeoutMs\n );\n\n if (response.statusCode < 200 || response.statusCode >= 300) {\n const errorMsg = `HTTP ${response.statusCode}: Request failed`;\n if (this.config.debug) {\n console.error(`[Base Node Transport] Request failed:`, errorMsg);\n }\n throw new Error(errorMsg);\n }\n\n const result = JSON.parse(response.body);\n if (this.config.debug) {\n console.log(`[Base Node Transport] Request successful:`, result);\n }\n \n return result;\n } catch (error) {\n attempt++;\n if (this.config.debug) {\n console.error(`[Base Node Transport] Attempt ${attempt} failed:`, error instanceof Error ? error.message : String(error));\n }\n if (attempt > maxRetries) {\n throw error;\n }\n\n // Exponential backoff\n await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));\n }\n }\n\n // This should never be reached, but TypeScript requires it\n throw new Error('Max retries exceeded');\n }\n}\n\nexport function createBaseNodeTransport(config: BaseNodeTransportConfig): BaseTransport {\n const transport = new BaseNodeTransport(config);\n return new DedupeTransport(transport, config.debug);\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 } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { createBaseNodeTransport } from '../../transports/base/node';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\n\n/**\n * BeLocal translation engine for Node.js environments.\n * \n * Provides on-demand translation with automatic request batching, caching, and deduplication.\n * Optimized for Node.js environments using HTTP/2 with connection pooling and automatic retries.\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 {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n /**\n * Creates a new BelocalEngine instance.\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 const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n \n // Use local cache for Node.js\n this.cache = new LocalCache();\n \n if (this.debug) {\n console.log('[BeLocal Engine] Using local (memory) cache');\n }\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n // Create base node transport\n const baseTransport = createBaseNodeTransport({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n // Always use multi transport\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 in English), `cache_type` ('managed' | string)\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 local 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 in local:', 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\n// Re-export types and transports\nexport type { BelocalEngineOptions, Lang, KV, BaseTransport } from '../types';\nexport { BaseNodeTransport, createBaseNodeTransport } from '../../transports/base';\nexport { createMultiTransport } from '../../transports/multi';\n"]}
package/dist/node.d.ts CHANGED
@@ -7,7 +7,7 @@ type Lang = string;
7
7
  * Key-value pairs for translation context.
8
8
  * Supported keys:
9
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., 'editable'
10
+ * - `cache_type` (string): Cache type, e.g., 'managed' (managed translations cache)
11
11
  *
12
12
  * Other keys will be ignored by the API.
13
13
  */
@@ -112,7 +112,7 @@ declare class BelocalEngine {
112
112
  * @param text - The text to translate
113
113
  * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
114
114
  * @param source_lang - Optional source language code. If not provided, auto-detection is used
115
- * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('editable' | string)
115
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
116
116
  * @returns Promise resolving to the translated text
117
117
  * @throws {Error} If the translation request fails (network error, API error, timeout)
118
118
  *
@@ -130,7 +130,7 @@ declare class BelocalEngine {
130
130
  * });
131
131
  *
132
132
  * // With cache_type
133
- * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'editable' });
133
+ * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'managed' });
134
134
  *
135
135
  * // With source language and context
136
136
  * const result = await engine.translate('Hello world', 'es', 'en', {
@@ -151,7 +151,7 @@ declare class BelocalEngine {
151
151
  * @param texts - Array of texts to translate
152
152
  * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
153
153
  * @param source_lang - Optional source language code. If not provided, auto-detection is used
154
- * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('editable' | string)
154
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
155
155
  * @returns Promise resolving to an array of translated texts in the same order as input
156
156
  * @throws {Error} If the translation request fails (network error, API error, timeout)
157
157
  *
@@ -177,7 +177,7 @@ declare class BelocalEngine {
177
177
  * ['Hello', 'World'],
178
178
  * 'es',
179
179
  * undefined,
180
- * { cache_type: 'editable' }
180
+ * { cache_type: 'managed' }
181
181
  * );
182
182
  *
183
183
  * // Empty array returns empty array
package/dist/node.mjs CHANGED
@@ -146,7 +146,7 @@ function createMultiTransport(config) {
146
146
  // src/version.ts
147
147
  var SDK_VERSION = (() => {
148
148
  try {
149
- return true ? "0.6.1" : "undefined";
149
+ return true ? "0.7.0" : "undefined";
150
150
  } catch {
151
151
  return "undefined";
152
152
  }
@@ -426,7 +426,7 @@ var BelocalEngine = class {
426
426
  * @param text - The text to translate
427
427
  * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
428
428
  * @param source_lang - Optional source language code. If not provided, auto-detection is used
429
- * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('editable' | string)
429
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
430
430
  * @returns Promise resolving to the translated text
431
431
  * @throws {Error} If the translation request fails (network error, API error, timeout)
432
432
  *
@@ -444,7 +444,7 @@ var BelocalEngine = class {
444
444
  * });
445
445
  *
446
446
  * // With cache_type
447
- * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'editable' });
447
+ * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'managed' });
448
448
  *
449
449
  * // With source language and context
450
450
  * const result = await engine.translate('Hello world', 'es', 'en', {
@@ -468,7 +468,7 @@ var BelocalEngine = class {
468
468
  * @param texts - Array of texts to translate
469
469
  * @param lang - Target language code (e.g., 'es', 'fr', 'ru')
470
470
  * @param source_lang - Optional source language code. If not provided, auto-detection is used
471
- * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('editable' | string)
471
+ * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
472
472
  * @returns Promise resolving to an array of translated texts in the same order as input
473
473
  * @throws {Error} If the translation request fails (network error, API error, timeout)
474
474
  *
@@ -494,7 +494,7 @@ var BelocalEngine = class {
494
494
  * ['Hello', 'World'],
495
495
  * 'es',
496
496
  * undefined,
497
- * { cache_type: 'editable' }
497
+ * { cache_type: 'managed' }
498
498
  * );
499
499
  *
500
500
  * // Empty array returns empty array
package/dist/node.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transports/multi.ts","../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/node.ts","../src/cache/local.ts","../src/core/engine/node.ts"],"names":["session","headers","md5"],"mappings":"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","file":"node.mjs","sourcesContent":["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 requestId: string;\n texts: string[];\n lang: string;\n sourceLang?: string;\n context?: 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 requestId: 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 // Сортируем тексты (создаем копию, чтобы не изменять оригинал)\n const sortedTexts = [...texts].sort();\n \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 // Создаем JSON массив: [sortedTexts, lang, sourceLang, context]\n // В PHP примере: [$sortedTexts, $this->lang, $context] (sourceLang не включен)\n // Но мы включаем sourceLang для правильной группировки запросов с разными sourceLang\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 // Группируем тексты по параметрам перевода (lang, sourceLang, context)\n const groups = new Map<string, MultiRequestItem[]>();\n \n items.forEach(item => {\n // Используем ключ для группировки без текстов (только параметры перевода)\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 // Создаем запросы для каждой группы и сохраняем соответствие requestId -> groupItems\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 // Генерируем requestId включая отсортированные тексты группы\n // Включаем sourceLang для правильной группировки запросов с разными sourceLang\n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n \n // Сохраняем соответствие requestId -> groupItems\n requestIdToGroupItems.set(requestId, groupItems);\n \n return {\n requestId,\n texts,\n lang: firstItem.lang,\n sourceLang: firstItem.sourceLang,\n context: 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 // Создаем map для быстрого поиска результатов по requestId\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.requestId, { texts: result.data.texts, status: result.data.status });\n });\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 requestId: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n // Маппим тексты обратно на промисы по индексу\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for requestId ${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 requestId ${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 // При ошибке сети отклоняем все промисы\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 // Для отладки показываем временный requestId (будет пересчитан при группировке)\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","// 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 * as http2 from 'node:http2';\nimport { URL } from 'node:url';\nimport 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 BaseNodeTransportConfig {\n headers?: Record<string, string>;\n timeoutMs?: number;\n retries?: number;\n debug?: boolean;\n}\n\nconst sessionCache = new Map<string, http2.ClientHttp2Session>();\n\nlet cleanupTimer: NodeJS.Timeout | null = null;\n\nfunction cleanupDeadSessions(): void {\n for (const [url, session] of sessionCache.entries()) {\n if (session.destroyed || session.closed) {\n sessionCache.delete(url);\n }\n }\n}\n\nfunction startCleanupTimer(): void {\n if (cleanupTimer === null) {\n cleanupTimer = setInterval(cleanupDeadSessions, 60000);\n if (cleanupTimer.unref) {\n cleanupTimer.unref();\n }\n }\n}\n\nfunction getOrCreateSession(debug?: boolean): http2.ClientHttp2Session {\n cleanupDeadSessions();\n \n startCleanupTimer();\n \n if (sessionCache.has(BASE_URL)) {\n const session = sessionCache.get(BASE_URL)!;\n if (!session.destroyed && !session.closed) {\n return session;\n }\n sessionCache.delete(BASE_URL);\n }\n\n const parsedUrl = new URL(BASE_URL);\n const session = http2.connect(parsedUrl.origin);\n\n session.socket?.unref();\n \n session.on('error', () => {\n sessionCache.delete(BASE_URL);\n });\n \n session.on('close', () => {\n sessionCache.delete(BASE_URL);\n });\n \n session.on('goaway', () => {\n sessionCache.delete(BASE_URL);\n });\n\n if (debug) {\n session.on('connect', () => console.log('[Base Node Transport H2] new session connected'));\n session.on('goaway', (code, lastStreamID, opaque) =>\n console.log('[Base Node Transport H2] goaway', code, lastStreamID)\n );\n }\n \n sessionCache.set(BASE_URL, session);\n return session;\n}\n\nfunction makeHttp2Request(\n session: http2.ClientHttp2Session,\n path: string,\n headers: Record<string, string>,\n body: string,\n timeoutMs?: number\n): Promise<{ statusCode: number; headers: Record<string, string>; body: string }> {\n return new Promise((resolve, reject) => {\n const req = session.request({\n ':method': 'POST',\n ':path': path,\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(body),\n ...headers,\n });\n\n let timeout: NodeJS.Timeout | null = null;\n if (timeoutMs) {\n timeout = setTimeout(() => {\n req.destroy();\n reject(new Error(`Request timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n }\n\n let responseData = '';\n let statusCode = 0;\n let responseHeaders: Record<string, string> = {};\n\n req.on('response', (headers) => {\n statusCode = headers[':status'] as number;\n responseHeaders = headers as Record<string, string>;\n });\n\n req.on('data', (chunk) => {\n responseData += chunk;\n });\n\n req.on('end', () => {\n if (timeout) clearTimeout(timeout);\n resolve({\n statusCode,\n headers: responseHeaders,\n body: responseData,\n });\n });\n\n req.on('error', (error) => {\n if (timeout) clearTimeout(timeout);\n reject(error);\n });\n\n req.write(body);\n req.end();\n });\n}\n\nexport class BaseNodeTransport implements BaseTransport {\n constructor(private config: BaseNodeTransportConfig) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const maxRetries = this.config.retries || 0;\n let attempt = 0;\n const url = `${BASE_URL}${endpointPath}`;\n\n if (this.config.debug) {\n console.log(`[Base Node Transport] POST request to ${url}`, data);\n }\n\n while (attempt <= maxRetries) {\n try {\n const session = getOrCreateSession(this.config.debug);\n const body = JSON.stringify(data);\n \n // Add SDK headers\n const headers = {\n 'x-sdk': SDK_NAME,\n 'x-sdk-version': SDK_VERSION,\n ...this.config.headers,\n };\n\n const timeoutMs = this.config.timeoutMs ?? 10000; // Default 10 seconds\n const response = await makeHttp2Request(\n session,\n endpointPath,\n headers,\n body,\n timeoutMs\n );\n\n if (response.statusCode < 200 || response.statusCode >= 300) {\n const errorMsg = `HTTP ${response.statusCode}: Request failed`;\n if (this.config.debug) {\n console.error(`[Base Node Transport] Request failed:`, errorMsg);\n }\n throw new Error(errorMsg);\n }\n\n const result = JSON.parse(response.body);\n if (this.config.debug) {\n console.log(`[Base Node Transport] Request successful:`, result);\n }\n \n return result;\n } catch (error) {\n attempt++;\n if (this.config.debug) {\n console.error(`[Base Node Transport] Attempt ${attempt} failed:`, error instanceof Error ? error.message : String(error));\n }\n if (attempt > maxRetries) {\n throw error;\n }\n\n // Exponential backoff\n await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));\n }\n }\n\n // This should never be reached, but TypeScript requires it\n throw new Error('Max retries exceeded');\n }\n}\n\nexport function createBaseNodeTransport(config: BaseNodeTransportConfig): BaseTransport {\n const transport = new BaseNodeTransport(config);\n return new DedupeTransport(transport, config.debug);\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 } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { createBaseNodeTransport } from '../../transports/base/node';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\n\n/**\n * BeLocal translation engine for Node.js environments.\n * \n * Provides on-demand translation with automatic request batching, caching, and deduplication.\n * Optimized for Node.js environments using HTTP/2 with connection pooling and automatic retries.\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 {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n /**\n * Creates a new BelocalEngine instance.\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 const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n \n // Use local cache for Node.js\n this.cache = new LocalCache();\n \n if (this.debug) {\n console.log('[BeLocal Engine] Using local (memory) cache');\n }\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n // Create base node transport\n const baseTransport = createBaseNodeTransport({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n // Always use multi transport\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 in English), `cache_type` ('editable' | string)\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: 'editable' });\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` ('editable' | 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: 'editable' }\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 local 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 in local:', 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\n// Re-export types and transports\nexport type { BelocalEngineOptions, Lang, KV, BaseTransport } from '../types';\nexport { BaseNodeTransport, createBaseNodeTransport } from '../../transports/base';\nexport { createMultiTransport } from '../../transports/multi';\n"]}
1
+ {"version":3,"sources":["../src/transports/multi.ts","../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/node.ts","../src/cache/local.ts","../src/core/engine/node.ts"],"names":["session","headers","md5"],"mappings":"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","file":"node.mjs","sourcesContent":["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 requestId: string;\n texts: string[];\n lang: string;\n sourceLang?: string;\n context?: 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 requestId: 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 // Сортируем тексты (создаем копию, чтобы не изменять оригинал)\n const sortedTexts = [...texts].sort();\n \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 // Создаем JSON массив: [sortedTexts, lang, sourceLang, context]\n // В PHP примере: [$sortedTexts, $this->lang, $context] (sourceLang не включен)\n // Но мы включаем sourceLang для правильной группировки запросов с разными sourceLang\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 // Группируем тексты по параметрам перевода (lang, sourceLang, context)\n const groups = new Map<string, MultiRequestItem[]>();\n \n items.forEach(item => {\n // Используем ключ для группировки без текстов (только параметры перевода)\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 // Создаем запросы для каждой группы и сохраняем соответствие requestId -> groupItems\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 // Генерируем requestId включая отсортированные тексты группы\n // Включаем sourceLang для правильной группировки запросов с разными sourceLang\n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n \n // Сохраняем соответствие requestId -> groupItems\n requestIdToGroupItems.set(requestId, groupItems);\n \n return {\n requestId,\n texts,\n lang: firstItem.lang,\n sourceLang: firstItem.sourceLang,\n context: 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 // Создаем map для быстрого поиска результатов по requestId\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.requestId, { texts: result.data.texts, status: result.data.status });\n });\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 requestId: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n // Маппим тексты обратно на промисы по индексу\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for requestId ${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 requestId ${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 // При ошибке сети отклоняем все промисы\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 // Для отладки показываем временный requestId (будет пересчитан при группировке)\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","// 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 * as http2 from 'node:http2';\nimport { URL } from 'node:url';\nimport 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 BaseNodeTransportConfig {\n headers?: Record<string, string>;\n timeoutMs?: number;\n retries?: number;\n debug?: boolean;\n}\n\nconst sessionCache = new Map<string, http2.ClientHttp2Session>();\n\nlet cleanupTimer: NodeJS.Timeout | null = null;\n\nfunction cleanupDeadSessions(): void {\n for (const [url, session] of sessionCache.entries()) {\n if (session.destroyed || session.closed) {\n sessionCache.delete(url);\n }\n }\n}\n\nfunction startCleanupTimer(): void {\n if (cleanupTimer === null) {\n cleanupTimer = setInterval(cleanupDeadSessions, 60000);\n if (cleanupTimer.unref) {\n cleanupTimer.unref();\n }\n }\n}\n\nfunction getOrCreateSession(debug?: boolean): http2.ClientHttp2Session {\n cleanupDeadSessions();\n \n startCleanupTimer();\n \n if (sessionCache.has(BASE_URL)) {\n const session = sessionCache.get(BASE_URL)!;\n if (!session.destroyed && !session.closed) {\n return session;\n }\n sessionCache.delete(BASE_URL);\n }\n\n const parsedUrl = new URL(BASE_URL);\n const session = http2.connect(parsedUrl.origin);\n\n session.socket?.unref();\n \n session.on('error', () => {\n sessionCache.delete(BASE_URL);\n });\n \n session.on('close', () => {\n sessionCache.delete(BASE_URL);\n });\n \n session.on('goaway', () => {\n sessionCache.delete(BASE_URL);\n });\n\n if (debug) {\n session.on('connect', () => console.log('[Base Node Transport H2] new session connected'));\n session.on('goaway', (code, lastStreamID, opaque) =>\n console.log('[Base Node Transport H2] goaway', code, lastStreamID)\n );\n }\n \n sessionCache.set(BASE_URL, session);\n return session;\n}\n\nfunction makeHttp2Request(\n session: http2.ClientHttp2Session,\n path: string,\n headers: Record<string, string>,\n body: string,\n timeoutMs?: number\n): Promise<{ statusCode: number; headers: Record<string, string>; body: string }> {\n return new Promise((resolve, reject) => {\n const req = session.request({\n ':method': 'POST',\n ':path': path,\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(body),\n ...headers,\n });\n\n let timeout: NodeJS.Timeout | null = null;\n if (timeoutMs) {\n timeout = setTimeout(() => {\n req.destroy();\n reject(new Error(`Request timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n }\n\n let responseData = '';\n let statusCode = 0;\n let responseHeaders: Record<string, string> = {};\n\n req.on('response', (headers) => {\n statusCode = headers[':status'] as number;\n responseHeaders = headers as Record<string, string>;\n });\n\n req.on('data', (chunk) => {\n responseData += chunk;\n });\n\n req.on('end', () => {\n if (timeout) clearTimeout(timeout);\n resolve({\n statusCode,\n headers: responseHeaders,\n body: responseData,\n });\n });\n\n req.on('error', (error) => {\n if (timeout) clearTimeout(timeout);\n reject(error);\n });\n\n req.write(body);\n req.end();\n });\n}\n\nexport class BaseNodeTransport implements BaseTransport {\n constructor(private config: BaseNodeTransportConfig) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const maxRetries = this.config.retries || 0;\n let attempt = 0;\n const url = `${BASE_URL}${endpointPath}`;\n\n if (this.config.debug) {\n console.log(`[Base Node Transport] POST request to ${url}`, data);\n }\n\n while (attempt <= maxRetries) {\n try {\n const session = getOrCreateSession(this.config.debug);\n const body = JSON.stringify(data);\n \n // Add SDK headers\n const headers = {\n 'x-sdk': SDK_NAME,\n 'x-sdk-version': SDK_VERSION,\n ...this.config.headers,\n };\n\n const timeoutMs = this.config.timeoutMs ?? 10000; // Default 10 seconds\n const response = await makeHttp2Request(\n session,\n endpointPath,\n headers,\n body,\n timeoutMs\n );\n\n if (response.statusCode < 200 || response.statusCode >= 300) {\n const errorMsg = `HTTP ${response.statusCode}: Request failed`;\n if (this.config.debug) {\n console.error(`[Base Node Transport] Request failed:`, errorMsg);\n }\n throw new Error(errorMsg);\n }\n\n const result = JSON.parse(response.body);\n if (this.config.debug) {\n console.log(`[Base Node Transport] Request successful:`, result);\n }\n \n return result;\n } catch (error) {\n attempt++;\n if (this.config.debug) {\n console.error(`[Base Node Transport] Attempt ${attempt} failed:`, error instanceof Error ? error.message : String(error));\n }\n if (attempt > maxRetries) {\n throw error;\n }\n\n // Exponential backoff\n await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));\n }\n }\n\n // This should never be reached, but TypeScript requires it\n throw new Error('Max retries exceeded');\n }\n}\n\nexport function createBaseNodeTransport(config: BaseNodeTransportConfig): BaseTransport {\n const transport = new BaseNodeTransport(config);\n return new DedupeTransport(transport, config.debug);\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 } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { createBaseNodeTransport } from '../../transports/base/node';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\n\n/**\n * BeLocal translation engine for Node.js environments.\n * \n * Provides on-demand translation with automatic request batching, caching, and deduplication.\n * Optimized for Node.js environments using HTTP/2 with connection pooling and automatic retries.\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 {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n /**\n * Creates a new BelocalEngine instance.\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 const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n \n // Use local cache for Node.js\n this.cache = new LocalCache();\n \n if (this.debug) {\n console.log('[BeLocal Engine] Using local (memory) cache');\n }\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n // Create base node transport\n const baseTransport = createBaseNodeTransport({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n // Always use multi transport\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 in English), `cache_type` ('managed' | string)\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 local 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 in local:', 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\n// Re-export types and transports\nexport type { BelocalEngineOptions, Lang, KV, BaseTransport } from '../types';\nexport { BaseNodeTransport, createBaseNodeTransport } from '../../transports/base';\nexport { createMultiTransport } from '../../transports/multi';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@belocal/js-sdk",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "BeLocal runtime JS SDK for on-demand translation (browser + node)",
5
5
  "scripts": {
6
6
  "test": "vitest run",