@belocal/js-sdk 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -40
- package/dist/browser.cjs +2 -2
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +31 -177
- package/dist/browser.mjs +2 -2
- package/dist/browser.mjs.map +1 -1
- package/dist/node.cjs +97 -170
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +32 -177
- package/dist/node.mjs +96 -171
- package/dist/node.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {md5}from'js-md5';var
|
|
2
|
-
export{
|
|
1
|
+
import {md5}from'js-md5';var M="1.0.3";function E(a,t){let e=s=>{if(s==null)return s;if(Array.isArray(s))return s.map(e);if(typeof s=="object"){let i={};for(let u of Object.keys(s).sort())i[u]=e(s[u]);return i}return s},r=JSON.stringify(e(t));return `${a}:${r}`}var m=class{constructor(t,e){this.wrappedTransport=t;this.debug=e;this.inFlightRequests=new Map;}async post(t,e){let r=E(e,t),s=this.inFlightRequests.get(r);if(s)return this.debug&&console.log(`[DedupeTransport] Deduplicating request to ${e}`),s;let i=this.wrappedTransport.post(t,e).finally(()=>{this.inFlightRequests.delete(r);});return this.inFlightRequests.set(r,i),this.debug&&console.log(`[DedupeTransport] New request to ${e} (${this.inFlightRequests.size} in-flight)`),i}};var R="https://dynamic.belocal.dev",d=class{constructor(t){this.config=t;}async post(t,e){let r=`${R}${e}`,s=new AbortController,i=this.config.timeoutMs??1e4,u=setTimeout(()=>s.abort(),i);this.config.debug&&console.log(`[Base Browser Transport] POST request to ${r}`,t);try{let g=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json","x-sdk":"js","x-sdk-version":M,...this.config.headers},body:JSON.stringify(t),signal:s.signal});if(!g.ok){let o=`HTTP ${g.status}: ${g.statusText}`;throw this.config.debug&&console.error("[Base Browser Transport] Request failed:",o),new Error(o)}let n=await g.json();return this.config.debug&&console.log("[Base Browser Transport] Request successful:",n),n}finally{u&&clearTimeout(u);}}};function h(a){let t=new d(a);return new m(t,a.debug)}function q(a,t,e,r){let s=[...a].sort(),i=r&&Object.keys(r).length>0?Object.fromEntries(Object.entries(r).sort(([g],[n])=>g.localeCompare(n))):null;return md5(JSON.stringify([s,t,e||null,i]))}async function S(a,t,e){a.debug&&console.log(`[BeLocal Multi Transport] Sending multi request with ${t.length} texts`);try{let r=new Map;t.forEach(n=>{let o=JSON.stringify({lang:n.lang,sourceLang:n.sourceLang||null,context:n.context||null});r.has(o)||r.set(o,[]),r.get(o).push(n);});let s=new Map,i=Array.from(r.entries()).map(([n,o])=>{let c=o[0],l=o.map(f=>f.text),p=q(l,c.lang,c.sourceLang,c.context);return s.set(p,o),{request_id:p,texts:l,lang:c.lang,source_lang:c.sourceLang,ctx:c.context}}),u=await a.baseTransport.post({requests:i},"/v1/translate/multi");a.debug&&console.log(`[BeLocal Multi Transport] Multi response received with ${u.results.length} groups`);let g=new Map;u.results.forEach(n=>{g.set(n.request_id,{texts:n.data.texts,status:n.data.status});}),s.forEach((n,o)=>{let c=g.get(o);if(!c){a.debug&&console.error(`[BeLocal Multi Transport] No result found for request_id: ${o}`),n.forEach(l=>{l.reject(new Error(`No result found for request ${o}`));});return}if(c.texts.length!==n.length){let l=new Error(`Mismatch: expected ${n.length} texts, got ${c.texts.length} for request_id ${o}`);a.debug&&console.error("[BeLocal Multi Transport]",l.message),n.forEach(p=>p.reject(l));return}n.forEach((l,p)=>{let f=c.texts[p],_=c.status||"success";a.debug&&console.log(`[BeLocal Multi Transport] Success for request_id ${o}[${p}]: "${f}"`),l.resolve({text:f,status:_});});});}catch(r){a.debug&&console.error("[BeLocal Multi Transport] Multi request error:",r);let s=r instanceof Error?r:new Error(String(r));t.forEach(i=>i.reject(s));}}function B(a,t){if(t.currentMulti.length===0||t.isRequestInFlight)return;let e=[...t.currentMulti];t.currentMulti=[],t.multiTimer=null,t.isRequestInFlight=true,S(a,e).finally(()=>{if(t.isRequestInFlight=false,t.currentMulti.length>0){let r=a.batchWindowMs??50;t.multiTimer=setTimeout(()=>B(a,t),r);}});}function x(a){let t=a.batchWindowMs??50,e={currentMulti:[],multiTimer:null,isRequestInFlight:false},r=(({text:s,lang:i,source_lang:u,ctx:g})=>new Promise((n,o)=>{if(a.debug){let l=md5(JSON.stringify([s,i,u||null,g||null]));console.log(`[BeLocal Multi Transport] Queuing request ${l}: "${s}" to ${i}`);}let c={text:s,lang:i,sourceLang:u,context:g,resolve:n,reject:o};e.currentMulti.push(c),e.multiTimer===null&&!e.isRequestInFlight&&(e.multiTimer=setTimeout(()=>B(a,e),t));}));return r.destroy=()=>{e.multiTimer&&(clearTimeout(e.multiTimer),e.multiTimer=null),e.currentMulti.forEach(s=>s.reject(new Error("Transport destroyed"))),e.currentMulti=[];},r}var T=class{constructor(t={}){this.storage=new Map;this.maxSize=t.maxSize??1e4,this.ttlMs=t.ttlMs??36e5;}get(t){let e=this.storage.get(t);return e===void 0?null:Date.now()>e.expiresAt?(this.storage.delete(t),null):(this.storage.delete(t),this.storage.set(t,e),e.value)}set(t,e){if(this.storage.delete(t),this.storage.size>=this.maxSize){let r=this.storage.keys().next().value;r!==void 0&&this.storage.delete(r);}this.storage.set(t,{value:e,expiresAt:Date.now()+this.ttlMs});}clear(){this.storage.clear();}};var y=class{constructor(t,e){let{apiKey:r,batchWindowMs:s=50,timeoutMs:i=1e4,debug:u=false,cacheTtlMs:g=36e5,cacheMaxSize:n=1e4}=t;if(!r||typeof r!="string")throw new Error("[BeLocal] apiKey is required and must be a non-empty string");if(typeof i!="number"||!Number.isFinite(i)||i<=0)throw new Error("[BeLocal] timeoutMs must be a positive finite number");let o=Math.max(0,s);this.debug=u,this.cache=new T({ttlMs:g,maxSize:n});let c={Authorization:`Bearer ${r}`},l=e({headers:c,timeoutMs:i,debug:this.debug});this.transport=x({baseTransport:l,debug:this.debug,batchWindowMs:o}),this.debug&&console.log("[BeLocal Engine] Multi transport created with config:",{baseUrl:"https://dynamic.belocal.dev",timeoutMs:i,batchWindowMs:o});}async translate(t,e,r){return (await this.translateMany([t],e,r))[0]}async translateMany(t,e,r){let{source_lang:s,ctx:i}=this.resolveOptions(r),u=new Array(t.length),g=[];for(let n=0;n<t.length;n++){let o=t[n],c=this.generateCacheKey(o,e,s,i),l=this.cache.get(c);if(l!==null){u[n]=l,this.debug&&console.log("[BeLocal Engine] Translation from cache:",o);continue}u[n]=null,g.push({index:n,text:o});}return g.length>0&&(await Promise.allSettled(g.map(async({index:o,text:c})=>{let l=await this.transport({text:c,lang:e,source_lang:s,ctx:i});if(l.status!=="error"){let p=this.generateCacheKey(c,e,s,i);this.cache.set(p,l.text),this.debug&&console.log("[BeLocal Engine] Translation from API, cached:",c);}else this.debug&&console.log("[BeLocal Engine] Translation from API (not cached due to error status):",c);return {index:o,translation:l.text}}))).forEach((o,c)=>{let{index:l,text:p}=g[c];if(o.status==="fulfilled")u[l]=o.value.translation;else if(u[l]=p,this.debug){let f=o.reason;console.error("[BeLocal Engine] Translation failed, using original text:",p,f instanceof Error?f.message:String(f));}}),u}resolveOptions(t){if(!t)return {};let e={...t.ctx,...t.context&&{user_ctx:t.context},...t.managed&&{cache_type:"managed"}},r=Object.values(e).some(Boolean)?e:void 0;return {source_lang:t.source_lang,ctx:r}}destroy(){this.transport.destroy?.(),this.cache.clear();}generateCacheKey(t,e,r,s){let i=s?Object.fromEntries(Object.entries(s).sort(([g],[n])=>g.localeCompare(n))):null;return md5(JSON.stringify({text:t,lang:e,source_lang:r||null,ctx:i}))}};var v="product",L="chat";var w=class extends y{constructor(t){super(t,h);}};
|
|
2
|
+
export{d as BaseBrowserTransport,w as BelocalEngine,L as USER_TYPE_CHAT,v as USER_TYPE_PRODUCT,h as createBaseBrowserTransport,x as createMultiTransport};//# sourceMappingURL=browser.mjs.map
|
|
3
3
|
//# sourceMappingURL=browser.mjs.map
|
package/dist/browser.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/browser.ts","../src/transports/multi.ts","../src/cache/local.ts","../src/core/engine/engine.ts","../src/core/engine/browser.ts"],"names":["SDK_VERSION","SDK_NAME","generateRequestKey","endpointPath","data","normalize","value","sorted","key","normalizedData","DedupeTransport","wrappedTransport","debug","requestKey","existingRequest","requestPromise","BASE_URL","BaseBrowserTransport","config","url","controller","timeoutMs","timeoutId","response","errorMsg","result","createBaseBrowserTransport","transport","generateRequestId","texts","lang","sourceLang","context","sortedTexts","sortedContext","sortedKeys","json","md5","sendMulti","items","state","groups","item","groupKey","requestIdToGroupItems","requests","groupItems","firstItem","requestId","multiResponse","resultMap","error","index","translatedText","status","errorToReject","processMulti","itemsToSend","windowMs","createMultiTransport","text","source_lang","ctx","resolve","reject","tempRequestId","requestItem","LocalCache","BelocalEngine","options","baseTransportFactory","apiKey","batchWindowMs","authHeaders","baseTransport","results","cacheMisses","i","cacheKey","cachedResult","translation","sortedCtx","acc"],"mappings":"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","file":"browser.mjs","sourcesContent":["// SDK version - will be replaced during build with tsup define\ndeclare const __SDK_VERSION__: string | undefined;\n\n// Safely check if __SDK_VERSION__ is defined (replaced during build)\nexport const SDK_VERSION: string = \n (() => { try { return typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : 'undefined'; } catch { return 'undefined'; } })();\n\nexport const SDK_NAME = 'js';\n\n","import type { BaseTransport } from '../../core/types';\n\n/**\n * Generates a deterministic key for request deduplication.\n * Uses sorted keys to ensure consistent key generation regardless of object property order.\n */\nfunction generateRequestKey(endpointPath: string, data: any): string {\n // Normalize data by sorting object keys for consistent serialization\n const normalize = (value: any): any => {\n if (value === null || value === undefined) {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map(normalize);\n }\n if (typeof value === 'object') {\n const sorted: Record<string, any> = {};\n for (const key of Object.keys(value).sort()) {\n sorted[key] = normalize(value[key]);\n }\n return sorted;\n }\n return value;\n };\n \n const normalizedData = JSON.stringify(normalize(data));\n return `${endpointPath}:${normalizedData}`;\n}\n\n/**\n * DedupeTransport wraps a BaseTransport instance and deduplicates in-flight requests.\n * If multiple identical requests (same endpointPath + same request body) are made\n * concurrently, they will share the same underlying HTTP request and promise.\n */\nexport class DedupeTransport implements BaseTransport {\n private inFlightRequests = new Map<string, Promise<any>>();\n\n constructor(\n private wrappedTransport: BaseTransport,\n private debug?: boolean\n ) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const requestKey = generateRequestKey(endpointPath, data);\n\n // Check if an identical request is already in-flight\n const existingRequest = this.inFlightRequests.get(requestKey);\n if (existingRequest) {\n if (this.debug) {\n console.log(`[DedupeTransport] Deduplicating request to ${endpointPath}`);\n }\n return existingRequest;\n }\n\n // Create new request and store it\n const requestPromise = this.wrappedTransport.post(data, endpointPath)\n .finally(() => {\n // Clean up when request completes (success or failure)\n this.inFlightRequests.delete(requestKey);\n });\n\n this.inFlightRequests.set(requestKey, requestPromise);\n\n if (this.debug) {\n console.log(`[DedupeTransport] New request to ${endpointPath} (${this.inFlightRequests.size} in-flight)`);\n }\n\n return requestPromise;\n }\n}\n","import type { BaseTransport } from '../../core/types';\nimport { SDK_NAME, SDK_VERSION } from '../../version';\nimport { DedupeTransport } from './dedupe';\n\nconst BASE_URL = 'https://dynamic.belocal.dev';\n\nexport interface BaseBrowserTransportConfig {\n headers?: Record<string, string>;\n timeoutMs?: number;\n debug?: boolean;\n}\n\nexport class BaseBrowserTransport implements BaseTransport {\n constructor(private config: BaseBrowserTransportConfig) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const url = `${BASE_URL}${endpointPath}`;\n const controller = new AbortController();\n const timeoutMs = this.config.timeoutMs ?? 10000;\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n if (this.config.debug) {\n console.log(`[Base Browser Transport] POST request to ${url}`, data);\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk': SDK_NAME,\n 'x-sdk-version': SDK_VERSION,\n ...this.config.headers,\n },\n body: JSON.stringify(data),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorMsg = `HTTP ${response.status}: ${response.statusText}`;\n if (this.config.debug) {\n console.error(`[Base Browser Transport] Request failed:`, errorMsg);\n }\n throw new Error(errorMsg);\n }\n\n const result = await response.json();\n if (this.config.debug) {\n console.log(`[Base Browser Transport] Request successful:`, result);\n }\n \n return result;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n}\n\nexport function createBaseBrowserTransport(config: BaseBrowserTransportConfig): BaseTransport {\n const transport = new BaseBrowserTransport(config);\n return new DedupeTransport(transport, config.debug);\n}\n","import type { Transport, BaseTransport } from '../core/types';\nimport { md5 } from 'js-md5';\n\nexport interface MultiTransportConfig {\n baseTransport: BaseTransport;\n debug?: boolean;\n batchWindowMs?: number;\n}\n\ninterface MultiRequest {\n request_id: string;\n texts: string[];\n lang: string;\n source_lang?: string;\n ctx?: Record<string, string>;\n}\n\ninterface MultiRequestItem {\n text: string;\n lang: string;\n sourceLang?: string;\n context?: Record<string, string>;\n resolve: (value: { text: string; status: string }) => void;\n reject: (error: Error) => void;\n}\n\ninterface MultiResponse {\n results: Array<{\n request_id: string;\n data: { texts: string[]; status: string };\n }>;\n}\n\ninterface MultiState {\n currentMulti: MultiRequestItem[];\n multiTimer: ReturnType<typeof setTimeout> | null;\n isRequestInFlight: boolean;\n}\n\nfunction generateRequestId(texts: string[], lang: string, sourceLang?: string, context?: Record<string, string>): string {\n const sortedTexts = [...texts].sort();\n \n let sortedContext: Record<string, string> | null = null;\n if (context && Object.keys(context).length > 0) {\n sortedContext = {};\n const sortedKeys = Object.keys(context).sort();\n for (const key of sortedKeys) {\n sortedContext[key] = context[key];\n }\n }\n \n // Include sourceLang for proper grouping of requests with different source languages\n const data = [sortedTexts, lang, sourceLang || null, sortedContext];\n const json = JSON.stringify(data);\n \n return md5(json);\n}\n\nasync function sendMulti(\n config: MultiTransportConfig,\n items: MultiRequestItem[],\n state: MultiState\n): Promise<void> {\n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Sending multi request with ${items.length} texts`);\n }\n\n try {\n // Group texts by translation parameters\n const groups = new Map<string, MultiRequestItem[]>();\n \n items.forEach(item => {\n const groupKey = JSON.stringify({\n lang: item.lang,\n sourceLang: item.sourceLang || null,\n context: item.context || null\n });\n \n if (!groups.has(groupKey)) {\n groups.set(groupKey, []);\n }\n groups.get(groupKey)!.push(item);\n });\n\n const requestIdToGroupItems = new Map<string, MultiRequestItem[]>();\n const requests: MultiRequest[] = Array.from(groups.entries()).map(([groupKey, groupItems]) => {\n const firstItem = groupItems[0];\n const texts = groupItems.map(item => item.text);\n \n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n \n requestIdToGroupItems.set(requestId, groupItems);\n \n return {\n request_id: requestId,\n texts,\n lang: firstItem.lang,\n source_lang: firstItem.sourceLang,\n ctx: firstItem.context\n };\n });\n\n const multiResponse: MultiResponse = await config.baseTransport.post({ requests }, '/v1/translate/multi');\n \n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Multi response received with ${multiResponse.results.length} groups`);\n }\n\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.request_id, { texts: result.data.texts, status: result.data.status });\n });\n\n requestIdToGroupItems.forEach((groupItems, requestId) => {\n const result = resultMap.get(requestId);\n \n if (!result) {\n if (config.debug) {\n console.error(`[BeLocal Multi Transport] No result found for request_id: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for request_id ${requestId}`);\n if (config.debug) {\n console.error(`[BeLocal Multi Transport]`, error.message);\n }\n groupItems.forEach(item => item.reject(error));\n return;\n }\n\n groupItems.forEach((item, index) => {\n const translatedText = result.texts[index];\n const status = result.status || 'success';\n \n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Success for request_id ${requestId}[${index}]: \"${translatedText}\"`);\n }\n \n item.resolve({ text: translatedText, status });\n });\n });\n\n } catch (error) {\n if (config.debug) {\n console.error(`[BeLocal Multi Transport] Multi request error:`, error);\n }\n \n const errorToReject = error instanceof Error ? error : new Error(String(error));\n items.forEach(item => item.reject(errorToReject));\n } finally {\n // Cleanup handled by base transport\n }\n}\n\nfunction processMulti(config: MultiTransportConfig, state: MultiState): void {\n if (state.currentMulti.length === 0 || state.isRequestInFlight) {\n return;\n }\n\n const itemsToSend = [...state.currentMulti];\n state.currentMulti = [];\n state.multiTimer = null;\n state.isRequestInFlight = true;\n\n sendMulti(config, itemsToSend, state).finally(() => {\n state.isRequestInFlight = false;\n \n if (state.currentMulti.length > 0) {\n const windowMs = config.batchWindowMs ?? 50;\n state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);\n }\n });\n}\n\nexport function createMultiTransport(config: MultiTransportConfig): Transport {\n const windowMs = config.batchWindowMs ?? 50;\n\n const state: MultiState = {\n currentMulti: [],\n multiTimer: null,\n isRequestInFlight: false,\n };\n \n return ({ text, lang, source_lang, ctx }) => {\n return new Promise<{ text: string; status: string }>((resolve, reject) => {\n if (config.debug) {\n const tempRequestId = md5(JSON.stringify([text, lang, source_lang || null, ctx || null]));\n console.log(`[BeLocal Multi Transport] Queuing request ${tempRequestId}: \"${text}\" to ${lang}`);\n }\n\n const requestItem: MultiRequestItem = {\n text,\n lang,\n sourceLang: source_lang,\n context: ctx,\n resolve,\n reject,\n };\n\n state.currentMulti.push(requestItem);\n\n if (state.multiTimer === null && !state.isRequestInFlight) {\n state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);\n }\n });\n };\n}\n\n","import type { Cache } from './types';\n\nexport class LocalCache implements Cache {\n private storage = new Map<string, string>();\n\n get(key: string): string | null {\n return this.storage.get(key) || null;\n }\n\n set(key: string, value: string): void {\n this.storage.set(key, value);\n }\n\n isAvailable(): boolean {\n return true;\n }\n}\n","import type { BelocalEngineOptions, KV, Lang, Transport, BaseTransport } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\n\n/**\n * Base engine implementation shared between browser and Node.js environments.\n */\nexport class BelocalEngine {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n constructor(options: BelocalEngineOptions, baseTransportFactory: (opts: any) => BaseTransport) {\n const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false\n } = options;\n\n this.debug = debug;\n this.cache = new LocalCache();\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n const baseTransport = baseTransportFactory({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n this.transport = createMultiTransport({\n baseTransport,\n debug: this.debug,\n batchWindowMs\n });\n \n if (this.debug) {\n console.log('[BeLocal Engine] Multi transport created with config:', {\n baseUrl: 'https://dynamic.belocal.dev',\n timeoutMs,\n batchWindowMs\n });\n }\n }\n\n /**\n * Translates a single text string to the target language.\n * \n * Uses in-memory cache to avoid redundant API calls. Results are automatically cached\n * for subsequent requests with the same parameters.\n * \n * @param text - The text to translate\n * @param lang - Target language code (e.g., 'es', 'fr', 'ru')\n * @param source_lang - Optional source language code. If not provided, auto-detection is used\n * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context), `cache_type` ('managed')\n * @returns Promise resolving to the translated text\n * @throws {Error} If the translation request fails (network error, API error, timeout)\n * \n * @example\n * ```typescript\n * // Simple translation\n * const result = await engine.translate('Hello world', 'es');\n * \n * // With source language\n * const result = await engine.translate('Hello world', 'es', 'en');\n * \n * // With context (user_ctx) - descriptive context helps improve translation quality\n * const result = await engine.translate('Hello world', 'es', undefined, { \n * user_ctx: 'greeting message on the homepage' \n * });\n * \n * // With cache_type\n * const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'managed' });\n * \n * // With source language and context\n * const result = await engine.translate('Hello world', 'es', 'en', { \n * user_ctx: 'greeting message on the homepage' \n * });\n * ```\n */\n async translate(text: string, lang: Lang, source_lang?: string, ctx?: KV): Promise<string> {\n const results = await this.translateMany([text], lang, source_lang, ctx);\n return results[0];\n }\n\n /**\n * Translates multiple text strings to the target language in a single batch.\n * \n * This method is more efficient than calling `translate()` multiple times as it:\n * - Batches requests together to reduce API calls\n * - Checks cache for each text individually\n * - Only requests translations for cache misses\n * - Maintains the order of input texts in the result array\n * \n * @param texts - Array of texts to translate\n * @param lang - Target language code (e.g., 'es', 'fr', 'ru')\n * @param source_lang - Optional source language code. If not provided, auto-detection is used\n * @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)\n * @returns Promise resolving to an array of translated texts in the same order as input\n * @throws {Error} If the translation request fails (network error, API error, timeout)\n * \n * @example\n * ```typescript\n * // Translate multiple texts\n * const results = await engine.translateMany(['Hello', 'World', 'Test'], 'es');\n * // Returns: ['Hola', 'Mundo', 'Prueba']\n * \n * // With source language\n * const results = await engine.translateMany(['Hello', 'World'], 'fr', 'en');\n * \n * // With context (user_ctx) - descriptive context helps improve translation quality\n * const results = await engine.translateMany(\n * ['Hello', 'World'], \n * 'es', \n * undefined, \n * { user_ctx: 'greeting message on the homepage' }\n * );\n * \n * // With cache_type\n * const results = await engine.translateMany(\n * ['Hello', 'World'], \n * 'es', \n * undefined, \n * { cache_type: 'managed' }\n * );\n * \n * // Empty array returns empty array\n * const results = await engine.translateMany([], 'es');\n * // Returns: []\n * ```\n */\n async translateMany(texts: string[], lang: Lang, source_lang?: string, ctx?: KV): Promise<string[]> {\n const results: (string | null)[] = new Array(texts.length);\n const cacheMisses: Array<{ index: number; text: string }> = [];\n\n for (let i = 0; i < texts.length; i++) {\n const text = texts[i];\n const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);\n \n const cachedResult = this.cache.get(cacheKey);\n if (cachedResult) {\n results[i] = cachedResult;\n if (this.debug) {\n console.log('[BeLocal Engine] Translation from cache:', text);\n }\n continue;\n }\n \n results[i] = null;\n cacheMisses.push({ index: i, text });\n }\n\n if (cacheMisses.length > 0) {\n const translations = await Promise.all(\n cacheMisses.map(async ({ index, text }) => {\n const result = await this.transport({ text, lang, source_lang, ctx });\n \n if (result.status !== 'error') {\n const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);\n this.cache.set(cacheKey, result.text);\n if (this.debug) {\n console.log('[BeLocal Engine] Translation from API, cached:', text);\n }\n } else {\n if (this.debug) {\n console.log('[BeLocal Engine] Translation from API (not cached due to error status):', text);\n }\n }\n \n return { index, translation: result.text };\n })\n );\n\n translations.forEach(({ index, translation }) => {\n results[index] = translation;\n });\n }\n\n return results as string[];\n }\n\n /**\n * Shortcut method for translation with simplified API.\n * \n * This is a convenience method that wraps `translate()`. When `context` is provided as a string,\n * it is automatically wrapped in `{user_ctx: context}` object.\n * \n * @param text - The text to translate\n * @param lang - Target language code (e.g., 'es', 'fr', 'ru')\n * @param source_lang - Optional source language code\n * @param context - Optional descriptive context string in English (will be wrapped as {user_ctx: context})\n * @returns Promise resolving to the translated text\n * @throws {Error} If the translation request fails (network error, API error, timeout)\n * \n * @example\n * ```typescript\n * // Simple translation\n * const result = await engine.t('Hello world', 'es');\n * \n * // With source language\n * const result = await engine.t('Hello world', 'fr', 'en');\n * \n * // With context string (automatically wrapped as {user_ctx: 'greeting message on the homepage'})\n * const result = await engine.t('Hello world', 'es', undefined, 'greeting message on the homepage');\n * \n * // With source language and context\n * const result = await engine.t('Hello world', 'es', 'en', 'greeting message on the homepage');\n * ```\n */\n async t(text: string, lang: Lang, source_lang?: string, context?: string): Promise<string> {\n if (context) {\n return this.translate(text, lang, source_lang, {user_ctx: context});\n }\n return this.translate(text, lang, source_lang);\n }\n\n private generateCacheKey(text: string, lang: Lang, source_lang?: string, ctx?: KV): string {\n const sortedCtx = ctx ? Object.keys(ctx)\n .sort()\n .reduce((acc, key) => {\n acc[key] = ctx[key];\n return acc;\n }, {} as KV) : null;\n\n const data = {\n text,\n lang,\n source_lang: source_lang || null,\n ctx: sortedCtx\n };\n return md5(JSON.stringify(data));\n }\n}\n","import type { BelocalEngineOptions } from '../types';\nimport { createBaseBrowserTransport } from '../../transports/base/browser';\nimport { BelocalEngine as BaseEngine } from './engine';\n\n/**\n * BeLocal translation engine for browser environments.\n * \n * Provides on-demand translation with automatic request batching, caching, and deduplication.\n * Optimized for browser environments using the Fetch API.\n * \n * @example\n * ```typescript\n * const engine = new BelocalEngine({\n * apiKey: 'your-api-key',\n * debug: true\n * });\n * \n * const translated = await engine.translate('Hello world', 'es');\n * ```\n */\nexport class BelocalEngine extends BaseEngine {\n /**\n * Creates a new BelocalEngine instance for browser environments.\n * \n * @param options - Configuration options for the engine\n * @throws {Error} If apiKey is not provided or invalid\n * \n * @example\n * ```typescript\n * const engine = new BelocalEngine({\n * apiKey: 'your-api-key',\n * batchWindowMs: 100,\n * timeoutMs: 10000,\n * debug: false\n * });\n * ```\n */\n constructor(options: BelocalEngineOptions) {\n super(options, createBaseBrowserTransport);\n }\n}\n\n// Re-export types and transports\nexport type { BelocalEngineOptions, Lang, KV, BaseTransport } from '../types';\nexport { createMultiTransport } from '../../transports/multi';\nexport { BaseBrowserTransport, createBaseBrowserTransport } from '../../transports/base';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/version.ts","../src/transports/base/dedupe.ts","../src/transports/base/browser.ts","../src/transports/multi.ts","../src/cache/local.ts","../src/core/engine/engine.ts","../src/core/types.ts","../src/core/engine/browser.ts"],"names":["SDK_VERSION","generateRequestKey","endpointPath","data","normalize","value","sorted","key","normalizedData","DedupeTransport","wrappedTransport","debug","requestKey","existingRequest","requestPromise","BASE_URL","BaseBrowserTransport","config","url","controller","timeoutMs","timeoutId","response","errorMsg","result","createBaseBrowserTransport","transport","generateRequestId","texts","lang","sourceLang","context","sortedTexts","sortedContext","a","b","md5","sendMulti","items","state","groups","item","groupKey","requestIdToGroupItems","requests","groupItems","firstItem","requestId","multiResponse","resultMap","error","index","translatedText","status","errorToReject","processMulti","itemsToSend","windowMs","createMultiTransport","text","source_lang","ctx","resolve","reject","tempRequestId","requestItem","LocalCache","options","entry","oldestKey","BelocalEngine","baseTransportFactory","apiKey","batchWindowMs","cacheTtlMs","cacheMaxSize","safeBatchWindowMs","authHeaders","baseTransport","results","cacheMisses","i","cacheKey","cachedResult","settlement","reason","serverCtx","sortedCtx","USER_TYPE_PRODUCT","USER_TYPE_CHAT"],"mappings":"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","file":"browser.mjs","sourcesContent":["declare const __SDK_VERSION__: string;\nexport const SDK_VERSION = typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : 'undefined';\nexport const SDK_NAME = 'js';\n","import type { BaseTransport } from '../../core/types';\n\nfunction generateRequestKey(endpointPath: string, data: any): string {\n const normalize = (value: any): any => {\n if (value === null || value === undefined) {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map(normalize);\n }\n if (typeof value === 'object') {\n const sorted: Record<string, any> = {};\n for (const key of Object.keys(value).sort()) {\n sorted[key] = normalize(value[key]);\n }\n return sorted;\n }\n return value;\n };\n\n const normalizedData = JSON.stringify(normalize(data));\n return `${endpointPath}:${normalizedData}`;\n}\n\n/** Deduplicates identical in-flight requests so they share a single HTTP call. */\nexport class DedupeTransport implements BaseTransport {\n private inFlightRequests = new Map<string, Promise<any>>();\n\n constructor(\n private wrappedTransport: BaseTransport,\n private debug?: boolean\n ) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const requestKey = generateRequestKey(endpointPath, data);\n\n const existingRequest = this.inFlightRequests.get(requestKey);\n if (existingRequest) {\n if (this.debug) {\n console.log(`[DedupeTransport] Deduplicating request to ${endpointPath}`);\n }\n return existingRequest;\n }\n\n const requestPromise = this.wrappedTransport.post(data, endpointPath)\n .finally(() => {\n this.inFlightRequests.delete(requestKey);\n });\n\n this.inFlightRequests.set(requestKey, requestPromise);\n\n if (this.debug) {\n console.log(`[DedupeTransport] New request to ${endpointPath} (${this.inFlightRequests.size} in-flight)`);\n }\n\n return requestPromise;\n }\n}\n","import type { BaseTransport } from '../../core/types';\nimport { SDK_NAME, SDK_VERSION } from '../../version';\nimport { DedupeTransport } from './dedupe';\n\nconst BASE_URL = 'https://dynamic.belocal.dev';\n\nexport interface BaseBrowserTransportConfig {\n headers?: Record<string, string>;\n timeoutMs?: number;\n debug?: boolean;\n}\n\nexport class BaseBrowserTransport implements BaseTransport {\n constructor(private config: BaseBrowserTransportConfig) {}\n\n async post(data: any, endpointPath: string): Promise<any> {\n const url = `${BASE_URL}${endpointPath}`;\n const controller = new AbortController();\n const timeoutMs = this.config.timeoutMs ?? 10000;\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n if (this.config.debug) {\n console.log(`[Base Browser Transport] POST request to ${url}`, data);\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk': SDK_NAME,\n 'x-sdk-version': SDK_VERSION,\n ...this.config.headers,\n },\n body: JSON.stringify(data),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorMsg = `HTTP ${response.status}: ${response.statusText}`;\n if (this.config.debug) {\n console.error(`[Base Browser Transport] Request failed:`, errorMsg);\n }\n throw new Error(errorMsg);\n }\n\n const result = await response.json();\n if (this.config.debug) {\n console.log(`[Base Browser Transport] Request successful:`, result);\n }\n \n return result;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n}\n\nexport function createBaseBrowserTransport(config: BaseBrowserTransportConfig): BaseTransport {\n const transport = new BaseBrowserTransport(config);\n return new DedupeTransport(transport, config.debug);\n}\n","import type { Transport, BaseTransport, TranslationCtx } from '../core/types';\nimport { md5 } from 'js-md5';\n\nexport interface MultiTransportConfig {\n baseTransport: BaseTransport;\n debug?: boolean;\n batchWindowMs?: number;\n}\n\ninterface MultiRequest {\n request_id: string;\n texts: string[];\n lang: string;\n source_lang?: string;\n ctx?: TranslationCtx;\n}\n\ninterface MultiRequestItem {\n text: string;\n lang: string;\n sourceLang?: string;\n context?: TranslationCtx;\n resolve: (value: { text: string; status: string }) => void;\n reject: (error: Error) => void;\n}\n\ninterface MultiResponse {\n results: Array<{\n request_id: string;\n data: { texts: string[]; status: string };\n }>;\n}\n\ninterface MultiState {\n currentMulti: MultiRequestItem[];\n multiTimer: ReturnType<typeof setTimeout> | null;\n isRequestInFlight: boolean;\n}\n\nfunction generateRequestId(texts: string[], lang: string, sourceLang?: string, context?: TranslationCtx): string {\n const sortedTexts = [...texts].sort();\n\n const sortedContext = context && Object.keys(context).length > 0\n ? Object.fromEntries(Object.entries(context).sort(([a], [b]) => a.localeCompare(b)))\n : null;\n\n const data = [sortedTexts, lang, sourceLang || null, sortedContext];\n return md5(JSON.stringify(data));\n}\n\nasync function sendMulti(\n config: MultiTransportConfig,\n items: MultiRequestItem[],\n state: MultiState\n): Promise<void> {\n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Sending multi request with ${items.length} texts`);\n }\n\n try {\n const groups = new Map<string, MultiRequestItem[]>();\n\n items.forEach(item => {\n const groupKey = JSON.stringify({\n lang: item.lang,\n sourceLang: item.sourceLang || null,\n context: item.context || null\n });\n\n if (!groups.has(groupKey)) {\n groups.set(groupKey, []);\n }\n groups.get(groupKey)!.push(item);\n });\n\n const requestIdToGroupItems = new Map<string, MultiRequestItem[]>();\n const requests: MultiRequest[] = Array.from(groups.entries()).map(([groupKey, groupItems]) => {\n const firstItem = groupItems[0];\n const texts = groupItems.map(item => item.text);\n\n const requestId = generateRequestId(\n texts,\n firstItem.lang,\n firstItem.sourceLang,\n firstItem.context\n );\n\n requestIdToGroupItems.set(requestId, groupItems);\n\n return {\n request_id: requestId,\n texts,\n lang: firstItem.lang,\n source_lang: firstItem.sourceLang,\n ctx: firstItem.context\n };\n });\n\n const multiResponse: MultiResponse = await config.baseTransport.post({ requests }, '/v1/translate/multi');\n\n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Multi response received with ${multiResponse.results.length} groups`);\n }\n\n const resultMap = new Map<string, { texts: string[]; status: string }>();\n multiResponse.results.forEach(result => {\n resultMap.set(result.request_id, { texts: result.data.texts, status: result.data.status });\n });\n\n requestIdToGroupItems.forEach((groupItems, requestId) => {\n const result = resultMap.get(requestId);\n\n if (!result) {\n if (config.debug) {\n console.error(`[BeLocal Multi Transport] No result found for request_id: ${requestId}`);\n }\n groupItems.forEach(item => {\n item.reject(new Error(`No result found for request ${requestId}`));\n });\n return;\n }\n\n if (result.texts.length !== groupItems.length) {\n const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for request_id ${requestId}`);\n if (config.debug) {\n console.error(`[BeLocal Multi Transport]`, error.message);\n }\n groupItems.forEach(item => item.reject(error));\n return;\n }\n\n groupItems.forEach((item, index) => {\n const translatedText = result.texts[index];\n const status = result.status || 'success';\n\n if (config.debug) {\n console.log(`[BeLocal Multi Transport] Success for request_id ${requestId}[${index}]: \"${translatedText}\"`);\n }\n\n item.resolve({ text: translatedText, status });\n });\n });\n } catch (error) {\n if (config.debug) {\n console.error(`[BeLocal Multi Transport] Multi request error:`, error);\n }\n\n const errorToReject = error instanceof Error ? error : new Error(String(error));\n items.forEach(item => item.reject(errorToReject));\n }\n}\n\nfunction processMulti(config: MultiTransportConfig, state: MultiState): void {\n if (state.currentMulti.length === 0 || state.isRequestInFlight) {\n return;\n }\n\n const itemsToSend = [...state.currentMulti];\n state.currentMulti = [];\n state.multiTimer = null;\n state.isRequestInFlight = true;\n\n sendMulti(config, itemsToSend, state).finally(() => {\n state.isRequestInFlight = false;\n\n if (state.currentMulti.length > 0) {\n const windowMs = config.batchWindowMs ?? 50;\n state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);\n }\n });\n}\n\nexport function createMultiTransport(config: MultiTransportConfig): Transport {\n const windowMs = config.batchWindowMs ?? 50;\n\n const state: MultiState = {\n currentMulti: [],\n multiTimer: null,\n isRequestInFlight: false,\n };\n\n const transport = (({ text, lang, source_lang, ctx }) => {\n return new Promise<{ text: string; status: string }>((resolve, reject) => {\n if (config.debug) {\n const tempRequestId = md5(JSON.stringify([text, lang, source_lang || null, ctx || null]));\n console.log(`[BeLocal Multi Transport] Queuing request ${tempRequestId}: \"${text}\" to ${lang}`);\n }\n\n const requestItem: MultiRequestItem = {\n text,\n lang,\n sourceLang: source_lang,\n context: ctx,\n resolve,\n reject,\n };\n\n state.currentMulti.push(requestItem);\n\n if (state.multiTimer === null && !state.isRequestInFlight) {\n state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);\n }\n });\n }) as Transport & { destroy: () => void };\n\n transport.destroy = () => {\n if (state.multiTimer) {\n clearTimeout(state.multiTimer);\n state.multiTimer = null;\n }\n state.currentMulti.forEach(item =>\n item.reject(new Error('Transport destroyed'))\n );\n state.currentMulti = [];\n };\n\n return transport;\n}\n","import type { Cache, CacheOptions } from './types';\n\ninterface CacheEntry {\n value: string;\n expiresAt: number;\n}\n\nexport class LocalCache implements Cache {\n private storage = new Map<string, CacheEntry>();\n private maxSize: number;\n private ttlMs: number;\n\n constructor(options: CacheOptions = {}) {\n this.maxSize = options.maxSize ?? 10_000;\n this.ttlMs = options.ttlMs ?? 3_600_000;\n }\n\n get(key: string): string | null {\n const entry = this.storage.get(key);\n if (entry === undefined) return null;\n\n if (Date.now() > entry.expiresAt) {\n this.storage.delete(key);\n return null;\n }\n\n this.storage.delete(key);\n this.storage.set(key, entry);\n\n return entry.value;\n }\n\n set(key: string, value: string): void {\n this.storage.delete(key);\n\n if (this.storage.size >= this.maxSize) {\n const oldestKey = this.storage.keys().next().value;\n if (oldestKey !== undefined) {\n this.storage.delete(oldestKey);\n }\n }\n\n this.storage.set(key, {\n value,\n expiresAt: Date.now() + this.ttlMs,\n });\n }\n\n clear(): void {\n this.storage.clear();\n }\n}\n","import type { BelocalEngineOptions, Lang, TranslateOptions, TranslationCtx, Transport, BaseTransport } from '../types';\nimport { createMultiTransport } from '../../transports/multi';\nimport { LocalCache } from '../../cache/local';\nimport type { Cache } from '../../cache/types';\nimport { md5 } from 'js-md5';\n\nexport class BelocalEngine {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n constructor(options: BelocalEngineOptions, baseTransportFactory: (opts: any) => BaseTransport) {\n const {\n apiKey,\n batchWindowMs = 50,\n timeoutMs = 10000,\n debug = false,\n cacheTtlMs = 3_600_000,\n cacheMaxSize = 10_000\n } = options;\n\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('[BeLocal] apiKey is required and must be a non-empty string');\n }\n\n if (typeof timeoutMs !== 'number' || !Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n throw new Error('[BeLocal] timeoutMs must be a positive finite number');\n }\n\n const safeBatchWindowMs = Math.max(0, batchWindowMs);\n\n this.debug = debug;\n this.cache = new LocalCache({ ttlMs: cacheTtlMs, maxSize: cacheMaxSize });\n\n const authHeaders = {\n 'Authorization': `Bearer ${apiKey}`\n };\n\n const baseTransport = baseTransportFactory({\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n this.transport = createMultiTransport({\n baseTransport,\n debug: this.debug,\n batchWindowMs: safeBatchWindowMs\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: safeBatchWindowMs\n });\n }\n }\n\n /** Translates a single text string to the target language. */\n async translate(text: string, lang: Lang, options?: TranslateOptions): Promise<string> {\n const results = await this.translateMany([text], lang, options);\n return results[0];\n }\n\n /** Translates multiple text strings to the target language in a single batch. */\n async translateMany(texts: string[], lang: Lang, options?: TranslateOptions): Promise<string[]> {\n const { source_lang, ctx } = this.resolveOptions(options);\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 !== null) {\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 settlements = await Promise.allSettled(\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 settlements.forEach((settlement, i) => {\n const { index, text } = cacheMisses[i];\n if (settlement.status === 'fulfilled') {\n results[index] = settlement.value.translation;\n } else {\n results[index] = text;\n if (this.debug) {\n const reason = settlement.reason;\n console.error(\n '[BeLocal Engine] Translation failed, using original text:',\n text,\n reason instanceof Error ? reason.message : String(reason)\n );\n }\n }\n });\n }\n\n return results as string[];\n }\n\n private resolveOptions(options?: TranslateOptions): { source_lang?: string; ctx?: TranslationCtx } {\n if (!options) return {};\n const serverCtx: TranslationCtx = {\n ...options.ctx,\n ...(options.context && { user_ctx: options.context }),\n ...(options.managed && { cache_type: 'managed' }),\n };\n const ctx = Object.values(serverCtx).some(Boolean) ? serverCtx : undefined;\n return { source_lang: options.source_lang, ctx };\n }\n\n /** Cleans up internal resources (timers, cache). Call when the engine is no longer needed. */\n destroy(): void {\n (this.transport as any).destroy?.();\n this.cache.clear();\n }\n\n private generateCacheKey(text: string, lang: Lang, source_lang?: string, ctx?: TranslationCtx): string {\n const sortedCtx = ctx\n ? Object.fromEntries(Object.entries(ctx).sort(([a], [b]) => a.localeCompare(b)))\n : null;\n\n const data = {\n text,\n lang,\n source_lang: source_lang || null,\n ctx: sortedCtx\n };\n return md5(JSON.stringify(data));\n }\n}\n","export type Lang = string;\n\nexport type UserCtx = {\n user_type?: string;\n entity_key?: string;\n entity_id?: string;\n};\n\nexport type TranslationCtx = {\n user_type?: string;\n user_ctx?: string;\n cache_type?: string;\n entity_key?: string;\n entity_id?: string;\n};\n\nexport const USER_TYPE_PRODUCT = 'product';\nexport const USER_TYPE_CHAT = 'chat';\n\nexport type TranslateOptions = {\n source_lang?: string;\n context?: string;\n managed?: boolean;\n ctx?: UserCtx;\n};\n\nexport interface BaseTransport {\n post(data: any, endpointPath: string): Promise<any>;\n}\n\nexport type Transport = (params: { text: string; lang: Lang; source_lang?: string; ctx?: TranslationCtx }) => Promise<{ text: string; status: string }>;\n\nexport interface BelocalEngineOptions {\n apiKey: string;\n batchWindowMs?: number;\n timeoutMs?: number;\n debug?: boolean;\n cacheTtlMs?: number;\n cacheMaxSize?: number;\n}\n","import type { BelocalEngineOptions } from '../types';\nimport { createBaseBrowserTransport } from '../../transports/base/browser';\nimport { BelocalEngine as BaseEngine } from './engine';\n\nexport class BelocalEngine extends BaseEngine {\n constructor(options: BelocalEngineOptions) {\n super(options, createBaseBrowserTransport);\n }\n}\n\nexport type { BelocalEngineOptions, Lang, TranslateOptions, BaseTransport, TranslationCtx, UserCtx } from '../types';\nexport { USER_TYPE_PRODUCT, USER_TYPE_CHAT } from '../types';\nexport { createMultiTransport } from '../../transports/multi';\nexport { BaseBrowserTransport, createBaseBrowserTransport } from '../../transports/base';\n"]}
|
package/dist/node.cjs
CHANGED
|
@@ -27,13 +27,7 @@ var http2__namespace = /*#__PURE__*/_interopNamespace(http2);
|
|
|
27
27
|
// src/transports/base/node.ts
|
|
28
28
|
|
|
29
29
|
// src/version.ts
|
|
30
|
-
var SDK_VERSION =
|
|
31
|
-
try {
|
|
32
|
-
return true ? "1.0.0" : "undefined";
|
|
33
|
-
} catch {
|
|
34
|
-
return "undefined";
|
|
35
|
-
}
|
|
36
|
-
})();
|
|
30
|
+
var SDK_VERSION = "1.0.3" ;
|
|
37
31
|
var SDK_NAME = "js";
|
|
38
32
|
|
|
39
33
|
// src/transports/base/dedupe.ts
|
|
@@ -227,7 +221,6 @@ var BaseNodeTransport = class {
|
|
|
227
221
|
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
|
|
228
222
|
}
|
|
229
223
|
}
|
|
230
|
-
throw new Error("Max retries exceeded");
|
|
231
224
|
}
|
|
232
225
|
};
|
|
233
226
|
function createBaseNodeTransport(config) {
|
|
@@ -236,17 +229,9 @@ function createBaseNodeTransport(config) {
|
|
|
236
229
|
}
|
|
237
230
|
function generateRequestId(texts, lang, sourceLang, context) {
|
|
238
231
|
const sortedTexts = [...texts].sort();
|
|
239
|
-
|
|
240
|
-
if (context && Object.keys(context).length > 0) {
|
|
241
|
-
sortedContext = {};
|
|
242
|
-
const sortedKeys = Object.keys(context).sort();
|
|
243
|
-
for (const key of sortedKeys) {
|
|
244
|
-
sortedContext[key] = context[key];
|
|
245
|
-
}
|
|
246
|
-
}
|
|
232
|
+
const sortedContext = context && Object.keys(context).length > 0 ? Object.fromEntries(Object.entries(context).sort(([a], [b]) => a.localeCompare(b))) : null;
|
|
247
233
|
const data = [sortedTexts, lang, sourceLang || null, sortedContext];
|
|
248
|
-
|
|
249
|
-
return jsMd5.md5(json);
|
|
234
|
+
return jsMd5.md5(JSON.stringify(data));
|
|
250
235
|
}
|
|
251
236
|
async function sendMulti(config, items, state) {
|
|
252
237
|
if (config.debug) {
|
|
@@ -326,7 +311,6 @@ async function sendMulti(config, items, state) {
|
|
|
326
311
|
}
|
|
327
312
|
const errorToReject = error instanceof Error ? error : new Error(String(error));
|
|
328
313
|
items.forEach((item) => item.reject(errorToReject));
|
|
329
|
-
} finally {
|
|
330
314
|
}
|
|
331
315
|
}
|
|
332
316
|
function processMulti(config, state) {
|
|
@@ -352,7 +336,7 @@ function createMultiTransport(config) {
|
|
|
352
336
|
multiTimer: null,
|
|
353
337
|
isRequestInFlight: false
|
|
354
338
|
};
|
|
355
|
-
|
|
339
|
+
const transport = (({ text, lang, source_lang, ctx }) => {
|
|
356
340
|
return new Promise((resolve, reject) => {
|
|
357
341
|
if (config.debug) {
|
|
358
342
|
const tempRequestId = jsMd5.md5(JSON.stringify([text, lang, source_lang || null, ctx || null]));
|
|
@@ -371,22 +355,53 @@ function createMultiTransport(config) {
|
|
|
371
355
|
state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);
|
|
372
356
|
}
|
|
373
357
|
});
|
|
358
|
+
});
|
|
359
|
+
transport.destroy = () => {
|
|
360
|
+
if (state.multiTimer) {
|
|
361
|
+
clearTimeout(state.multiTimer);
|
|
362
|
+
state.multiTimer = null;
|
|
363
|
+
}
|
|
364
|
+
state.currentMulti.forEach(
|
|
365
|
+
(item) => item.reject(new Error("Transport destroyed"))
|
|
366
|
+
);
|
|
367
|
+
state.currentMulti = [];
|
|
374
368
|
};
|
|
369
|
+
return transport;
|
|
375
370
|
}
|
|
376
371
|
|
|
377
372
|
// src/cache/local.ts
|
|
378
373
|
var LocalCache = class {
|
|
379
|
-
constructor() {
|
|
374
|
+
constructor(options = {}) {
|
|
380
375
|
this.storage = /* @__PURE__ */ new Map();
|
|
376
|
+
this.maxSize = options.maxSize ?? 1e4;
|
|
377
|
+
this.ttlMs = options.ttlMs ?? 36e5;
|
|
381
378
|
}
|
|
382
379
|
get(key) {
|
|
383
|
-
|
|
380
|
+
const entry = this.storage.get(key);
|
|
381
|
+
if (entry === void 0) return null;
|
|
382
|
+
if (Date.now() > entry.expiresAt) {
|
|
383
|
+
this.storage.delete(key);
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
this.storage.delete(key);
|
|
387
|
+
this.storage.set(key, entry);
|
|
388
|
+
return entry.value;
|
|
384
389
|
}
|
|
385
390
|
set(key, value) {
|
|
386
|
-
this.storage.
|
|
391
|
+
this.storage.delete(key);
|
|
392
|
+
if (this.storage.size >= this.maxSize) {
|
|
393
|
+
const oldestKey = this.storage.keys().next().value;
|
|
394
|
+
if (oldestKey !== void 0) {
|
|
395
|
+
this.storage.delete(oldestKey);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
this.storage.set(key, {
|
|
399
|
+
value,
|
|
400
|
+
expiresAt: Date.now() + this.ttlMs
|
|
401
|
+
});
|
|
387
402
|
}
|
|
388
|
-
|
|
389
|
-
|
|
403
|
+
clear() {
|
|
404
|
+
this.storage.clear();
|
|
390
405
|
}
|
|
391
406
|
};
|
|
392
407
|
var BelocalEngine = class {
|
|
@@ -395,10 +410,19 @@ var BelocalEngine = class {
|
|
|
395
410
|
apiKey,
|
|
396
411
|
batchWindowMs = 50,
|
|
397
412
|
timeoutMs = 1e4,
|
|
398
|
-
debug = false
|
|
413
|
+
debug = false,
|
|
414
|
+
cacheTtlMs = 36e5,
|
|
415
|
+
cacheMaxSize = 1e4
|
|
399
416
|
} = options;
|
|
417
|
+
if (!apiKey || typeof apiKey !== "string") {
|
|
418
|
+
throw new Error("[BeLocal] apiKey is required and must be a non-empty string");
|
|
419
|
+
}
|
|
420
|
+
if (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
421
|
+
throw new Error("[BeLocal] timeoutMs must be a positive finite number");
|
|
422
|
+
}
|
|
423
|
+
const safeBatchWindowMs = Math.max(0, batchWindowMs);
|
|
400
424
|
this.debug = debug;
|
|
401
|
-
this.cache = new LocalCache();
|
|
425
|
+
this.cache = new LocalCache({ ttlMs: cacheTtlMs, maxSize: cacheMaxSize });
|
|
402
426
|
const authHeaders = {
|
|
403
427
|
"Authorization": `Bearer ${apiKey}`
|
|
404
428
|
};
|
|
@@ -410,109 +434,31 @@ var BelocalEngine = class {
|
|
|
410
434
|
this.transport = createMultiTransport({
|
|
411
435
|
baseTransport,
|
|
412
436
|
debug: this.debug,
|
|
413
|
-
batchWindowMs
|
|
437
|
+
batchWindowMs: safeBatchWindowMs
|
|
414
438
|
});
|
|
415
439
|
if (this.debug) {
|
|
416
440
|
console.log("[BeLocal Engine] Multi transport created with config:", {
|
|
417
441
|
baseUrl: "https://dynamic.belocal.dev",
|
|
418
442
|
timeoutMs,
|
|
419
|
-
batchWindowMs
|
|
443
|
+
batchWindowMs: safeBatchWindowMs
|
|
420
444
|
});
|
|
421
445
|
}
|
|
422
446
|
}
|
|
423
|
-
/**
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
* Uses in-memory cache to avoid redundant API calls. Results are automatically cached
|
|
427
|
-
* for subsequent requests with the same parameters.
|
|
428
|
-
*
|
|
429
|
-
* @param text - The text to translate
|
|
430
|
-
* @param lang - Target language code (e.g., 'es', 'fr', 'ru')
|
|
431
|
-
* @param source_lang - Optional source language code. If not provided, auto-detection is used
|
|
432
|
-
* @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context), `cache_type` ('managed')
|
|
433
|
-
* @returns Promise resolving to the translated text
|
|
434
|
-
* @throws {Error} If the translation request fails (network error, API error, timeout)
|
|
435
|
-
*
|
|
436
|
-
* @example
|
|
437
|
-
* ```typescript
|
|
438
|
-
* // Simple translation
|
|
439
|
-
* const result = await engine.translate('Hello world', 'es');
|
|
440
|
-
*
|
|
441
|
-
* // With source language
|
|
442
|
-
* const result = await engine.translate('Hello world', 'es', 'en');
|
|
443
|
-
*
|
|
444
|
-
* // With context (user_ctx) - descriptive context helps improve translation quality
|
|
445
|
-
* const result = await engine.translate('Hello world', 'es', undefined, {
|
|
446
|
-
* user_ctx: 'greeting message on the homepage'
|
|
447
|
-
* });
|
|
448
|
-
*
|
|
449
|
-
* // With cache_type
|
|
450
|
-
* const result = await engine.translate('Hello world', 'es', undefined, { cache_type: 'managed' });
|
|
451
|
-
*
|
|
452
|
-
* // With source language and context
|
|
453
|
-
* const result = await engine.translate('Hello world', 'es', 'en', {
|
|
454
|
-
* user_ctx: 'greeting message on the homepage'
|
|
455
|
-
* });
|
|
456
|
-
* ```
|
|
457
|
-
*/
|
|
458
|
-
async translate(text, lang, source_lang, ctx) {
|
|
459
|
-
const results = await this.translateMany([text], lang, source_lang, ctx);
|
|
447
|
+
/** Translates a single text string to the target language. */
|
|
448
|
+
async translate(text, lang, options) {
|
|
449
|
+
const results = await this.translateMany([text], lang, options);
|
|
460
450
|
return results[0];
|
|
461
451
|
}
|
|
462
|
-
/**
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
* This method is more efficient than calling `translate()` multiple times as it:
|
|
466
|
-
* - Batches requests together to reduce API calls
|
|
467
|
-
* - Checks cache for each text individually
|
|
468
|
-
* - Only requests translations for cache misses
|
|
469
|
-
* - Maintains the order of input texts in the result array
|
|
470
|
-
*
|
|
471
|
-
* @param texts - Array of texts to translate
|
|
472
|
-
* @param lang - Target language code (e.g., 'es', 'fr', 'ru')
|
|
473
|
-
* @param source_lang - Optional source language code. If not provided, auto-detection is used
|
|
474
|
-
* @param ctx - Optional key-value pairs for translation context. Supported keys: `user_ctx` (string - descriptive context in English), `cache_type` ('managed' | string)
|
|
475
|
-
* @returns Promise resolving to an array of translated texts in the same order as input
|
|
476
|
-
* @throws {Error} If the translation request fails (network error, API error, timeout)
|
|
477
|
-
*
|
|
478
|
-
* @example
|
|
479
|
-
* ```typescript
|
|
480
|
-
* // Translate multiple texts
|
|
481
|
-
* const results = await engine.translateMany(['Hello', 'World', 'Test'], 'es');
|
|
482
|
-
* // Returns: ['Hola', 'Mundo', 'Prueba']
|
|
483
|
-
*
|
|
484
|
-
* // With source language
|
|
485
|
-
* const results = await engine.translateMany(['Hello', 'World'], 'fr', 'en');
|
|
486
|
-
*
|
|
487
|
-
* // With context (user_ctx) - descriptive context helps improve translation quality
|
|
488
|
-
* const results = await engine.translateMany(
|
|
489
|
-
* ['Hello', 'World'],
|
|
490
|
-
* 'es',
|
|
491
|
-
* undefined,
|
|
492
|
-
* { user_ctx: 'greeting message on the homepage' }
|
|
493
|
-
* );
|
|
494
|
-
*
|
|
495
|
-
* // With cache_type
|
|
496
|
-
* const results = await engine.translateMany(
|
|
497
|
-
* ['Hello', 'World'],
|
|
498
|
-
* 'es',
|
|
499
|
-
* undefined,
|
|
500
|
-
* { cache_type: 'managed' }
|
|
501
|
-
* );
|
|
502
|
-
*
|
|
503
|
-
* // Empty array returns empty array
|
|
504
|
-
* const results = await engine.translateMany([], 'es');
|
|
505
|
-
* // Returns: []
|
|
506
|
-
* ```
|
|
507
|
-
*/
|
|
508
|
-
async translateMany(texts, lang, source_lang, ctx) {
|
|
452
|
+
/** Translates multiple text strings to the target language in a single batch. */
|
|
453
|
+
async translateMany(texts, lang, options) {
|
|
454
|
+
const { source_lang, ctx } = this.resolveOptions(options);
|
|
509
455
|
const results = new Array(texts.length);
|
|
510
456
|
const cacheMisses = [];
|
|
511
457
|
for (let i = 0; i < texts.length; i++) {
|
|
512
458
|
const text = texts[i];
|
|
513
459
|
const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);
|
|
514
460
|
const cachedResult = this.cache.get(cacheKey);
|
|
515
|
-
if (cachedResult) {
|
|
461
|
+
if (cachedResult !== null) {
|
|
516
462
|
results[i] = cachedResult;
|
|
517
463
|
if (this.debug) {
|
|
518
464
|
console.log("[BeLocal Engine] Translation from cache:", text);
|
|
@@ -523,7 +469,7 @@ var BelocalEngine = class {
|
|
|
523
469
|
cacheMisses.push({ index: i, text });
|
|
524
470
|
}
|
|
525
471
|
if (cacheMisses.length > 0) {
|
|
526
|
-
const
|
|
472
|
+
const settlements = await Promise.allSettled(
|
|
527
473
|
cacheMisses.map(async ({ index, text }) => {
|
|
528
474
|
const result = await this.transport({ text, lang, source_lang, ctx });
|
|
529
475
|
if (result.status !== "error") {
|
|
@@ -540,51 +486,42 @@ var BelocalEngine = class {
|
|
|
540
486
|
return { index, translation: result.text };
|
|
541
487
|
})
|
|
542
488
|
);
|
|
543
|
-
|
|
544
|
-
|
|
489
|
+
settlements.forEach((settlement, i) => {
|
|
490
|
+
const { index, text } = cacheMisses[i];
|
|
491
|
+
if (settlement.status === "fulfilled") {
|
|
492
|
+
results[index] = settlement.value.translation;
|
|
493
|
+
} else {
|
|
494
|
+
results[index] = text;
|
|
495
|
+
if (this.debug) {
|
|
496
|
+
const reason = settlement.reason;
|
|
497
|
+
console.error(
|
|
498
|
+
"[BeLocal Engine] Translation failed, using original text:",
|
|
499
|
+
text,
|
|
500
|
+
reason instanceof Error ? reason.message : String(reason)
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
545
504
|
});
|
|
546
505
|
}
|
|
547
506
|
return results;
|
|
548
507
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
* ```typescript
|
|
564
|
-
* // Simple translation
|
|
565
|
-
* const result = await engine.t('Hello world', 'es');
|
|
566
|
-
*
|
|
567
|
-
* // With source language
|
|
568
|
-
* const result = await engine.t('Hello world', 'fr', 'en');
|
|
569
|
-
*
|
|
570
|
-
* // With context string (automatically wrapped as {user_ctx: 'greeting message on the homepage'})
|
|
571
|
-
* const result = await engine.t('Hello world', 'es', undefined, 'greeting message on the homepage');
|
|
572
|
-
*
|
|
573
|
-
* // With source language and context
|
|
574
|
-
* const result = await engine.t('Hello world', 'es', 'en', 'greeting message on the homepage');
|
|
575
|
-
* ```
|
|
576
|
-
*/
|
|
577
|
-
async t(text, lang, source_lang, context) {
|
|
578
|
-
if (context) {
|
|
579
|
-
return this.translate(text, lang, source_lang, { user_ctx: context });
|
|
580
|
-
}
|
|
581
|
-
return this.translate(text, lang, source_lang);
|
|
508
|
+
resolveOptions(options) {
|
|
509
|
+
if (!options) return {};
|
|
510
|
+
const serverCtx = {
|
|
511
|
+
...options.ctx,
|
|
512
|
+
...options.context && { user_ctx: options.context },
|
|
513
|
+
...options.managed && { cache_type: "managed" }
|
|
514
|
+
};
|
|
515
|
+
const ctx = Object.values(serverCtx).some(Boolean) ? serverCtx : void 0;
|
|
516
|
+
return { source_lang: options.source_lang, ctx };
|
|
517
|
+
}
|
|
518
|
+
/** Cleans up internal resources (timers, cache). Call when the engine is no longer needed. */
|
|
519
|
+
destroy() {
|
|
520
|
+
this.transport.destroy?.();
|
|
521
|
+
this.cache.clear();
|
|
582
522
|
}
|
|
583
523
|
generateCacheKey(text, lang, source_lang, ctx) {
|
|
584
|
-
const sortedCtx = ctx ? Object.
|
|
585
|
-
acc[key] = ctx[key];
|
|
586
|
-
return acc;
|
|
587
|
-
}, {}) : null;
|
|
524
|
+
const sortedCtx = ctx ? Object.fromEntries(Object.entries(ctx).sort(([a], [b]) => a.localeCompare(b))) : null;
|
|
588
525
|
const data = {
|
|
589
526
|
text,
|
|
590
527
|
lang,
|
|
@@ -595,24 +532,12 @@ var BelocalEngine = class {
|
|
|
595
532
|
}
|
|
596
533
|
};
|
|
597
534
|
|
|
535
|
+
// src/core/types.ts
|
|
536
|
+
var USER_TYPE_PRODUCT = "product";
|
|
537
|
+
var USER_TYPE_CHAT = "chat";
|
|
538
|
+
|
|
598
539
|
// src/core/engine/node.ts
|
|
599
540
|
var BelocalEngine2 = class extends BelocalEngine {
|
|
600
|
-
/**
|
|
601
|
-
* Creates a new BelocalEngine instance for Node.js environments.
|
|
602
|
-
*
|
|
603
|
-
* @param options - Configuration options for the engine
|
|
604
|
-
* @throws {Error} If apiKey is not provided or invalid
|
|
605
|
-
*
|
|
606
|
-
* @example
|
|
607
|
-
* ```typescript
|
|
608
|
-
* const engine = new BelocalEngine({
|
|
609
|
-
* apiKey: 'your-api-key',
|
|
610
|
-
* batchWindowMs: 100,
|
|
611
|
-
* timeoutMs: 10000,
|
|
612
|
-
* debug: false
|
|
613
|
-
* });
|
|
614
|
-
* ```
|
|
615
|
-
*/
|
|
616
541
|
constructor(options) {
|
|
617
542
|
super(options, createBaseNodeTransport);
|
|
618
543
|
}
|
|
@@ -620,6 +545,8 @@ var BelocalEngine2 = class extends BelocalEngine {
|
|
|
620
545
|
|
|
621
546
|
exports.BaseNodeTransport = BaseNodeTransport;
|
|
622
547
|
exports.BelocalEngine = BelocalEngine2;
|
|
548
|
+
exports.USER_TYPE_CHAT = USER_TYPE_CHAT;
|
|
549
|
+
exports.USER_TYPE_PRODUCT = USER_TYPE_PRODUCT;
|
|
623
550
|
exports.createBaseNodeTransport = createBaseNodeTransport;
|
|
624
551
|
exports.createMultiTransport = createMultiTransport;
|
|
625
552
|
//# sourceMappingURL=node.cjs.map
|