gengo-ts 1.0.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/index.js ADDED
@@ -0,0 +1,469 @@
1
+ // src/core/auth.ts
2
+ var encoder = new TextEncoder();
3
+ async function generateSignature(privateKey, timestamp) {
4
+ const keyData = encoder.encode(privateKey);
5
+ const messageData = encoder.encode(timestamp);
6
+ const cryptoKey = await crypto.subtle.importKey(
7
+ "raw",
8
+ keyData,
9
+ { name: "HMAC", hash: "SHA-1" },
10
+ false,
11
+ ["sign"]
12
+ );
13
+ const signature = await crypto.subtle.sign("HMAC", cryptoKey, messageData);
14
+ return arrayBufferToHex(signature);
15
+ }
16
+ function arrayBufferToHex(buffer) {
17
+ const bytes = new Uint8Array(buffer);
18
+ let hex = "";
19
+ for (let i = 0; i < bytes.length; i++) {
20
+ hex += bytes[i].toString(16).padStart(2, "0");
21
+ }
22
+ return hex;
23
+ }
24
+ function getTimestamp() {
25
+ return Math.floor(Date.now() / 1e3).toString();
26
+ }
27
+ async function generateAuthParams(publicKey, privateKey) {
28
+ const ts = getTimestamp();
29
+ const api_sig = await generateSignature(privateKey, ts);
30
+ return {
31
+ api_key: publicKey,
32
+ ts,
33
+ api_sig
34
+ };
35
+ }
36
+
37
+ // src/core/errors.ts
38
+ var GengoError = class extends Error {
39
+ code;
40
+ constructor(message, code = 0) {
41
+ super(message);
42
+ this.name = "GengoError";
43
+ this.code = code;
44
+ Object.setPrototypeOf(this, new.target.prototype);
45
+ }
46
+ };
47
+ var GengoAuthError = class extends GengoError {
48
+ constructor(message, code = 1e3) {
49
+ super(message, code);
50
+ this.name = "GengoAuthError";
51
+ }
52
+ };
53
+ var GengoValidationError = class extends GengoError {
54
+ constructor(message, code = 0) {
55
+ super(message, code);
56
+ this.name = "GengoValidationError";
57
+ }
58
+ };
59
+ var GengoNotFoundError = class extends GengoError {
60
+ constructor(message, code = 0) {
61
+ super(message, code);
62
+ this.name = "GengoNotFoundError";
63
+ }
64
+ };
65
+ var GengoRateLimitError = class extends GengoError {
66
+ constructor(message = "Rate limit exceeded") {
67
+ super(message, 429);
68
+ this.name = "GengoRateLimitError";
69
+ }
70
+ };
71
+ var GengoInsufficientCreditsError = class extends GengoError {
72
+ constructor(message = "Not enough credits - please top up") {
73
+ super(message, 2700);
74
+ this.name = "GengoInsufficientCreditsError";
75
+ }
76
+ };
77
+ var GengoNetworkError = class extends GengoError {
78
+ constructor(message, cause) {
79
+ super(message, 0);
80
+ this.name = "GengoNetworkError";
81
+ if (cause) {
82
+ this.cause = cause;
83
+ }
84
+ }
85
+ };
86
+ var GengoJobStateError = class extends GengoError {
87
+ constructor(message, code = 0) {
88
+ super(message, code);
89
+ this.name = "GengoJobStateError";
90
+ }
91
+ };
92
+ var AUTH_ERROR_CODES = [1e3, 1100, 1150];
93
+ var NOT_FOUND_CODES = [2100, 2200, 2600, 2750];
94
+ var JOB_STATE_CODES = [2250, 2251, 2252, 2650];
95
+ var INSUFFICIENT_CREDITS_CODE = 2700;
96
+ function createErrorFromCode(code, message) {
97
+ if (AUTH_ERROR_CODES.includes(code)) {
98
+ return new GengoAuthError(message, code);
99
+ }
100
+ if (NOT_FOUND_CODES.includes(code)) {
101
+ return new GengoNotFoundError(message, code);
102
+ }
103
+ if (JOB_STATE_CODES.includes(code)) {
104
+ return new GengoJobStateError(message, code);
105
+ }
106
+ if (code === INSUFFICIENT_CREDITS_CODE) {
107
+ return new GengoInsufficientCreditsError(message);
108
+ }
109
+ if (code >= 1200 && code <= 1951) {
110
+ return new GengoValidationError(message, code);
111
+ }
112
+ return new GengoError(message, code);
113
+ }
114
+
115
+ // src/core/http.ts
116
+ var BASE_URLS = {
117
+ production: "https://api.gengo.com/v2",
118
+ sandbox: "https://api.sandbox.gengo.com/v2"
119
+ };
120
+ var HttpClient = class {
121
+ publicKey;
122
+ privateKey;
123
+ baseUrl;
124
+ timeout;
125
+ constructor(config) {
126
+ this.publicKey = config.publicKey;
127
+ this.privateKey = config.privateKey;
128
+ this.baseUrl = config.sandbox ? BASE_URLS.sandbox : BASE_URLS.production;
129
+ this.timeout = config.timeout ?? 3e4;
130
+ }
131
+ /**
132
+ * Makes an authenticated GET request
133
+ */
134
+ async get(path, params) {
135
+ return this.request({ method: "GET", path, params });
136
+ }
137
+ /**
138
+ * Makes an authenticated POST request
139
+ */
140
+ async post(path, data) {
141
+ return this.request({ method: "POST", path, data });
142
+ }
143
+ /**
144
+ * Makes an authenticated PUT request
145
+ */
146
+ async put(path, data) {
147
+ return this.request({ method: "PUT", path, data });
148
+ }
149
+ /**
150
+ * Makes an authenticated DELETE request
151
+ */
152
+ async delete(path) {
153
+ return this.request({ method: "DELETE", path });
154
+ }
155
+ /**
156
+ * Core request method that handles authentication, serialization, and error handling
157
+ */
158
+ async request(options) {
159
+ const { method, path, params, data } = options;
160
+ const authParams = await generateAuthParams(this.publicKey, this.privateKey);
161
+ let url = `${this.baseUrl}${path}`;
162
+ let body;
163
+ const headers = {
164
+ Accept: "application/json"
165
+ };
166
+ if (method === "GET" || method === "DELETE") {
167
+ const queryParams = new URLSearchParams();
168
+ queryParams.set("api_key", authParams.api_key);
169
+ queryParams.set("ts", authParams.ts);
170
+ queryParams.set("api_sig", authParams.api_sig);
171
+ if (params) {
172
+ for (const [key, value] of Object.entries(params)) {
173
+ if (value !== void 0) {
174
+ queryParams.set(key, String(value));
175
+ }
176
+ }
177
+ }
178
+ url = `${url}?${queryParams.toString()}`;
179
+ } else {
180
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
181
+ const formData = new URLSearchParams();
182
+ formData.set("api_key", authParams.api_key);
183
+ formData.set("ts", authParams.ts);
184
+ formData.set("api_sig", authParams.api_sig);
185
+ if (data) {
186
+ formData.set("data", JSON.stringify(data));
187
+ }
188
+ body = formData.toString();
189
+ }
190
+ let response;
191
+ try {
192
+ const controller = new AbortController();
193
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
194
+ response = await fetch(url, {
195
+ method,
196
+ headers,
197
+ body,
198
+ signal: controller.signal
199
+ });
200
+ clearTimeout(timeoutId);
201
+ } catch (error) {
202
+ if (error instanceof Error) {
203
+ if (error.name === "AbortError") {
204
+ throw new GengoNetworkError("Request timeout", error);
205
+ }
206
+ throw new GengoNetworkError(`Network error: ${error.message}`, error);
207
+ }
208
+ throw new GengoNetworkError("Unknown network error");
209
+ }
210
+ if (response.status === 429) {
211
+ throw new GengoRateLimitError();
212
+ }
213
+ let json;
214
+ try {
215
+ json = await response.json();
216
+ } catch {
217
+ throw new GengoError(
218
+ `Invalid JSON response (HTTP ${response.status})`,
219
+ response.status
220
+ );
221
+ }
222
+ if (json.opstat === "error" && json.err) {
223
+ throw createErrorFromCode(json.err.code, json.err.msg);
224
+ }
225
+ if (json.opstat !== "ok") {
226
+ throw new GengoError("Unknown API error", 0);
227
+ }
228
+ return json.response;
229
+ }
230
+ };
231
+
232
+ // src/resources/account.ts
233
+ var AccountResource = class {
234
+ constructor(http) {
235
+ this.http = http;
236
+ }
237
+ /**
238
+ * Retrieves account statistics such as orders made and credits spent
239
+ */
240
+ async getStats() {
241
+ return this.http.get("/account/stats");
242
+ }
243
+ /**
244
+ * Retrieves account information such as email and display name
245
+ */
246
+ async getMe() {
247
+ return this.http.get("/account/me");
248
+ }
249
+ /**
250
+ * Retrieves account balance in credits
251
+ */
252
+ async getBalance() {
253
+ return this.http.get("/account/balance");
254
+ }
255
+ /**
256
+ * Retrieves list of preferred translators set by user
257
+ */
258
+ async getPreferredTranslators() {
259
+ return this.http.get("/account/preferred_translators");
260
+ }
261
+ };
262
+
263
+ // src/resources/jobs.ts
264
+ var JobsResource = class {
265
+ constructor(http) {
266
+ this.http = http;
267
+ }
268
+ /**
269
+ * Submits a job or group of jobs for translation
270
+ * @param jobs - Record of job keys to job inputs, or a JobsPayload with optional order comment
271
+ */
272
+ async create(jobs) {
273
+ const payload = "jobs" in jobs && !("body_src" in jobs) ? jobs : { jobs };
274
+ return this.http.post("/translate/jobs", payload);
275
+ }
276
+ /**
277
+ * Retrieves a list of recent jobs filtered by parameters
278
+ */
279
+ async list(options) {
280
+ return this.http.get("/translate/jobs", options);
281
+ }
282
+ /**
283
+ * Retrieves a specific job by ID
284
+ */
285
+ async get(id) {
286
+ return this.http.get(`/translate/job/${id}`);
287
+ }
288
+ /**
289
+ * Retrieves multiple jobs by their IDs
290
+ * @param ids - Array of job IDs or comma-separated string
291
+ */
292
+ async getMany(ids) {
293
+ const idString = Array.isArray(ids) ? ids.join(",") : ids;
294
+ return this.http.get(`/translate/jobs/${idString}`);
295
+ }
296
+ /**
297
+ * Updates a job (approve, revise, reject, or archive)
298
+ */
299
+ async update(id, action) {
300
+ await this.http.put(`/translate/job/${id}`, action);
301
+ }
302
+ /**
303
+ * Updates multiple jobs with the same action
304
+ */
305
+ async updateMany(action) {
306
+ await this.http.put("/translate/jobs", action);
307
+ }
308
+ /**
309
+ * Cancels a job (only if not yet started by translator)
310
+ */
311
+ async cancel(id) {
312
+ await this.http.delete(`/translate/job/${id}`);
313
+ }
314
+ /**
315
+ * Gets list of revisions for a job
316
+ */
317
+ async getRevisions(id) {
318
+ return this.http.get(
319
+ `/translate/job/${id}/revisions`
320
+ );
321
+ }
322
+ /**
323
+ * Gets a specific revision for a job
324
+ */
325
+ async getRevision(id, revisionId) {
326
+ return this.http.get(
327
+ `/translate/job/${id}/revision/${revisionId}`
328
+ );
329
+ }
330
+ /**
331
+ * Retrieves feedback submitted for a job
332
+ */
333
+ async getFeedback(id) {
334
+ return this.http.get(`/translate/job/${id}/feedback`);
335
+ }
336
+ /**
337
+ * Retrieves the comment thread for a job
338
+ */
339
+ async getComments(id) {
340
+ return this.http.get(`/translate/job/${id}/comments`);
341
+ }
342
+ /**
343
+ * Submits a new comment to the job's comment thread
344
+ */
345
+ async addComment(id, body) {
346
+ await this.http.post(`/translate/job/${id}/comment`, { body });
347
+ }
348
+ };
349
+
350
+ // src/resources/orders.ts
351
+ var OrdersResource = class {
352
+ constructor(http) {
353
+ this.http = http;
354
+ }
355
+ /**
356
+ * Retrieves an order by ID with job status breakdown
357
+ */
358
+ async get(id) {
359
+ return this.http.get(`/translate/order/${id}`);
360
+ }
361
+ /**
362
+ * Cancels all available jobs in an order
363
+ * Note: This will not cancel jobs that are already in progress
364
+ */
365
+ async cancel(id) {
366
+ return this.http.delete(`/translate/order/${id}`);
367
+ }
368
+ /**
369
+ * Retrieves the comment thread for an order
370
+ */
371
+ async getComments(id) {
372
+ return this.http.get(`/translate/order/${id}/comments`);
373
+ }
374
+ /**
375
+ * Submits a new comment to the order's comment thread
376
+ */
377
+ async addComment(id, body) {
378
+ await this.http.post(`/translate/order/${id}/comment`, { body });
379
+ }
380
+ };
381
+
382
+ // src/resources/glossary.ts
383
+ var GlossaryResource = class {
384
+ constructor(http) {
385
+ this.http = http;
386
+ }
387
+ /**
388
+ * Retrieves all glossaries belonging to the authenticated user
389
+ */
390
+ async list() {
391
+ return this.http.get("/translate/glossary");
392
+ }
393
+ /**
394
+ * Retrieves a specific glossary by ID
395
+ */
396
+ async get(id) {
397
+ return this.http.get(`/translate/glossary/${id}`);
398
+ }
399
+ };
400
+
401
+ // src/resources/service.ts
402
+ var ServiceResource = class {
403
+ constructor(http) {
404
+ this.http = http;
405
+ }
406
+ /**
407
+ * Returns a list of supported languages and their language codes
408
+ */
409
+ async getLanguages() {
410
+ return this.http.get("/translate/service/languages");
411
+ }
412
+ /**
413
+ * Returns supported translation language pairs with pricing
414
+ * @param lcSrc - Optional source language code to filter pairs
415
+ */
416
+ async getLanguagePairs(lcSrc) {
417
+ return this.http.get("/translate/service/language_pairs", {
418
+ lc_src: lcSrc
419
+ });
420
+ }
421
+ /**
422
+ * Returns credit quote and unit count for text jobs
423
+ */
424
+ async getQuote(jobs) {
425
+ return this.http.post("/translate/service/quote", { jobs });
426
+ }
427
+ /**
428
+ * Uploads files and returns quotes with identifiers for ordering
429
+ * Note: File upload requires multipart/form-data which is not yet implemented.
430
+ * Use the Gengo dashboard for file jobs, or implement custom file upload logic.
431
+ */
432
+ async getFileQuote(_jobs) {
433
+ throw new Error(
434
+ "File quote upload is not yet implemented. Use text jobs or the Gengo dashboard."
435
+ );
436
+ }
437
+ };
438
+
439
+ // src/client.ts
440
+ var GengoClient = class {
441
+ http;
442
+ /** Account information and statistics */
443
+ account;
444
+ /** Translation jobs management */
445
+ jobs;
446
+ /** Translation orders management */
447
+ orders;
448
+ /** Glossary management */
449
+ glossary;
450
+ /** Language and pricing services */
451
+ service;
452
+ constructor(config) {
453
+ this.http = new HttpClient({
454
+ publicKey: config.publicKey,
455
+ privateKey: config.privateKey,
456
+ sandbox: config.sandbox ?? false,
457
+ timeout: config.timeout
458
+ });
459
+ this.account = new AccountResource(this.http);
460
+ this.jobs = new JobsResource(this.http);
461
+ this.orders = new OrdersResource(this.http);
462
+ this.glossary = new GlossaryResource(this.http);
463
+ this.service = new ServiceResource(this.http);
464
+ }
465
+ };
466
+
467
+ export { GengoAuthError, GengoClient, GengoError, GengoInsufficientCreditsError, GengoJobStateError, GengoNetworkError, GengoNotFoundError, GengoRateLimitError, GengoValidationError };
468
+ //# sourceMappingURL=index.js.map
469
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/auth.ts","../src/core/errors.ts","../src/core/http.ts","../src/resources/account.ts","../src/resources/jobs.ts","../src/resources/orders.ts","../src/resources/glossary.ts","../src/resources/service.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * HMAC-SHA1 signature generation for Gengo API authentication\n * Uses Web Crypto API for cross-runtime compatibility (Node.js 18+, Deno, Bun)\n */\n\nconst encoder = new TextEncoder();\n\n/**\n * Generates HMAC-SHA1 signature of the timestamp using the private key\n */\nexport async function generateSignature(\n privateKey: string,\n timestamp: string\n): Promise<string> {\n const keyData = encoder.encode(privateKey);\n const messageData = encoder.encode(timestamp);\n\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-1' },\n false,\n ['sign']\n );\n\n const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);\n\n return arrayBufferToHex(signature);\n}\n\n/**\n * Converts an ArrayBuffer to a hexadecimal string\n */\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += bytes[i]!.toString(16).padStart(2, '0');\n }\n return hex;\n}\n\n/**\n * Gets the current Unix timestamp as a string\n */\nexport function getTimestamp(): string {\n return Math.floor(Date.now() / 1000).toString();\n}\n\n/**\n * Authentication parameters required for every API call\n */\nexport interface AuthParams {\n api_key: string;\n ts: string;\n api_sig: string;\n}\n\n/**\n * Generates the authentication parameters for an API call\n */\nexport async function generateAuthParams(\n publicKey: string,\n privateKey: string\n): Promise<AuthParams> {\n const ts = getTimestamp();\n const api_sig = await generateSignature(privateKey, ts);\n\n return {\n api_key: publicKey,\n ts,\n api_sig,\n };\n}\n","/**\n * Base error class for all Gengo API errors\n */\nexport class GengoError extends Error {\n readonly code: number;\n\n constructor(message: string, code: number = 0) {\n super(message);\n this.name = 'GengoError';\n this.code = code;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Authentication failed (invalid API keys, missing signature, etc.)\n * Error codes: 1000, 1100, 1150\n */\nexport class GengoAuthError extends GengoError {\n constructor(message: string, code: number = 1000) {\n super(message, code);\n this.name = 'GengoAuthError';\n }\n}\n\n/**\n * Validation errors for request parameters\n * Error codes: 1200-1951\n */\nexport class GengoValidationError extends GengoError {\n constructor(message: string, code: number = 0) {\n super(message, code);\n this.name = 'GengoValidationError';\n }\n}\n\n/**\n * Resource not found or unauthorized access\n * Error codes: 2100, 2200, 2600, 2750\n */\nexport class GengoNotFoundError extends GengoError {\n constructor(message: string, code: number = 0) {\n super(message, code);\n this.name = 'GengoNotFoundError';\n }\n}\n\n/**\n * Rate limit exceeded\n * HTTP status: 429\n */\nexport class GengoRateLimitError extends GengoError {\n constructor(message: string = 'Rate limit exceeded') {\n super(message, 429);\n this.name = 'GengoRateLimitError';\n }\n}\n\n/**\n * Insufficient credits in account\n * Error code: 2700\n */\nexport class GengoInsufficientCreditsError extends GengoError {\n constructor(message: string = 'Not enough credits - please top up') {\n super(message, 2700);\n this.name = 'GengoInsufficientCreditsError';\n }\n}\n\n/**\n * Network or connection errors\n */\nexport class GengoNetworkError extends GengoError {\n constructor(message: string, cause?: Error) {\n super(message, 0);\n this.name = 'GengoNetworkError';\n if (cause) {\n this.cause = cause;\n }\n }\n}\n\n/**\n * Job is not in the correct state for the requested action\n * Error codes: 2250, 2251, 2252, 2650\n */\nexport class GengoJobStateError extends GengoError {\n constructor(message: string, code: number = 0) {\n super(message, code);\n this.name = 'GengoJobStateError';\n }\n}\n\n// Error code ranges for classification\nconst AUTH_ERROR_CODES = [1000, 1100, 1150];\nconst NOT_FOUND_CODES = [2100, 2200, 2600, 2750];\nconst JOB_STATE_CODES = [2250, 2251, 2252, 2650];\nconst INSUFFICIENT_CREDITS_CODE = 2700;\n\n/**\n * Creates the appropriate error type based on the API error code\n */\nexport function createErrorFromCode(code: number, message: string): GengoError {\n if (AUTH_ERROR_CODES.includes(code)) {\n return new GengoAuthError(message, code);\n }\n\n if (NOT_FOUND_CODES.includes(code)) {\n return new GengoNotFoundError(message, code);\n }\n\n if (JOB_STATE_CODES.includes(code)) {\n return new GengoJobStateError(message, code);\n }\n\n if (code === INSUFFICIENT_CREDITS_CODE) {\n return new GengoInsufficientCreditsError(message);\n }\n\n // Validation errors are in the 1200-1951 range\n if (code >= 1200 && code <= 1951) {\n return new GengoValidationError(message, code);\n }\n\n // Default to base GengoError\n return new GengoError(message, code);\n}\n","import { generateAuthParams } from './auth.js';\nimport {\n GengoError,\n GengoNetworkError,\n GengoRateLimitError,\n createErrorFromCode,\n} from './errors.js';\n\n/**\n * Base URLs for Gengo API\n */\nconst BASE_URLS = {\n production: 'https://api.gengo.com/v2',\n sandbox: 'https://api.sandbox.gengo.com/v2',\n} as const;\n\n/**\n * API response wrapper\n */\ninterface ApiResponse<T> {\n opstat: 'ok' | 'error';\n response?: T;\n err?: {\n code: number;\n msg: string;\n };\n}\n\n/**\n * HTTP client configuration\n */\nexport interface HttpClientConfig {\n publicKey: string;\n privateKey: string;\n sandbox?: boolean;\n timeout?: number;\n}\n\n/**\n * Request options for API calls\n */\ninterface RequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n params?: object;\n data?: object;\n}\n\n/**\n * HTTP client for making authenticated requests to the Gengo API\n */\nexport class HttpClient {\n private readonly publicKey: string;\n private readonly privateKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: HttpClientConfig) {\n this.publicKey = config.publicKey;\n this.privateKey = config.privateKey;\n this.baseUrl = config.sandbox ? BASE_URLS.sandbox : BASE_URLS.production;\n this.timeout = config.timeout ?? 30000;\n }\n\n /**\n * Makes an authenticated GET request\n */\n async get<T>(path: string, params?: object): Promise<T> {\n return this.request<T>({ method: 'GET', path, params });\n }\n\n /**\n * Makes an authenticated POST request\n */\n async post<T>(path: string, data?: object): Promise<T> {\n return this.request<T>({ method: 'POST', path, data });\n }\n\n /**\n * Makes an authenticated PUT request\n */\n async put<T>(path: string, data?: object): Promise<T> {\n return this.request<T>({ method: 'PUT', path, data });\n }\n\n /**\n * Makes an authenticated DELETE request\n */\n async delete<T>(path: string): Promise<T> {\n return this.request<T>({ method: 'DELETE', path });\n }\n\n /**\n * Core request method that handles authentication, serialization, and error handling\n */\n private async request<T>(options: RequestOptions): Promise<T> {\n const { method, path, params, data } = options;\n const authParams = await generateAuthParams(this.publicKey, this.privateKey);\n\n let url = `${this.baseUrl}${path}`;\n let body: string | undefined;\n const headers: Record<string, string> = {\n Accept: 'application/json',\n };\n\n if (method === 'GET' || method === 'DELETE') {\n // For GET/DELETE, all params go in the query string\n const queryParams = new URLSearchParams();\n queryParams.set('api_key', authParams.api_key);\n queryParams.set('ts', authParams.ts);\n queryParams.set('api_sig', authParams.api_sig);\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n queryParams.set(key, String(value));\n }\n }\n }\n\n url = `${url}?${queryParams.toString()}`;\n } else {\n // For POST/PUT, send as form data\n headers['Content-Type'] = 'application/x-www-form-urlencoded';\n\n const formData = new URLSearchParams();\n formData.set('api_key', authParams.api_key);\n formData.set('ts', authParams.ts);\n formData.set('api_sig', authParams.api_sig);\n\n if (data) {\n // The Gengo API expects a 'data' field with JSON-stringified content\n formData.set('data', JSON.stringify(data));\n }\n\n body = formData.toString();\n }\n\n let response: Response;\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n response = await fetch(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new GengoNetworkError('Request timeout', error);\n }\n throw new GengoNetworkError(`Network error: ${error.message}`, error);\n }\n throw new GengoNetworkError('Unknown network error');\n }\n\n // Handle rate limiting\n if (response.status === 429) {\n throw new GengoRateLimitError();\n }\n\n // Parse JSON response\n let json: ApiResponse<T>;\n\n try {\n json = (await response.json()) as ApiResponse<T>;\n } catch {\n throw new GengoError(\n `Invalid JSON response (HTTP ${response.status})`,\n response.status\n );\n }\n\n // Check for API errors\n if (json.opstat === 'error' && json.err) {\n throw createErrorFromCode(json.err.code, json.err.msg);\n }\n\n if (json.opstat !== 'ok') {\n throw new GengoError('Unknown API error', 0);\n }\n\n return json.response as T;\n }\n}\n","import type { HttpClient } from '../core/http.js';\nimport type {\n AccountStats,\n AccountMe,\n AccountBalance,\n PreferredTranslatorGroup,\n} from '../types/index.js';\n\n/**\n * Account resource for managing account information\n */\nexport class AccountResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Retrieves account statistics such as orders made and credits spent\n */\n async getStats(): Promise<AccountStats> {\n return this.http.get<AccountStats>('/account/stats');\n }\n\n /**\n * Retrieves account information such as email and display name\n */\n async getMe(): Promise<AccountMe> {\n return this.http.get<AccountMe>('/account/me');\n }\n\n /**\n * Retrieves account balance in credits\n */\n async getBalance(): Promise<AccountBalance> {\n return this.http.get<AccountBalance>('/account/balance');\n }\n\n /**\n * Retrieves list of preferred translators set by user\n */\n async getPreferredTranslators(): Promise<PreferredTranslatorGroup[]> {\n return this.http.get<PreferredTranslatorGroup[]>('/account/preferred_translators');\n }\n}\n","import type { HttpClient } from '../core/http.js';\nimport type {\n Job,\n JobInput,\n JobSummary,\n JobAction,\n BulkJobAction,\n ListJobsOptions,\n CreateJobsResponse,\n Revision,\n RevisionDetail,\n Feedback,\n Comment,\n} from '../types/index.js';\n\n/**\n * Jobs payload for submission\n */\ninterface JobsPayload {\n jobs: Record<string, JobInput>;\n comment?: string;\n}\n\n/**\n * Jobs resource for managing translation jobs\n */\nexport class JobsResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Submits a job or group of jobs for translation\n * @param jobs - Record of job keys to job inputs, or a JobsPayload with optional order comment\n */\n async create(jobs: Record<string, JobInput> | JobsPayload): Promise<CreateJobsResponse> {\n const payload = 'jobs' in jobs && !('body_src' in jobs)\n ? jobs as JobsPayload\n : { jobs: jobs as Record<string, JobInput> };\n return this.http.post<CreateJobsResponse>('/translate/jobs', payload);\n }\n\n /**\n * Retrieves a list of recent jobs filtered by parameters\n */\n async list(options?: ListJobsOptions): Promise<JobSummary[]> {\n return this.http.get<JobSummary[]>('/translate/jobs', options);\n }\n\n /**\n * Retrieves a specific job by ID\n */\n async get(id: number | string): Promise<{ job: Job }> {\n return this.http.get<{ job: Job }>(`/translate/job/${id}`);\n }\n\n /**\n * Retrieves multiple jobs by their IDs\n * @param ids - Array of job IDs or comma-separated string\n */\n async getMany(ids: (number | string)[] | string): Promise<{ jobs: Job[] }> {\n const idString = Array.isArray(ids) ? ids.join(',') : ids;\n return this.http.get<{ jobs: Job[] }>(`/translate/jobs/${idString}`);\n }\n\n /**\n * Updates a job (approve, revise, reject, or archive)\n */\n async update(id: number | string, action: JobAction): Promise<void> {\n await this.http.put<Record<string, never>>(`/translate/job/${id}`, action);\n }\n\n /**\n * Updates multiple jobs with the same action\n */\n async updateMany(action: BulkJobAction): Promise<void> {\n await this.http.put<Record<string, never>>('/translate/jobs', action);\n }\n\n /**\n * Cancels a job (only if not yet started by translator)\n */\n async cancel(id: number | string): Promise<void> {\n await this.http.delete<Record<string, never>>(`/translate/job/${id}`);\n }\n\n /**\n * Gets list of revisions for a job\n */\n async getRevisions(id: number | string): Promise<{ job_id: number; revisions: Revision[] }> {\n return this.http.get<{ job_id: number; revisions: Revision[] }>(\n `/translate/job/${id}/revisions`\n );\n }\n\n /**\n * Gets a specific revision for a job\n */\n async getRevision(\n id: number | string,\n revisionId: number | string\n ): Promise<{ revision: RevisionDetail }> {\n return this.http.get<{ revision: RevisionDetail }>(\n `/translate/job/${id}/revision/${revisionId}`\n );\n }\n\n /**\n * Retrieves feedback submitted for a job\n */\n async getFeedback(id: number | string): Promise<{ feedback: Feedback }> {\n return this.http.get<{ feedback: Feedback }>(`/translate/job/${id}/feedback`);\n }\n\n /**\n * Retrieves the comment thread for a job\n */\n async getComments(id: number | string): Promise<{ thread: Comment[] }> {\n return this.http.get<{ thread: Comment[] }>(`/translate/job/${id}/comments`);\n }\n\n /**\n * Submits a new comment to the job's comment thread\n */\n async addComment(id: number | string, body: string): Promise<void> {\n await this.http.post<Record<string, never>>(`/translate/job/${id}/comment`, { body });\n }\n}\n","import type { HttpClient } from '../core/http.js';\nimport type { Order, Comment } from '../types/index.js';\n\n/**\n * Orders resource for managing translation orders\n */\nexport class OrdersResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Retrieves an order by ID with job status breakdown\n */\n async get(id: number | string): Promise<{ order: Order }> {\n return this.http.get<{ order: Order }>(`/translate/order/${id}`);\n }\n\n /**\n * Cancels all available jobs in an order\n * Note: This will not cancel jobs that are already in progress\n */\n async cancel(id: number | string): Promise<{ order: Order }> {\n return this.http.delete<{ order: Order }>(`/translate/order/${id}`);\n }\n\n /**\n * Retrieves the comment thread for an order\n */\n async getComments(id: number | string): Promise<{ thread: Comment[] }> {\n return this.http.get<{ thread: Comment[] }>(`/translate/order/${id}/comments`);\n }\n\n /**\n * Submits a new comment to the order's comment thread\n */\n async addComment(id: number | string, body: string): Promise<void> {\n await this.http.post<Record<string, never>>(`/translate/order/${id}/comment`, { body });\n }\n}\n","import type { HttpClient } from '../core/http.js';\nimport type { Glossary } from '../types/index.js';\n\n/**\n * Glossary resource for managing translation glossaries\n */\nexport class GlossaryResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Retrieves all glossaries belonging to the authenticated user\n */\n async list(): Promise<Glossary[]> {\n return this.http.get<Glossary[]>('/translate/glossary');\n }\n\n /**\n * Retrieves a specific glossary by ID\n */\n async get(id: number | string): Promise<Glossary> {\n return this.http.get<Glossary>(`/translate/glossary/${id}`);\n }\n}\n","import type { HttpClient } from '../core/http.js';\nimport type {\n Language,\n LanguagePair,\n QuoteResponse,\n QuoteJobInput,\n} from '../types/index.js';\n\n/**\n * Service resource for language and pricing information\n */\nexport class ServiceResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Returns a list of supported languages and their language codes\n */\n async getLanguages(): Promise<Language[]> {\n return this.http.get<Language[]>('/translate/service/languages');\n }\n\n /**\n * Returns supported translation language pairs with pricing\n * @param lcSrc - Optional source language code to filter pairs\n */\n async getLanguagePairs(lcSrc?: string): Promise<LanguagePair[]> {\n return this.http.get<LanguagePair[]>('/translate/service/language_pairs', {\n lc_src: lcSrc,\n });\n }\n\n /**\n * Returns credit quote and unit count for text jobs\n */\n async getQuote(jobs: Record<string, QuoteJobInput>): Promise<QuoteResponse> {\n return this.http.post<QuoteResponse>('/translate/service/quote', { jobs });\n }\n\n /**\n * Uploads files and returns quotes with identifiers for ordering\n * Note: File upload requires multipart/form-data which is not yet implemented.\n * Use the Gengo dashboard for file jobs, or implement custom file upload logic.\n */\n async getFileQuote(\n _jobs: Record<string, { type: 'file'; lc_src: string; lc_tgt: string; tier: string }>\n ): Promise<QuoteResponse> {\n throw new Error(\n 'File quote upload is not yet implemented. Use text jobs or the Gengo dashboard.'\n );\n }\n}\n","import { HttpClient } from './core/http.js';\nimport { AccountResource } from './resources/account.js';\nimport { JobsResource } from './resources/jobs.js';\nimport { OrdersResource } from './resources/orders.js';\nimport { GlossaryResource } from './resources/glossary.js';\nimport { ServiceResource } from './resources/service.js';\n\n/**\n * Configuration options for the Gengo client\n */\nexport interface GengoClientConfig {\n /** Your Gengo API public key */\n publicKey: string;\n /** Your Gengo API private key */\n privateKey: string;\n /** Use sandbox environment (default: false) */\n sandbox?: boolean;\n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n}\n\n/**\n * Gengo API client for human translation services\n *\n * @example\n * ```typescript\n * import { GengoClient } from 'gengo-ts';\n *\n * const client = new GengoClient({\n * publicKey: 'your_public_key',\n * privateKey: 'your_private_key',\n * sandbox: true,\n * });\n *\n * // Get account balance\n * const balance = await client.account.getBalance();\n *\n * // Submit translation jobs\n * const order = await client.jobs.create({\n * job_1: {\n * body_src: 'Hello, world!',\n * lc_src: 'en',\n * lc_tgt: 'ja',\n * tier: 'standard',\n * slug: 'greeting',\n * },\n * });\n * ```\n */\nexport class GengoClient {\n private readonly http: HttpClient;\n\n /** Account information and statistics */\n readonly account: AccountResource;\n\n /** Translation jobs management */\n readonly jobs: JobsResource;\n\n /** Translation orders management */\n readonly orders: OrdersResource;\n\n /** Glossary management */\n readonly glossary: GlossaryResource;\n\n /** Language and pricing services */\n readonly service: ServiceResource;\n\n constructor(config: GengoClientConfig) {\n this.http = new HttpClient({\n publicKey: config.publicKey,\n privateKey: config.privateKey,\n sandbox: config.sandbox ?? false,\n timeout: config.timeout,\n });\n\n this.account = new AccountResource(this.http);\n this.jobs = new JobsResource(this.http);\n this.orders = new OrdersResource(this.http);\n this.glossary = new GlossaryResource(this.http);\n this.service = new ServiceResource(this.http);\n }\n}\n"]}
@@ -0,0 +1,50 @@
1
+ import { WebhookHandler } from './index.js';
2
+ import '../common-Bk4hl8fX.js';
3
+
4
+ /**
5
+ * Express-compatible request interface
6
+ */
7
+ interface ExpressRequest {
8
+ method: string;
9
+ body?: Record<string, string>;
10
+ headers: Record<string, string | string[] | undefined>;
11
+ }
12
+ /**
13
+ * Express-compatible response interface
14
+ */
15
+ interface ExpressResponse {
16
+ status(code: number): ExpressResponse;
17
+ send(body: string): void;
18
+ }
19
+ /**
20
+ * Express-compatible next function
21
+ */
22
+ type NextFunction = (error?: Error) => void;
23
+ /**
24
+ * Express middleware type
25
+ */
26
+ type ExpressMiddleware = (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => void | Promise<void>;
27
+ /**
28
+ * Creates an Express middleware adapter for the webhook handler
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import express from 'express';
33
+ * import { createWebhookHandler } from 'gengo-ts/webhook';
34
+ * import { expressAdapter } from 'gengo-ts/webhook/express';
35
+ *
36
+ * const app = express();
37
+ * app.use(express.urlencoded({ extended: true }));
38
+ *
39
+ * const handler = createWebhookHandler({
40
+ * onJobUpdate: (job) => {
41
+ * console.log('Job updated:', job.job_id, job.status);
42
+ * },
43
+ * });
44
+ *
45
+ * app.post('/webhook', expressAdapter(handler));
46
+ * ```
47
+ */
48
+ declare function expressAdapter(handler: WebhookHandler): ExpressMiddleware;
49
+
50
+ export { expressAdapter };
@@ -0,0 +1,24 @@
1
+ // src/webhook/express.ts
2
+ function expressAdapter(handler) {
3
+ return async (req, res, next) => {
4
+ if (req.method !== "POST") {
5
+ res.status(405).send("Method not allowed");
6
+ return;
7
+ }
8
+ try {
9
+ const body = req.body ?? {};
10
+ const result = await handler.handleFormData(body);
11
+ if (result.success) {
12
+ res.status(200).send("OK");
13
+ } else {
14
+ res.status(500).send(result.error?.message ?? "Processing failed");
15
+ }
16
+ } catch (error) {
17
+ next(error instanceof Error ? error : new Error(String(error)));
18
+ }
19
+ };
20
+ }
21
+
22
+ export { expressAdapter };
23
+ //# sourceMappingURL=express.js.map
24
+ //# sourceMappingURL=express.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/webhook/express.ts"],"names":[],"mappings":";AAsDO,SAAS,eAAe,OAAA,EAA4C;AACzE,EAAA,OAAO,OAAO,GAAA,EAAK,GAAA,EAAK,IAAA,KAAS;AAC/B,IAAA,IAAI,GAAA,CAAI,WAAW,MAAA,EAAQ;AACzB,MAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,oBAAoB,CAAA;AACzC,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,GAAA,CAAI,IAAA,IAAQ,EAAC;AAC1B,MAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,cAAA,CAAe,IAAI,CAAA;AAEhD,MAAA,IAAI,OAAO,OAAA,EAAS;AAClB,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAAA,MAC3B,CAAA,MAAO;AACL,QAAA,GAAA,CAAI,OAAO,GAAG,CAAA,CAAE,KAAK,MAAA,CAAO,KAAA,EAAO,WAAW,mBAAmB,CAAA;AAAA,MACnE;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,KAAA,YAAiB,QAAQ,KAAA,GAAQ,IAAI,MAAM,MAAA,CAAO,KAAK,CAAC,CAAC,CAAA;AAAA,IAChE;AAAA,EACF,CAAA;AACF","file":"express.js","sourcesContent":["import type { WebhookHandler } from './index.js';\n\n/**\n * Express-compatible request interface\n */\ninterface ExpressRequest {\n method: string;\n body?: Record<string, string>;\n headers: Record<string, string | string[] | undefined>;\n}\n\n/**\n * Express-compatible response interface\n */\ninterface ExpressResponse {\n status(code: number): ExpressResponse;\n send(body: string): void;\n}\n\n/**\n * Express-compatible next function\n */\ntype NextFunction = (error?: Error) => void;\n\n/**\n * Express middleware type\n */\ntype ExpressMiddleware = (\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction\n) => void | Promise<void>;\n\n/**\n * Creates an Express middleware adapter for the webhook handler\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createWebhookHandler } from 'gengo-ts/webhook';\n * import { expressAdapter } from 'gengo-ts/webhook/express';\n *\n * const app = express();\n * app.use(express.urlencoded({ extended: true }));\n *\n * const handler = createWebhookHandler({\n * onJobUpdate: (job) => {\n * console.log('Job updated:', job.job_id, job.status);\n * },\n * });\n *\n * app.post('/webhook', expressAdapter(handler));\n * ```\n */\nexport function expressAdapter(handler: WebhookHandler): ExpressMiddleware {\n return async (req, res, next) => {\n if (req.method !== 'POST') {\n res.status(405).send('Method not allowed');\n return;\n }\n\n try {\n const body = req.body ?? {};\n const result = await handler.handleFormData(body);\n\n if (result.success) {\n res.status(200).send('OK');\n } else {\n res.status(500).send(result.error?.message ?? 'Processing failed');\n }\n } catch (error) {\n next(error instanceof Error ? error : new Error(String(error)));\n }\n };\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import { WebhookHandler } from './index.js';
2
+ import '../common-Bk4hl8fX.js';
3
+
4
+ /**
5
+ * Hono-compatible context interface
6
+ */
7
+ interface HonoContext {
8
+ req: {
9
+ method: string;
10
+ parseBody(): Promise<Record<string, string>>;
11
+ };
12
+ text(body: string, status?: number): Response;
13
+ }
14
+ /**
15
+ * Hono handler type
16
+ */
17
+ type HonoHandler = (c: HonoContext) => Promise<Response>;
18
+ /**
19
+ * Creates a Hono handler adapter for the webhook handler
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { Hono } from 'hono';
24
+ * import { createWebhookHandler } from 'gengo-ts/webhook';
25
+ * import { honoAdapter } from 'gengo-ts/webhook/hono';
26
+ *
27
+ * const app = new Hono();
28
+ *
29
+ * const handler = createWebhookHandler({
30
+ * onJobUpdate: (job) => {
31
+ * console.log('Job updated:', job.job_id, job.status);
32
+ * },
33
+ * });
34
+ *
35
+ * app.post('/webhook', honoAdapter(handler));
36
+ * ```
37
+ */
38
+ declare function honoAdapter(handler: WebhookHandler): HonoHandler;
39
+
40
+ export { honoAdapter };