@belocal/js-sdk 0.4.2 → 0.5.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/node.mjs CHANGED
@@ -2,141 +2,151 @@ import { md5 } from 'js-md5';
2
2
  import * as http2 from 'http2';
3
3
  import { URL } from 'url';
4
4
 
5
- // src/transports/batch.ts
6
- function generateRequestId(text, lang, source_lang, ctx) {
7
- const data = {
8
- text,
9
- lang,
10
- source_lang: source_lang || null,
11
- ctx: ctx || null
12
- };
13
- return md5(JSON.stringify(data));
5
+ // src/transports/multi.ts
6
+ function generateRequestId(texts, lang, sourceLang, context) {
7
+ const sortedTexts = [...texts].sort();
8
+ let sortedContext = null;
9
+ if (context && Object.keys(context).length > 0) {
10
+ sortedContext = {};
11
+ const sortedKeys = Object.keys(context).sort();
12
+ for (const key of sortedKeys) {
13
+ sortedContext[key] = context[key];
14
+ }
15
+ }
16
+ const data = [sortedTexts, lang, sourceLang || null, sortedContext];
17
+ const json = JSON.stringify(data);
18
+ return md5(json);
14
19
  }
15
- async function sendBatch(config, batch, state) {
20
+ async function sendMulti(config, items, state) {
16
21
  if (config.debug) {
17
- console.log(`[BeLocal Batch Transport] Sending batch of ${batch.length} requests`);
22
+ console.log(`[BeLocal Multi Transport] Sending multi request with ${items.length} texts`);
18
23
  }
19
24
  try {
20
- const batchRequests = batch.map((item) => ({
21
- requestId: item.requestId,
22
- payload: item.payload
23
- }));
24
- const batchResponse = await config.baseTransport.post({ batch: batchRequests }, "/v1/translate/batch");
25
+ const groups = /* @__PURE__ */ new Map();
26
+ items.forEach((item) => {
27
+ const groupKey = JSON.stringify({
28
+ lang: item.lang,
29
+ sourceLang: item.sourceLang || null,
30
+ context: item.context || null
31
+ });
32
+ if (!groups.has(groupKey)) {
33
+ groups.set(groupKey, []);
34
+ }
35
+ groups.get(groupKey).push(item);
36
+ });
37
+ const requestIdToGroupItems = /* @__PURE__ */ new Map();
38
+ const requests = Array.from(groups.entries()).map(([groupKey, groupItems]) => {
39
+ const firstItem = groupItems[0];
40
+ const texts = groupItems.map((item) => item.text);
41
+ const requestId = generateRequestId(
42
+ texts,
43
+ firstItem.lang,
44
+ firstItem.sourceLang,
45
+ firstItem.context
46
+ );
47
+ requestIdToGroupItems.set(requestId, groupItems);
48
+ return {
49
+ requestId,
50
+ texts,
51
+ lang: firstItem.lang,
52
+ sourceLang: firstItem.sourceLang,
53
+ context: firstItem.context
54
+ };
55
+ });
56
+ const multiResponse = await config.baseTransport.post({ requests }, "/v1/translate/multi");
25
57
  if (config.debug) {
26
- console.log(`[BeLocal Batch Transport] Batch response received with ${batchResponse.results.length} results`);
58
+ console.log(`[BeLocal Multi Transport] Multi response received with ${multiResponse.results.length} groups`);
27
59
  }
28
60
  const resultMap = /* @__PURE__ */ new Map();
29
- batchResponse.results.forEach((result) => {
30
- resultMap.set(result.requestId, { data: result.data, error: result.error });
61
+ multiResponse.results.forEach((result) => {
62
+ resultMap.set(result.requestId, { texts: result.data.texts, status: result.data.status });
31
63
  });
32
- batch.forEach((item) => {
33
- const result = resultMap.get(item.requestId);
64
+ requestIdToGroupItems.forEach((groupItems, requestId) => {
65
+ const result = resultMap.get(requestId);
34
66
  if (!result) {
35
67
  if (config.debug) {
36
- console.error(`[BeLocal Batch Transport] No result found for requestId: ${item.requestId}`);
68
+ console.error(`[BeLocal Multi Transport] No result found for requestId: ${requestId}`);
37
69
  }
38
- item.reject(new Error(`No result found for request ${item.requestId}`));
70
+ groupItems.forEach((item) => {
71
+ item.reject(new Error(`No result found for request ${requestId}`));
72
+ });
39
73
  return;
40
74
  }
41
- if (result.error) {
75
+ if (result.texts.length !== groupItems.length) {
76
+ const error = new Error(`Mismatch: expected ${groupItems.length} texts, got ${result.texts.length} for requestId ${requestId}`);
42
77
  if (config.debug) {
43
- console.error(`[BeLocal Batch Transport] Error for requestId ${item.requestId}:`, result.error.message);
78
+ console.error(`[BeLocal Multi Transport]`, error.message);
44
79
  }
45
- item.reject(new Error(result.error.message));
46
- } else if (result.data) {
47
- if (config.debug) {
48
- console.log(`[BeLocal Batch Transport] Success for requestId ${item.requestId}: "${result.data.text}"`);
49
- }
50
- const status = result.data.status || "success";
51
- item.resolve({ text: result.data.text, status });
52
- } else {
80
+ groupItems.forEach((item) => item.reject(error));
81
+ return;
82
+ }
83
+ groupItems.forEach((item, index) => {
84
+ const translatedText = result.texts[index];
85
+ const status = result.status || "success";
53
86
  if (config.debug) {
54
- console.warn(`[BeLocal Batch Transport] No data or error for requestId ${item.requestId}, returning original text`);
87
+ console.log(`[BeLocal Multi Transport] Success for requestId ${requestId}[${index}]: "${translatedText}"`);
55
88
  }
56
- item.resolve({ text: item.payload.text, status: "error" });
57
- }
89
+ item.resolve({ text: translatedText, status });
90
+ });
58
91
  });
59
92
  } catch (error) {
60
93
  if (config.debug) {
61
- console.error(`[BeLocal Batch Transport] Batch request error:`, error);
94
+ console.error(`[BeLocal Multi Transport] Multi request error:`, error);
62
95
  }
63
96
  const errorToReject = error instanceof Error ? error : new Error(String(error));
64
- batch.forEach((item) => item.reject(errorToReject));
97
+ items.forEach((item) => item.reject(errorToReject));
65
98
  } finally {
66
99
  }
67
100
  }
68
- function processBatch(config, state) {
69
- if (state.currentBatch.length === 0 || state.isRequestInFlight) {
101
+ function processMulti(config, state) {
102
+ if (state.currentMulti.length === 0 || state.isRequestInFlight) {
70
103
  return;
71
104
  }
72
- const batchToSend = [...state.currentBatch];
73
- state.currentBatch = [];
74
- state.batchTimer = null;
105
+ const itemsToSend = [...state.currentMulti];
106
+ state.currentMulti = [];
107
+ state.multiTimer = null;
75
108
  state.isRequestInFlight = true;
76
- sendBatch(config, batchToSend).finally(() => {
109
+ sendMulti(config, itemsToSend).finally(() => {
77
110
  state.isRequestInFlight = false;
78
- if (state.currentBatch.length > 0) {
111
+ if (state.currentMulti.length > 0) {
79
112
  const windowMs = config.batchWindowMs ?? 50;
80
- state.batchTimer = setTimeout(() => processBatch(config, state), windowMs);
113
+ state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);
81
114
  }
82
115
  });
83
116
  }
84
- function createBatchTransport(config) {
117
+ function createMultiTransport(config) {
85
118
  const windowMs = config.batchWindowMs ?? 50;
86
119
  const state = {
87
- currentBatch: [],
88
- batchTimer: null,
120
+ currentMulti: [],
121
+ multiTimer: null,
89
122
  isRequestInFlight: false
90
123
  };
91
124
  return ({ text, lang, source_lang, ctx }) => {
92
125
  return new Promise((resolve, reject) => {
93
- const requestId = generateRequestId(text, lang, source_lang, ctx);
94
126
  if (config.debug) {
95
- console.log(`[BeLocal Batch Transport] Queuing request ${requestId}: "${text}" to ${lang}`);
127
+ const tempRequestId = md5(JSON.stringify([text, lang, source_lang || null, ctx || null]));
128
+ console.log(`[BeLocal Multi Transport] Queuing request ${tempRequestId}: "${text}" to ${lang}`);
96
129
  }
97
130
  const requestItem = {
98
- requestId,
99
- payload: { text, lang, source_lang, ctx },
131
+ text,
132
+ lang,
133
+ sourceLang: source_lang,
134
+ context: ctx,
100
135
  resolve,
101
136
  reject
102
137
  };
103
- state.currentBatch.push(requestItem);
104
- if (state.batchTimer === null && !state.isRequestInFlight) {
105
- state.batchTimer = setTimeout(() => processBatch(config, state), windowMs);
138
+ state.currentMulti.push(requestItem);
139
+ if (state.multiTimer === null && !state.isRequestInFlight) {
140
+ state.multiTimer = setTimeout(() => processMulti(config, state), windowMs);
106
141
  }
107
142
  });
108
143
  };
109
144
  }
110
145
 
111
- // src/transports/single.ts
112
- function createSingleTransport(config) {
113
- return async ({ text, lang, source_lang, ctx }) => {
114
- if (config.debug) {
115
- console.log(`[BeLocal Single Transport] Translating "${text}" to ${lang}`);
116
- }
117
- try {
118
- const result = await config.baseTransport.post({ text, lang, source_lang, ctx }, "/v1/translate");
119
- if (config.debug) {
120
- if (result.status === "error") {
121
- console.log(`[BeLocal Single Transport] Translation failed: "${result.text || text}"`);
122
- } else {
123
- console.log(`[BeLocal Single Transport] Translation successful: "${result.text || text}"`);
124
- }
125
- }
126
- return { text: result.text || text, status: result.status || "error" };
127
- } catch (error) {
128
- if (config.debug) {
129
- console.error(`[BeLocal Single Transport] Request failed:`, error);
130
- }
131
- throw error;
132
- }
133
- };
134
- }
135
-
136
146
  // src/version.ts
137
147
  var SDK_VERSION = (() => {
138
148
  try {
139
- return true ? "0.4.2" : "undefined";
149
+ return true ? "0.5.0" : "undefined";
140
150
  } catch {
141
151
  return "undefined";
142
152
  }
@@ -309,53 +319,63 @@ var BelocalEngine = class {
309
319
  timeoutMs,
310
320
  debug: this.debug
311
321
  });
312
- if (batchWindowMs > 0) {
313
- this.transport = createBatchTransport({
314
- baseTransport,
315
- debug: this.debug,
322
+ this.transport = createMultiTransport({
323
+ baseTransport,
324
+ debug: this.debug,
325
+ batchWindowMs
326
+ });
327
+ if (this.debug) {
328
+ console.log("[BeLocal Engine] Multi transport created with config:", {
329
+ baseUrl,
330
+ timeoutMs,
316
331
  batchWindowMs
317
332
  });
318
- if (this.debug) {
319
- console.log("[BeLocal Engine] Batch transport created with config:", {
320
- baseUrl,
321
- timeoutMs,
322
- batchWindowMs
323
- });
324
- }
325
- } else {
326
- this.transport = createSingleTransport({
327
- baseTransport,
328
- debug: this.debug
329
- });
330
- if (this.debug) {
331
- console.log("[BeLocal Engine] Single transport created with config:", {
332
- baseUrl,
333
- timeoutMs
334
- });
335
- }
336
333
  }
337
334
  }
338
335
  async translate(text, lang, source_lang, ctx) {
339
- const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);
340
- const cachedResult = this.cache.get(cacheKey);
341
- if (cachedResult) {
342
- if (this.debug) {
343
- console.log("[BeLocal Engine] Translation from local cache:", text);
336
+ const results = await this.translateMany([text], lang, source_lang, ctx);
337
+ return results[0];
338
+ }
339
+ async translateMany(texts, lang, source_lang, ctx) {
340
+ const results = new Array(texts.length);
341
+ const cacheMisses = [];
342
+ for (let i = 0; i < texts.length; i++) {
343
+ const text = texts[i];
344
+ const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);
345
+ const cachedResult = this.cache.get(cacheKey);
346
+ if (cachedResult) {
347
+ results[i] = cachedResult;
348
+ if (this.debug) {
349
+ console.log("[BeLocal Engine] Translation from local cache:", text);
350
+ }
351
+ continue;
344
352
  }
345
- return cachedResult;
353
+ results[i] = null;
354
+ cacheMisses.push({ index: i, text });
346
355
  }
347
- const result = await this.transport({ text, lang, source_lang, ctx });
348
- if (result.status !== "error") {
349
- this.cache.set(cacheKey, result.text);
350
- if (this.debug) {
351
- console.log("[BeLocal Engine] Translation from API, cached in local:", text);
352
- }
353
- } else {
354
- if (this.debug) {
355
- console.log("[BeLocal Engine] Translation from API (not cached due to error status):", text);
356
- }
356
+ if (cacheMisses.length > 0) {
357
+ const translations = await Promise.all(
358
+ cacheMisses.map(async ({ index, text }) => {
359
+ const result = await this.transport({ text, lang, source_lang, ctx });
360
+ if (result.status !== "error") {
361
+ const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);
362
+ this.cache.set(cacheKey, result.text);
363
+ if (this.debug) {
364
+ console.log("[BeLocal Engine] Translation from API, cached in local:", text);
365
+ }
366
+ } else {
367
+ if (this.debug) {
368
+ console.log("[BeLocal Engine] Translation from API (not cached due to error status):", text);
369
+ }
370
+ }
371
+ return { index, translation: result.text };
372
+ })
373
+ );
374
+ translations.forEach(({ index, translation }) => {
375
+ results[index] = translation;
376
+ });
357
377
  }
358
- return result.text;
378
+ return results;
359
379
  }
360
380
  async t(text, lang, source_lang, context) {
361
381
  if (context) {
@@ -378,6 +398,6 @@ var BelocalEngine = class {
378
398
  }
379
399
  };
380
400
 
381
- export { BaseNodeTransport, BelocalEngine, createBaseNodeTransport, createBatchTransport, createSingleTransport };
401
+ export { BaseNodeTransport, BelocalEngine, createBaseNodeTransport, createMultiTransport };
382
402
  //# sourceMappingURL=node.mjs.map
383
403
  //# sourceMappingURL=node.mjs.map
package/dist/node.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transports/batch.ts","../src/transports/single.ts","../src/version.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 BatchTransportConfig {\n baseTransport: BaseTransport;\n debug?: boolean;\n batchWindowMs?: number;\n}\n\ninterface BatchRequest {\n requestId: string;\n payload: { text: string; lang: string; source_lang?: string; ctx?: Record<string, string> };\n}\n\ninterface BatchRequestItem {\n requestId: string;\n payload: { text: string; lang: string; source_lang?: string; ctx?: Record<string, string> };\n resolve: (value: { text: string; status: string }) => void;\n reject: (error: Error) => void;\n}\n\ninterface BatchResponse {\n results: Array<{\n requestId: string;\n data?: { text: string; status: string };\n error?: { message: string };\n }>;\n}\n\ninterface BatchState {\n currentBatch: BatchRequestItem[];\n batchTimer: ReturnType<typeof setTimeout> | null;\n isRequestInFlight: boolean;\n}\n\nfunction generateRequestId(text: string, lang: string, source_lang?: string, ctx?: Record<string, string>): string {\n const data = {\n text,\n lang,\n source_lang: source_lang || null,\n ctx: ctx || null\n };\n return md5(JSON.stringify(data));\n}\n\nasync function sendBatch(\n config: BatchTransportConfig,\n batch: BatchRequestItem[],\n state: BatchState\n): Promise<void> {\n if (config.debug) {\n console.log(`[BeLocal Batch Transport] Sending batch of ${batch.length} requests`);\n }\n\n try {\n const batchRequests: BatchRequest[] = batch.map(item => ({\n requestId: item.requestId,\n payload: item.payload,\n }));\n\n const batchResponse: BatchResponse = await config.baseTransport.post({ batch: batchRequests }, '/v1/translate/batch');\n \n if (config.debug) {\n console.log(`[BeLocal Batch Transport] Batch response received with ${batchResponse.results.length} results`);\n }\n\n // Создаем map для быстрого поиска результатов по requestId\n const resultMap = new Map<string, { data?: { text: string; status?: string }; error?: { message: string } }>();\n batchResponse.results.forEach(result => {\n resultMap.set(result.requestId, { data: result.data, error: result.error });\n });\n\n // Раздаем результаты каждому промису\n batch.forEach(item => {\n const result = resultMap.get(item.requestId);\n \n if (!result) {\n if (config.debug) {\n console.error(`[BeLocal Batch Transport] No result found for requestId: ${item.requestId}`);\n }\n item.reject(new Error(`No result found for request ${item.requestId}`));\n return;\n }\n\n if (result.error) {\n if (config.debug) {\n console.error(`[BeLocal Batch Transport] Error for requestId ${item.requestId}:`, result.error.message);\n }\n item.reject(new Error(result.error.message));\n } else if (result.data) {\n if (config.debug) {\n console.log(`[BeLocal Batch Transport] Success for requestId ${item.requestId}: \"${result.data.text}\"`);\n }\n // Use status from data if available, otherwise default to 'success'\n const status = result.data.status || 'success';\n item.resolve({ text: result.data.text, status });\n } else {\n // Фоллбэк: если нет ни data, ни error, возвращаем оригинальный текст с error status\n if (config.debug) {\n console.warn(`[BeLocal Batch Transport] No data or error for requestId ${item.requestId}, returning original text`);\n }\n item.resolve({ text: item.payload.text, status: 'error' });\n }\n });\n\n } catch (error) {\n if (config.debug) {\n console.error(`[BeLocal Batch Transport] Batch request error:`, error);\n }\n \n // При ошибке сети отклоняем все промисы в батче\n const errorToReject = error instanceof Error ? error : new Error(String(error));\n batch.forEach(item => item.reject(errorToReject));\n } finally {\n // Cleanup handled by base transport\n }\n}\n\nfunction processBatch(config: BatchTransportConfig, state: BatchState): void {\n if (state.currentBatch.length === 0 || state.isRequestInFlight) {\n return;\n }\n\n const batchToSend = [...state.currentBatch];\n state.currentBatch = [];\n state.batchTimer = null;\n state.isRequestInFlight = true;\n\n sendBatch(config, batchToSend, state).finally(() => {\n state.isRequestInFlight = false;\n \n if (state.currentBatch.length > 0) {\n const windowMs = config.batchWindowMs ?? 50;\n state.batchTimer = setTimeout(() => processBatch(config, state), windowMs);\n }\n });\n}\n\nexport function createBatchTransport(config: BatchTransportConfig): Transport {\n const windowMs = config.batchWindowMs ?? 50;\n\n const state: BatchState = {\n currentBatch: [],\n batchTimer: null,\n isRequestInFlight: false,\n };\n \n return ({ text, lang, source_lang, ctx }) => {\n return new Promise<{ text: string; status: string }>((resolve, reject) => {\n const requestId = generateRequestId(text, lang, source_lang, ctx);\n \n if (config.debug) {\n console.log(`[BeLocal Batch Transport] Queuing request ${requestId}: \"${text}\" to ${lang}`);\n }\n\n const requestItem: BatchRequestItem = {\n requestId,\n payload: { text, lang, source_lang, ctx },\n resolve,\n reject,\n };\n\n state.currentBatch.push(requestItem);\n\n if (state.batchTimer === null && !state.isRequestInFlight) {\n state.batchTimer = setTimeout(() => processBatch(config, state), windowMs);\n }\n });\n };\n}\n","import type { Transport, BaseTransport } from '../core/types';\n\nexport interface SingleTransportConfig {\n baseTransport: BaseTransport;\n debug?: boolean;\n}\n\ninterface TranslationResponse {\n text: string;\n status: string;\n}\n\nexport function createSingleTransport(config: SingleTransportConfig): Transport {\n return async ({ text, lang, source_lang, ctx }) => {\n if (config.debug) {\n console.log(`[BeLocal Single Transport] Translating \"${text}\" to ${lang}`);\n }\n\n try {\n const result: TranslationResponse = await config.baseTransport.post({ text, lang, source_lang, ctx }, '/v1/translate');\n \n if (config.debug) {\n if (result.status === 'error') {\n console.log(`[BeLocal Single Transport] Translation failed: \"${result.text || text}\"`);\n } else {\n console.log(`[BeLocal Single Transport] Translation successful: \"${result.text || text}\"`);\n }\n }\n \n return { text: result.text || text, status: result.status || 'error' };\n } catch (error) {\n if (config.debug) {\n console.error(`[BeLocal Single Transport] Request failed:`, error);\n }\n throw error;\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 * as http2 from 'node:http2';\nimport { URL } from 'node:url';\nimport type { BaseTransport } from '../../core/types';\nimport { SDK_NAME, SDK_VERSION } from '../../version';\n\nexport interface BaseNodeTransportConfig {\n baseUrl: string;\n headers?: Record<string, string>;\n timeoutMs?: number;\n retries?: number;\n debug?: boolean;\n}\n\n// Global session cache for HTTP/2 connections\nconst sessionCache = new Map<string, http2.ClientHttp2Session>();\n\nfunction getOrCreateSession(baseUrl: string, debug?: boolean): http2.ClientHttp2Session {\n if (sessionCache.has(baseUrl)) {\n const session = sessionCache.get(baseUrl)!;\n if (!session.destroyed) {\n return session;\n }\n sessionCache.delete(baseUrl);\n }\n\n const parsedUrl = new URL(baseUrl);\n const session = http2.connect(parsedUrl.origin);\n\n session.socket?.unref();\n \n // Set up session cleanup\n session.on('error', () => {\n sessionCache.delete(baseUrl);\n });\n \n session.on('close', () => {\n sessionCache.delete(baseUrl);\n });\n\n // Add debug logging if enabled\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(baseUrl, 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 = `${this.config.baseUrl}${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.baseUrl, 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 response = await makeHttp2Request(\n session,\n endpointPath,\n headers,\n body,\n this.config.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): BaseNodeTransport {\n return new BaseNodeTransport(config);\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 { createBatchTransport } from '../../transports/batch';\nimport { createSingleTransport } from '../../transports/single';\nimport { createBaseNodeTransport } from '../../transports/base/node';\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) {\n const {\n apiKey,\n baseUrl = 'https://dynamic.belocal.dev',\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 baseUrl,\n headers: authHeaders,\n timeoutMs,\n debug: this.debug\n });\n\n // Create appropriate transport based on batchWindowMs config\n if (batchWindowMs > 0) {\n // Use batch transport with node base transport\n this.transport = createBatchTransport({\n baseTransport,\n debug: this.debug,\n batchWindowMs\n });\n \n if (this.debug) {\n console.log('[BeLocal Engine] Batch transport created with config:', {\n baseUrl,\n timeoutMs,\n batchWindowMs\n });\n }\n } else {\n // Use single transport with node base transport\n this.transport = createSingleTransport({\n baseTransport,\n debug: this.debug\n });\n \n if (this.debug) {\n console.log('[BeLocal Engine] Single transport created with config:', {\n baseUrl,\n timeoutMs\n });\n }\n }\n }\n\n async translate(text: string, lang: Lang, source_lang?: string, ctx?: KV): Promise<string> {\n // Generate cache key from parameters\n const cacheKey = this.generateCacheKey(text, lang, source_lang, ctx);\n \n // Try to get from cache first\n const cachedResult = this.cache.get(cacheKey);\n if (cachedResult) {\n if (this.debug) {\n console.log('[BeLocal Engine] Translation from local cache:', text);\n }\n return cachedResult;\n }\n\n // Cache miss, get translation from transport\n const result = await this.transport({ text, lang, source_lang, ctx });\n \n // Store in cache only if status is not 'error'\n if (result.status !== 'error') {\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 return result.text;\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 { createBatchTransport } from '../../transports/batch';\nexport { createSingleTransport } from '../../transports/single';\n"]}
1
+ {"version":3,"sources":["../src/transports/multi.ts","../src/version.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 * as http2 from 'node:http2';\nimport { URL } from 'node:url';\nimport type { BaseTransport } from '../../core/types';\nimport { SDK_NAME, SDK_VERSION } from '../../version';\n\nexport interface BaseNodeTransportConfig {\n baseUrl: string;\n headers?: Record<string, string>;\n timeoutMs?: number;\n retries?: number;\n debug?: boolean;\n}\n\n// Global session cache for HTTP/2 connections\nconst sessionCache = new Map<string, http2.ClientHttp2Session>();\n\nfunction getOrCreateSession(baseUrl: string, debug?: boolean): http2.ClientHttp2Session {\n if (sessionCache.has(baseUrl)) {\n const session = sessionCache.get(baseUrl)!;\n if (!session.destroyed) {\n return session;\n }\n sessionCache.delete(baseUrl);\n }\n\n const parsedUrl = new URL(baseUrl);\n const session = http2.connect(parsedUrl.origin);\n\n session.socket?.unref();\n \n // Set up session cleanup\n session.on('error', () => {\n sessionCache.delete(baseUrl);\n });\n \n session.on('close', () => {\n sessionCache.delete(baseUrl);\n });\n\n // Add debug logging if enabled\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(baseUrl, 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 = `${this.config.baseUrl}${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.baseUrl, 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 response = await makeHttp2Request(\n session,\n endpointPath,\n headers,\n body,\n this.config.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): BaseNodeTransport {\n return new BaseNodeTransport(config);\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\nexport class BelocalEngine {\n private transport: Transport;\n private debug: boolean;\n private cache: Cache;\n\n constructor(options: BelocalEngineOptions) {\n const {\n apiKey,\n baseUrl = 'https://dynamic.belocal.dev',\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 baseUrl,\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,\n timeoutMs,\n batchWindowMs\n });\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 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 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.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "BeLocal runtime JS SDK for on-demand translation (browser + node)",
5
5
  "scripts": {
6
6
  "test": "vitest run",