UNPKG

@lokalise/node-api

Version:
1,752 lines (1,693 loc) 65.8 kB
// src/models/base_model.ts var BaseModel = class { constructor(params) { Object.assign(this, params); } }; // src/models/branch.ts var Branch = class extends BaseModel { }; // src/lokalise/pkg.ts import { readFile } from "fs/promises"; function pkgPath() { return "../../package.json"; } async function getVersion() { try { const data = await readFile(new URL(pkgPath(), import.meta.url)); const pkg = JSON.parse(data.toString()); return String(pkg.version); } catch { return "unknown"; } } // src/models/api_error.ts var ApiError = class extends Error { /** * The error code representing the type of API error. */ code; /** * Additional details about the error (optional). */ details; /** * Creates an instance of ApiError. * * @param {string} message - The error message. * @param {number} code - The error code. * @param {Record<string, string | number | boolean>} [details] - Additional details about the error. */ constructor(message, code, details) { super(message); this.code = code; if (details) { this.details = details; } } /** * Returns a string representation of the error, including code and details. * * @returns The formatted error message. */ toString() { let baseMessage = `LokaliseError: ${this.message}`; baseMessage += ` (Code: ${this.code})`; if (this.details) { const formattedDetails = Object.entries(this.details).map(([key, value]) => `${key}: ${value}`).join(", "); baseMessage += ` | Details: ${formattedDetails}`; } return baseMessage; } }; // src/http_client/base.ts var ApiRequest = class _ApiRequest { /** * The default base URL for the Lokalise API. */ static urlRoot = "https://api.lokalise.com/api2/"; /** * The resolved response from the API request. */ response; /** * Query and path parameters used to construct the request URL. * This object is modified during URL construction, removing parameters used in path segments. */ params = {}; /** * Constructs a new ApiRequest instance. * @param params - Query and/or path parameters. */ constructor(params) { this.params = { ...params }; } /** * Static async factory method to create an ApiRequest instance with a fully resolved response. * @param uri - The endpoint URI (versioned path expected). * @param method - The HTTP method (GET, POST, PUT, DELETE, etc). * @param body - The request payload, if applicable. * @param params - Query and/or path parameters. * @param clientData - Authentication and configuration data for the request. * @returns A promise that resolves to a fully constructed ApiRequest instance with the `response` set. */ static async create(uri, method, body, params, clientData) { const apiRequest = new _ApiRequest(params); apiRequest.response = await apiRequest.createPromise( uri, method, body, clientData ); return apiRequest; } /** * Creates the request promise by composing the URL, building headers, and executing the fetch. * @param uri - The endpoint URI. * @param method - The HTTP method. * @param body - The request payload. * @param clientData - Client configuration and auth data. * @returns A promise resolving to an ApiResponse or rejecting with an ApiError. */ async createPromise(uri, method, body, clientData) { const url = this.composeURI(`/${clientData.version}/${uri}`); const prefixUrl = clientData.host ?? _ApiRequest.urlRoot; const headers = await this.buildHeaders(clientData, method, body); const options = { method, headers, ...method !== "GET" && body ? { body: JSON.stringify(body) } : {} }; const target = new URL(url, prefixUrl); const stringifiedParams = Object.fromEntries( Object.entries(this.params).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => [key, String(value)]) ); target.search = new URLSearchParams(stringifiedParams).toString(); return this.fetchAndHandleResponse( target, options, clientData.requestTimeout ); } /** * Executes the fetch request and handles network-level errors. * Applies a request timeout if specified. * @param target - The fully constructed request URL. * @param options - The fetch request options. * @param requestTimeout - Optional timeout in milliseconds. * @returns A promise resolving to an ApiResponse or rejecting with an ApiError. */ async fetchAndHandleResponse(target, options, requestTimeout = 0) { const signal = requestTimeout > 0 ? AbortSignal.timeout(requestTimeout) : void 0; try { const response = await fetch(target, { ...options, ...signal ? { signal } : {} }); return this.processResponse(response); } catch (err) { if (err instanceof Error) { if (err.name === "TimeoutError") { return Promise.reject( new ApiError(`Request timed out after ${requestTimeout}ms`, 408, { reason: "timeout" }) ); } return Promise.reject( new ApiError(err.message, 500, { reason: "network or fetch error" }) ); } return Promise.reject( new ApiError("An unknown error occurred", 500, { reason: String(err) }) ); } } /** * Processes the fetch response. * Attempts to parse JSON unless the status is 204 (No Content). * @param response - The raw fetch Response object. * @returns A promise resolving to an ApiResponse if successful, or rejecting with ApiError otherwise. */ async processResponse(response) { let responseJSON = null; try { if (response.status !== 204) { responseJSON = await response.json(); } } catch (error) { return Promise.reject( new ApiError(error.message, response.status, { statusText: response.statusText, reason: "JSON parsing error" }) ); } if (response.ok) { return { json: responseJSON, headers: response.headers }; } return Promise.reject(this.getErrorFromResp(responseJSON)); } /** * Derives an ApiError instance from the response JSON, which may follow various patterns. * @param respJson - The parsed JSON response from the server. * @returns An ApiError representing the server error. */ getErrorFromResp(respJson) { if (!respJson || typeof respJson !== "object") { return new ApiError("An unknown error occurred", 500, { reason: "unexpected response format" }); } const errorObj = respJson; if (typeof errorObj.message === "string" && typeof errorObj.statusCode === "number" && typeof errorObj.error === "string") { return new ApiError(errorObj.message, errorObj.statusCode, { reason: errorObj.error }); } if (errorObj.error && typeof errorObj.error === "object") { const { message = "Unknown error", code = 500, details } = errorObj.error; const safeDetails = typeof details === "object" && details !== null ? details : { reason: "server error without details" }; return new ApiError( String(message), typeof code === "number" ? code : 500, safeDetails ); } if (typeof errorObj.message === "string" && (typeof errorObj.code === "number" || typeof errorObj.errorCode === "number")) { const statusCode = typeof errorObj.code === "number" ? errorObj.code : errorObj.errorCode; const rawDetails = errorObj.details; const safeDetails = typeof rawDetails === "object" && rawDetails !== null ? rawDetails : { reason: "server error without details" }; return new ApiError(errorObj.message, statusCode, safeDetails); } return new ApiError("An unknown error occurred", 500, { reason: "unhandled error format", data: JSON.stringify(respJson) }); } /** * Builds the request headers, including authentication, compression, and JSON headers as needed. * @param clientData - Client configuration and auth data. * @param method - The HTTP method. * @param body - The request payload. * @returns A promise resolving to the constructed Headers. */ async buildHeaders(clientData, method, body) { const userAgent = clientData.userAgent?.trim() || `node-lokalise-api/${await getVersion()}`; const headers = new Headers({ Accept: "application/json", "User-Agent": userAgent }); headers.append( clientData.authHeader, clientData.tokenType.length > 0 ? `${clientData.tokenType} ${clientData.token}` : clientData.token ); if (clientData.enableCompression) { headers.append("Accept-Encoding", "gzip,deflate"); } if (method !== "GET" && body) { headers.append("Content-Type", "application/json"); } return headers; } /** * Composes the final URI by replacing placeholders of the form `/{!:{paramName}}` * with the corresponding parameter values. * @param rawUri - The raw URI template. * @returns The final composed URI string. * @throws Error if a required parameter is missing. */ composeURI(rawUri) { const regexp = /\{(!?):(\w+)\}/g; const uri = rawUri.replace(regexp, this.mapUriParams()); return uri.endsWith("/") ? uri.slice(0, -1) : uri; } /** * Returns a function that maps URI parameters from placeholders. * @returns A function used as a replacement callback in `composeURI`. * @throws Error if a required parameter is missing. */ mapUriParams() { return (_substring, isMandatory, paramName) => { if (this.params[paramName] != null) { const paramValue = String(this.params[paramName]); delete this.params[paramName]; return paramValue; } if (isMandatory === "!") { throw new Error(`Missing required parameter: ${paramName}`); } return ""; }; } }; // src/models/paginated_result.ts var PaginatedResult = class { totalResults; totalPages; resultsPerPage; currentPage; responseTooBig; items; constructor(items, headers) { this.totalResults = this.safeParseInt( headers.get("x-pagination-total-count") ); this.totalPages = this.safeParseInt(headers.get("x-pagination-page-count")); this.resultsPerPage = this.safeParseInt(headers.get("x-pagination-limit")); this.currentPage = this.safeParseInt(headers.get("x-pagination-page")); this.responseTooBig = headers.has("x-response-too-big"); this.items = items; } hasNextPage() { return this.currentPage > 0 && this.currentPage < this.totalPages; } hasPrevPage() { return this.currentPage > 1; } isLastPage() { return !this.hasNextPage(); } isFirstPage() { return !this.hasPrevPage(); } nextPage() { if (this.isLastPage()) { return this.currentPage; } return this.currentPage + 1; } prevPage() { if (this.isFirstPage()) { return this.currentPage; } return this.currentPage - 1; } safeParseInt(str) { if (!str || Number.isNaN(Number(str))) { return 0; } return Number.parseInt(str, 10); } }; // src/models/cursor_paginated_result.ts var CursorPaginatedResult = class extends PaginatedResult { nextCursor; constructor(items, headers) { super(items, headers); this.nextCursor = headers.get("x-pagination-next-cursor"); } hasNextCursor() { return this.nextCursor !== null; } }; // src/collections/base_collection.ts var BaseCollection = class { /** * Client data containing authentication and configuration details. * Provided by a `BaseClient` or similar client instance. */ clientData; /** * Static endpoint property that subclasses can define to indicate the API endpoint * for this collection. If not set, ensure `prefixURI` or `uri` parameters are passed. */ static endpoint; /** * Static prefixURI property that subclasses can define to indicate a base path. * If `uri` is not passed explicitly, this prefix is used to construct the request URL. */ static prefixURI; /** * Constructs a new BaseCollection instance. * @param clientData - Client data for making authenticated requests. */ constructor(clientData) { this.clientData = clientData; } /** * Getter that must be overridden by subclasses to return the root element name * for array-based JSON responses. * @throws Error if not defined by the subclass. */ get rootElementName() { throw new Error( "rootElementName is not defined. Subclasses must override `rootElementName`." ); } /** * Getter that may be overridden by subclasses to return the root element name * for single-item JSON responses. * @throws Error if not defined by the subclass. */ get rootElementNameSingular() { throw new Error( "rootElementNameSingular is not defined. Subclasses must override `rootElementNameSingular`." ); } /** * Getter that may be overridden by subclasses if a secondary model type is returned. * By default, this throws an error. If needed, override it in the subclass. */ get secondaryElementClass() { throw new Error( "Secondary elements are not supported by this collection. Override `secondaryElementClass` if needed." ); } /** * Getter that must be overridden if `secondaryElementClass` is used. * Returns the JSON property name for the secondary element. * @throws Error if not defined by the subclass that uses secondary elements. */ get secondaryElementNameSingular() { throw new Error( "secondaryElementNameSingular is not defined. Subclasses must override this if secondary elements are used." ); } /** * Perform a GET request that expects a list of items. * @param params Optional query or request parameters. * @returns A promise resolving to either a paginated result or an array of ElementType. */ doList(params) { return this.createPromise("GET", params, this.populateArrayFromJson, null); } /** * Perform a GET request that expects a cursor-paginated list of items. * @param params Optional query or request parameters. * @returns A promise resolving to a CursorPaginatedResult of ElementType. */ doListCursor(params) { return this.createPromise( "GET", params, this.populateArrayFromJsonCursor, null ); } /** * Perform a GET request to retrieve a single item by its ID. * @param id The ID of the item to retrieve. * @param params Optional query or request parameters. * @returns A promise resolving to a single ElementType instance. */ doGet(id, params = {}) { return this.createPromise( "GET", { ...params, id }, this.populateObjectFromJsonRoot, null ); } /** * Perform a DELETE request to remove a single item by its ID. * @param id The ID of the item to delete. * @param params Optional query or request parameters. * @returns A promise resolving to JSON representing the deletion result. */ doDelete(id, params = {}) { return this.createPromise( "DELETE", { ...params, id }, this.returnBareJSON, null ); } /** * Perform a POST request to create a new resource. * @param body The object or array of objects to send in the request body. * @param params Optional query or request parameters. * @param resolveFn Optional custom resolve handler to parse the response. * @returns A promise resolving to an ElementType or SecondaryType instance. */ doCreate(body, params = {}, resolveFn = this.populateObjectFromJson) { return this.createPromise("POST", params, resolveFn, body); } /** * Perform a POST request to create multiple resources at once. * @param body The object or array of objects to send in the request body. * @param params Optional query or request parameters. * @param resolveFn Optional custom resolve handler to parse the response array. * @returns A promise resolving to an array of ElementType. */ doCreateArray(body, params, resolveFn = this.populateArray) { return this.createPromise("POST", params, resolveFn, body); } /** * Perform an UPDATE (PUT/PATCH) request to modify an existing resource by its ID. * @param id The ID of the item to update. * @param body The updated fields to send in the request body. * @param params Optional query or request parameters. * @param resolveFn Optional custom resolve handler to parse the response object. * @param method The HTTP method to use, typically PUT or PATCH. * @returns A promise resolving to the updated ElementType instance. */ doUpdate(id, body, params, resolveFn = this.populateObjectFromJsonRoot, method = "PUT") { return this.createPromise(method, { ...params, id }, resolveFn, body); } /** * Parse a JSON response that contains a single item under a known root element name. * @param json The raw JSON object returned by the API. * @param headers The response headers. * @returns The parsed ElementType instance. * @throws Error if the expected root element name is missing. */ populateObjectFromJsonRoot(json, headers) { let jsonData = json; const rootElementName = this.rootElementNameSingular; if (this.rootElementNameSingular && rootElementName) { const dataRecord = jsonData; jsonData = dataRecord[rootElementName]; if (!jsonData) { throw new Error(`Missing property '${rootElementName}' in JSON object`); } } return this.populateObjectFromJson(jsonData, headers); } /** * Parse a JSON response that contains a secondary item under a known secondary root element name. * @param json The raw JSON object returned by the API. * @param headers The response headers. * @returns The parsed SecondaryType instance. * @throws Error if the expected secondary element name is missing. */ populateSecondaryObjectFromJsonRoot(json, headers) { const root = this.secondaryElementNameSingular; const record = json; const itemJson = record[root]; if (typeof itemJson !== "object" || itemJson === null) { throw new Error( `Missing expected secondary property '${root}' in JSON response.` ); } return this.populateObjectFromJson( itemJson, headers, true ); } /** * Parse a JSON response that contains a secondary item. * @param json The raw JSON object returned by the API. * @param headers The response headers. * @returns The parsed SecondaryType instance. */ populateSecondaryObjectFromJson(json, headers) { return this.populateObjectFromJson(json, headers, true); } /** * Parse a JSON response that contains an array of items along with bulk result details. * @param json The raw JSON object returned by the API. * @param headers The response headers. * @returns A BulkResult object containing items and potential errors. * @throws Error if the expected root element is missing or not an array. */ populateArrayFromJsonBulk(json, headers) { const root = this.rootElementName; const jsonArray = json[root]; if (!Array.isArray(jsonArray)) { throw new Error( `Expected an array under '${root}' but received: ${typeof jsonArray}` ); } const items = jsonArray.map( (obj) => this.populateObjectFromJson(obj, headers) ); const errors = Array.isArray(json.errors) ? json.errors : []; return { errors, items }; } /** * Parse a JSON response that contains an array of items. * If pagination headers are detected, returns a PaginatedResult. * Otherwise, returns a plain array of ElementType. * @param json The raw JSON object returned by the API. * @param headers The response headers. */ populateArrayFromJson(json, headers) { const array = this.populateArray(json, headers); return this.isPaginated(headers) ? new PaginatedResult(array, headers) : array; } /** * Parse a JSON response that contains an array of items. * This method returns a plain array and does not consider pagination. * @param json The raw JSON object returned by the API. * @param headers The response headers. */ populateArray(json, headers) { const root = this.rootElementName; const jsonArray = json[root]; if (!Array.isArray(jsonArray)) { throw new Error( `Expected an array under '${root}' but received: ${typeof jsonArray}` ); } return jsonArray.map( (obj) => this.populateObjectFromJson(obj, headers) ); } /** * Parse a JSON response that contains a cursor-paginated array of items. * @param json The raw JSON object returned by the API. * @param headers The response headers. */ populateArrayFromJsonCursor(json, headers) { const root = this.rootElementName; const jsonArray = json[root]; if (!Array.isArray(jsonArray)) { throw new Error( `Expected an array under '${root}' for cursor pagination but received: ${typeof jsonArray}` ); } const items = jsonArray.map( (obj) => this.populateObjectFromJson(obj, headers) ); return new CursorPaginatedResult(items, headers); } /** * Parse a JSON object into either an ElementType or a SecondaryType instance. * @param json The raw JSON object returned by the API. * @param _headers The response headers (if needed). * @param secondary If true, use the secondaryElementClass instead of elementClass. */ populateObjectFromJson(json, _headers, secondary = false) { const cls = secondary ? this.secondaryElementClass : this.elementClass; return new cls(json); } /** * Return the raw JSON as-is. * @param json The raw JSON object or array returned by the API. * @param _headers The response headers (if needed). */ returnBareJSON(json, _headers) { return json; } /** * Convert a single object into an array if it's not already an array. * @param raw_body The raw request body. */ objToArray(raw_body) { return Array.isArray(raw_body) ? raw_body : [raw_body]; } /** * Create a Promise that sends an HTTP request and resolves with a parsed response. * @param method The HTTP method (GET, POST, PUT, DELETE, etc.). * @param params Query or request parameters. * @param resolveFn A function to resolve and parse the JSON response. * @param body The request body, if applicable. * @param uri An explicit URI to use for the request. If not provided, prefixURI is used. */ async createPromise(method, params, resolveFn, body, uri = null) { const request = await this.prepareRequest(method, body, params, uri); return resolveFn.call( this, request.response.json, request.response.headers ); } /** * Prepare the API request by creating a new ApiRequest instance using the static async factory method. * @param method The HTTP method. * @param body The request body. * @param params The request parameters. * @param uri An explicit URI for the request or null. */ async prepareRequest(method, body, params, uri) { return await ApiRequest.create( this.getUri(uri), method, body, params, this.clientData ); } /** * Determine the URI for the request. If uri is not provided, use prefixURI. * @param uri An explicit URI or null. * @throws Error if no URI or prefixURI is provided. */ getUri(uri) { const childClass = this.constructor; const resolvedUri = uri ?? childClass.prefixURI; if (!resolvedUri) { throw new Error( "No URI or prefixURI provided. Ensure the subclass defines a static prefixURI or pass a URI explicitly." ); } return resolvedUri; } isResponseTooBig(headers) { return headers.has("x-response-too-big"); } /** * Determine if the response headers indicate pagination. * @param headers The response headers. */ isPaginated(headers) { return headers.has("x-pagination-total-count") && headers.has("x-pagination-page"); } }; // src/collections/branches.ts var Branches = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/branches/{:id}"; get elementClass() { return Branch; } get rootElementName() { return "branches"; } get rootElementNameSingular() { return "branch"; } list(request_params) { return this.doList(request_params); } create(branch_params, request_params) { return this.doCreate( branch_params, request_params, this.populateObjectFromJsonRoot ); } get(branch_id, request_params) { return this.doGet(branch_id, request_params); } update(branch_id, branch_params, request_params) { return this.doUpdate(branch_id, branch_params, request_params); } delete(branch_id, request_params) { return this.doDelete(branch_id, request_params); } merge(branch_id, request_params, body = {}) { const params = { ...request_params, ...{ id: branch_id } }; return this.createPromise( "POST", params, this.returnBareJSON, body, "projects/{!:project_id}/branches/{:id}/merge" ); } }; // src/models/comment.ts var Comment = class extends BaseModel { }; // src/collections/comments.ts var Comments = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/keys/{!:key_id}/comments/{:id}"; get elementClass() { return Comment; } get rootElementName() { return "comments"; } get rootElementNameSingular() { return "comment"; } list(request_params) { return this.doList(request_params); } create(comment_params, request_params) { const body = { comments: this.objToArray(comment_params) }; return this.doCreateArray(body, request_params); } get(comment_id, request_params) { return this.doGet(comment_id, request_params); } delete(comment_id, request_params) { return this.doDelete(comment_id, request_params); } list_project_comments(params) { return this.createPromise( "GET", params, this.populateArrayFromJson, null, "projects/{!:project_id}/comments" ); } }; // src/models/contributor.ts var Contributor = class extends BaseModel { }; // src/collections/contributors.ts var Contributors = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/contributors/{:id}"; get elementClass() { return Contributor; } get rootElementName() { return "contributors"; } get rootElementNameSingular() { return "contributor"; } list(request_params) { return this.doList(request_params); } create(contributor_params, request_params) { const body = { contributors: this.objToArray(contributor_params) }; return this.doCreateArray(body, request_params); } get(contributor_id, request_params) { return this.doGet(contributor_id, request_params); } me(request_params) { return this.doGet("me", request_params); } update(contributor_id, contributor_params, request_params) { return this.doUpdate(contributor_id, contributor_params, request_params); } delete(contributor_id, request_params) { return this.doDelete(contributor_id, request_params); } }; // src/models/file.ts var File = class extends BaseModel { }; // src/models/queued_process.ts var QueuedProcess = class extends BaseModel { }; // src/utils/logger.ts function warn(silent, ...args) { if (silent) return; console.warn(...args); } // src/collections/files.ts var Files = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/files/{:id}"; get elementClass() { return File; } get rootElementName() { return "files"; } get secondaryElementClass() { return QueuedProcess; } get secondaryElementNameSingular() { return "process"; } returnBareJSON(json, headers) { if (this.isResponseTooBig(headers)) { warn( this.clientData.silent, "\x1B[33m\x1B[1mWarning:\x1B[0m Project too big for sync export. Please use our async export lokaliseApi.files().async_download() method." ); } return { ...super.returnBareJSON(json, headers), responseTooBig: this.isResponseTooBig(headers) }; } list(request_params) { return this.doList(request_params); } upload(project_id, upload) { return this.createPromise( "POST", { project_id }, this.populateSecondaryObjectFromJsonRoot, upload, "projects/{!:project_id}/files/upload" ); } download(project_id, download) { return this.createPromise( "POST", { project_id }, this.returnBareJSON, download, "projects/{!:project_id}/files/download" ); } async_download(project_id, download) { return this.createPromise( "POST", { project_id }, this.populateSecondaryObjectFromJson, download, "projects/{!:project_id}/files/async-download" ); } delete(file_id, request_params) { return this.doDelete(file_id, request_params); } }; // src/models/glossary_term.ts var GlossaryTerm = class extends BaseModel { }; // src/collections/glossary_terms.ts var GlossaryTerms = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/glossary-terms/{:id}"; get elementClass() { return GlossaryTerm; } get rootElementName() { return "data"; } get rootElementNameSingular() { return "data"; } get(term_id, request_params) { return this.doGet(term_id, request_params); } list(request_params) { return this.doListCursor(request_params); } create(term_params, request_params) { return this.createPromise( "POST", request_params, this.populateArrayFromJsonBulk, term_params ); } update(term_params, request_params) { return this.createPromise( "PUT", request_params, this.populateArrayFromJsonBulk, term_params, "projects/{!:project_id}/glossary-terms" ); } delete(term_ids, request_params) { const keys = { terms: term_ids }; return this.createPromise( "DELETE", request_params, this.populateFromBulkDelete, keys, "projects/{!:project_id}/glossary-terms" ); } populateFromBulkDelete(json, _headers) { const dataRecord = json; const jsonData = dataRecord.data; return jsonData; } }; // src/models/jwt.ts var Jwt = class extends BaseModel { }; // src/collections/jwt.ts var Jwt2 = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/tokens"; get elementClass() { return Jwt; } create(project_id, body = { service: "ota" }) { const request_params = { project_id }; return this.doCreate(body, request_params, this.populateObjectFromJson); } }; // src/models/key.ts var Key = class extends BaseModel { }; // src/collections/keys.ts var Keys = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/keys/{:id}"; get elementClass() { return Key; } get rootElementName() { return "keys"; } get rootElementNameSingular() { return "key"; } list(request_params) { return this.doListCursor(request_params); } create(key_params, request_params) { return this.createPromise( "POST", request_params, this.populateArrayFromJsonBulk, key_params ); } get(key_id, request_params) { return this.doGet(key_id, request_params); } update(key_id, key_params, request_params) { return this.doUpdate(key_id, key_params, request_params); } delete(key_id, request_params) { return this.doDelete(key_id, request_params); } bulk_update(key_params, request_params) { return this.createPromise( "PUT", request_params, this.populateArrayFromJsonBulk, key_params, "projects/{!:project_id}/keys" ); } bulk_delete(key_ids, request_params) { const keys = { keys: key_ids }; return this.createPromise( "DELETE", request_params, this.returnBareJSON, keys, "projects/{!:project_id}/keys" ); } }; // src/models/language.ts var Language = class extends BaseModel { }; // src/collections/languages.ts var Languages = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/languages/{:id}"; get elementClass() { return Language; } get rootElementName() { return "languages"; } get rootElementNameSingular() { return "language"; } system_languages(params = {}) { return this.createPromise( "GET", params, this.populateArrayFromJson, null, "system/languages" ); } list(request_params) { return this.doList(request_params); } create(raw_body, request_params) { const body = { languages: this.objToArray(raw_body) }; return this.createPromise( "POST", request_params, this.populateArrayFromJsonBulk, body ); } get(lang_id, request_params) { return this.doGet(lang_id, request_params); } update(lang_id, lang_params, request_params) { return this.doUpdate(lang_id, lang_params, request_params); } delete(lang_id, request_params) { return super.doDelete(lang_id, request_params); } }; // src/models/order.ts var Order = class extends BaseModel { }; // src/collections/orders.ts var Orders = class extends BaseCollection { static prefixURI = "teams/{!:team_id}/orders/{:id}"; get elementClass() { return Order; } get rootElementName() { return "orders"; } get rootElementNameSingular() { return null; } list(request_params) { return this.doList(request_params); } create(order_params, request_params) { return this.doCreate( order_params, request_params, this.populateObjectFromJsonRoot ); } get(order_id, request_params) { return this.doGet(order_id, request_params); } }; // src/models/payment_card.ts var PaymentCard = class extends BaseModel { }; // src/collections/payment_cards.ts var PaymentCards = class extends BaseCollection { static prefixURI = "payment_cards/{:id}"; get elementClass() { return PaymentCard; } get rootElementName() { return "payment_cards"; } get rootElementNameSingular() { return "payment_card"; } list(request_params = {}) { return this.doList(request_params); } create(card_params) { return this.doCreate(card_params); } get(card_id) { return this.doGet(card_id); } delete(card_id) { return this.doDelete(card_id); } }; // src/models/permission_template.ts var PermissionTemplate = class extends BaseModel { }; // src/collections/permission_templates.ts var PermissionTemplates = class extends BaseCollection { static prefixURI = "teams/{!:team_id}/roles"; get elementClass() { return PermissionTemplate; } get rootElementName() { return "roles"; } list(request_params) { return this.doList(request_params); } }; // src/models/project.ts var Project = class extends BaseModel { }; // src/collections/projects.ts var Projects = class extends BaseCollection { static prefixURI = "projects/{:id}"; get elementClass() { return Project; } get rootElementName() { return "projects"; } get rootElementNameSingular() { return null; } list(request_params = {}) { return this.doList(request_params); } create(project_params) { return this.doCreate(project_params); } get(project_id) { return this.doGet(project_id); } update(project_id, project_params) { return this.doUpdate( project_id, project_params, {}, this.populateObjectFromJson ); } delete(project_id) { return this.doDelete(project_id); } empty(project_id) { return this.createPromise( "PUT", { project_id }, this.returnBareJSON, null, "projects/{!:project_id}/empty" ); } }; // src/collections/queued_processes.ts var QueuedProcesses = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/processes/{:id}"; get elementClass() { return QueuedProcess; } get rootElementName() { return "processes"; } get rootElementNameSingular() { return "process"; } list(request_params) { return this.doList(request_params); } get(process_id, request_params) { return this.doGet(process_id, request_params); } }; // src/models/screenshot.ts var Screenshot = class extends BaseModel { }; // src/collections/screenshots.ts var Screenshots = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/screenshots/{:id}"; get elementClass() { return Screenshot; } get rootElementName() { return "screenshots"; } get rootElementNameSingular() { return "screenshot"; } list(request_params) { return this.doList(request_params); } create(raw_body, request_params) { const body = { screenshots: this.objToArray(raw_body) }; return this.createPromise( "POST", request_params, this.populateArrayFromJsonBulk, body ); } get(screnshot_id, request_params) { return this.doGet(screnshot_id, request_params); } update(screenshot_id, screenshot_params, request_params) { return this.doUpdate(screenshot_id, screenshot_params, request_params); } delete(screenshot_id, request_params) { return this.doDelete(screenshot_id, request_params); } }; // src/models/segment.ts var Segment = class extends BaseModel { }; // src/collections/segments.ts var Segments = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/keys/{!:key_id}/segments/{!:language_iso}/{:id}"; get elementClass() { return Segment; } get rootElementName() { return "segments"; } get rootElementNameSingular() { return "segment"; } list(request_params) { return this.doList(request_params); } get(segment_number, request_params) { return this.doGet(segment_number, request_params); } update(segment_number, segment_params, request_params) { return this.doUpdate(segment_number, segment_params, request_params); } }; // src/models/snapshot.ts var Snapshot = class extends BaseModel { }; // src/collections/snapshots.ts var Snapshots = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/snapshots/{:id}"; get elementClass() { return Snapshot; } get rootElementName() { return "snapshots"; } get rootElementNameSingular() { return "snapshot"; } list(request_params) { return this.doList(request_params); } create(snapshot_params, request_params) { return this.doCreate( snapshot_params, request_params, this.populateObjectFromJsonRoot ); } restore(snapshot_id, request_params) { const params = { ...request_params, ...{ id: snapshot_id } }; return this.createPromise("POST", params, this.returnBareJSON, {}); } delete(snapshot_id, request_params) { return this.doDelete(snapshot_id, request_params); } }; // src/models/task.ts var Task = class extends BaseModel { }; // src/collections/tasks.ts var Tasks = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/tasks/{:id}"; get elementClass() { return Task; } get rootElementName() { return "tasks"; } get rootElementNameSingular() { return "task"; } list(request_params) { return this.doList(request_params); } create(task_params, request_params) { return this.doCreate( task_params, request_params, this.populateObjectFromJsonRoot ); } get(task_id, request_params) { return this.doGet(task_id, request_params); } update(task_id, task_params, request_params) { return this.doUpdate(task_id, task_params, request_params); } delete(task_id, request_params) { return this.doDelete(task_id, request_params); } }; // src/models/team_user_billing_details.ts var TeamUserBillingDetails = class extends BaseModel { }; // src/collections/team_user_billing_details.ts var TeamUserBillingDetails2 = class extends BaseCollection { static prefixURI = "teams/{!:team_id}/billing_details"; get elementClass() { return TeamUserBillingDetails; } get(team_id) { const params = { team_id }; return this.createPromise("GET", params, this.populateObjectFromJson, null); } create(billing_details_params, request_params) { return this.doCreate(billing_details_params, request_params); } update(team_id, billing_details_params) { const params = { team_id }; return this.createPromise( "PUT", params, this.populateObjectFromJson, billing_details_params ); } }; // src/models/team_user.ts var TeamUser = class extends BaseModel { }; // src/collections/team_users.ts var TeamUsers = class extends BaseCollection { static prefixURI = "teams/{!:team_id}/users/{:id}"; get elementClass() { return TeamUser; } get rootElementName() { return "team_users"; } get rootElementNameSingular() { return "team_user"; } list(request_params) { return this.doList(request_params); } get(team_user_id, request_params) { return this.doGet(team_user_id, request_params); } update(team_user_id, team_user_params, request_params) { return this.doUpdate(team_user_id, team_user_params, request_params); } delete(team_user_id, request_params) { return this.doDelete(team_user_id, request_params); } }; // src/models/team.ts var Team = class extends BaseModel { }; // src/collections/teams.ts var Teams = class extends BaseCollection { static prefixURI = "teams/{:id}"; get elementClass() { return Team; } get rootElementName() { return "teams"; } get rootElementNameSingular() { return "team"; } list(request_params = {}) { return this.doList(request_params); } get(id) { return this.doGet(id); } }; // src/models/translation_provider.ts var TranslationProvider = class extends BaseModel { }; // src/collections/translation_providers.ts var TranslationProviders = class extends BaseCollection { static prefixURI = "teams/{!:team_id}/translation_providers/{:id}"; get elementClass() { return TranslationProvider; } get rootElementName() { return "translation_providers"; } get rootElementNameSingular() { return null; } list(request_params) { return this.doList(request_params); } get(provider_id, request_params) { return this.doGet(provider_id, request_params); } }; // src/models/translation_status.ts var TranslationStatus = class extends BaseModel { }; // src/collections/translation_statuses.ts var TranslationStatuses = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/custom_translation_statuses/{:id}"; get elementClass() { return TranslationStatus; } get rootElementName() { return "custom_translation_statuses"; } get rootElementNameSingular() { return "custom_translation_status"; } list(request_params) { return this.doList(request_params); } create(translation_status_params, request_params) { return this.doCreate( translation_status_params, request_params, this.populateObjectFromJsonRoot ); } get(translation_status_id, request_params) { return this.doGet(translation_status_id, request_params); } update(translation_status_id, translation_status_params, request_params) { return this.doUpdate( translation_status_id, translation_status_params, request_params ); } delete(translation_status_id, request_params) { return this.doDelete(translation_status_id, request_params); } available_colors(request_params) { return this.createPromise( "GET", request_params, this.returnBareJSON, {}, "projects/{!:project_id}/custom_translation_statuses/colors" ); } }; // src/models/translation.ts var Translation = class extends BaseModel { }; // src/collections/translations.ts var Translations = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/translations/{:id}"; get elementClass() { return Translation; } get rootElementName() { return "translations"; } get rootElementNameSingular() { return "translation"; } list(request_params) { return this.doListCursor(request_params); } get(translation_id, request_params) { return this.doGet(translation_id, request_params); } update(translation_id, translation_params, request_params) { return this.doUpdate(translation_id, translation_params, request_params); } }; // src/models/user_group.ts var UserGroup = class extends BaseModel { }; // src/collections/user_groups.ts var UserGroups = class extends BaseCollection { static prefixURI = "teams/{!:team_id}/groups/{:id}"; get elementClass() { return UserGroup; } get rootElementName() { return "user_groups"; } get rootElementNameSingular() { return null; } list(request_params) { return this.doList(request_params); } create(user_group_params, request_params) { return this.doCreate( user_group_params, request_params, this.populateGroupFromJsonRoot ); } get(user_group_id, request_params) { return this.doGet(user_group_id, request_params); } update(user_group_id, user_group_params, request_params) { return this.doUpdate( user_group_id, user_group_params, request_params, this.populateGroupFromJsonRoot ); } delete(user_group_id, request_params) { return this.doDelete(user_group_id, request_params); } add_members_to_group(team_id, group_id, user_ids) { const params = { team_id, group_id }; const body = { users: user_ids }; return this.createPromise( "PUT", params, this.populateGroupFromJsonRoot, body, "teams/{!:team_id}/groups/{!:group_id}/members/add" ); } remove_members_from_group(team_id, group_id, user_ids) { const params = { team_id, group_id }; const body = { users: user_ids }; return this.createPromise( "PUT", params, this.populateGroupFromJsonRoot, body, "teams/{!:team_id}/groups/{!:group_id}/members/remove" ); } add_projects_to_group(team_id, group_id, project_ids) { const params = { team_id, group_id }; const body = { projects: project_ids }; return this.createPromise( "PUT", params, this.populateGroupFromJsonRoot, body, "teams/{!:team_id}/groups/{!:group_id}/projects/add" ); } remove_projects_from_group(team_id, group_id, project_ids) { const params = { team_id, group_id }; const body = { projects: project_ids }; return this.createPromise( "PUT", params, this.populateGroupFromJsonRoot, body, "teams/{!:team_id}/groups/{!:group_id}/projects/remove" ); } populateGroupFromJsonRoot(json, headers) { const formatted_json = json.group; return this.populateObjectFromJson(formatted_json, headers); } }; // src/models/webhook.ts var Webhook = class extends BaseModel { }; // src/collections/webhooks.ts var Webhooks = class extends BaseCollection { static prefixURI = "projects/{!:project_id}/webhooks/{:id}"; get elementClass() { return Webhook; } get rootElementName() { return "webhooks"; } get rootElementNameSingular() { return "webhook"; } list(request_params) { return this.doList(request_params); } create(webhook_params, request_params) { return this.doCreate( webhook_params, request_params, this.populateObjectFromJsonRoot ); } get(webhook_id, request_params) { return this.doGet(webhook_id, request_params); } update(webhook_id, webhook_params, request_params) { return this.doUpdate(webhook_id, webhook_params, request_params); } delete(webhook_id, request_params) { return this.doDelete(webhook_id, request_params); } regenerate_secret(webhook_id, request_params) { const params = { ...request_params, ...{ id: webhook_id } }; return this.createPromise( "PATCH", params, this.returnBareJSON, null, "projects/{!:project_id}/webhooks/{:id}/secret/regenerate" ); } }; // src/lokalise/base_client.ts var BaseClient = class { /** * Internal client data including token, token type, host, compression, and timeouts. */ clientData = { token: "", tokenType: "", authHeader: "x-api-token", enableCompression: false, requestTimeout: 0, silent: false }; /** * Constructs a new BaseClient instance. * @param params - Configuration parameters including API key and optional features. * @throws Error if the API key is not provided or is empty. */ constructor({ apiKey, enableCompression = false, silent = false, tokenType = "", host, requestTimeout, userAgent = void 0 }) { if (typeof apiKey !== "string" || apiKey.trim().length === 0) { throw new Error( "Instantiation failed: A non-empty API key or JWT must be provided." ); } this.clientData.token = apiKey; this.clientData.enableCompression = enableCompression; this.clientData.silent = silent; this.clientData.tokenType = tokenType; this.clientData.host = host; this.clientData.requestTimeout = requestTimeout ?? 0; this.clientData.userAgent = userAgent; } }; // src/lokalise/lokalise_api.ts var LokaliseApi = class extends BaseClient { /** * Creates a new instance of the LokaliseApi client