diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..be21527
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,114 @@
+# Node.js
+node_modules/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Logs
+logs
+*.log
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Coverage directory used by tools like istanbul
+coverage/
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+public
+
+# Storybook build outputs
+.out
+.storybook-out
+
+# Temporary folders
+tmp/
+temp/
+
+# Editor directories and files
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# OS generated files
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+
+# Test configuration files
+test-config.json
+local-config.js
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..3f5c16b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,72 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.0.0] - 2025-09-26
+
+### Added
+
+- Initial release of Tryton RPC Client for JavaScript
+- Full JSON-RPC implementation compatible with Tryton server
+- TrytonClient class with comprehensive API
+- Connection pooling support with ServerPool
+- Smart caching system with LRU cache and expiration
+- Automatic session management and authentication
+- Helper methods for CRUD operations (create, read, write, delete)
+- Helper methods for search operations (search, searchRead, searchCount)
+- Type-safe serialization/deserialization of Tryton data types:
+ - Date/DateTime objects
+ - Decimal numbers
+ - Bytes/Buffer objects
+ - TimeDelta values
+- Comprehensive error handling:
+ - Fault errors for RPC issues
+ - ProtocolError for HTTP/connection issues
+ - ResponseError for invalid responses
+- Retry logic for failed requests
+- SSL/TLS support
+- Configurable timeouts and connection limits
+- Verbose logging support
+- Multiple example files demonstrating usage
+- Complete documentation in README.md
+- MIT-compatible dependencies (none required)
+
+### Features
+
+- ✅ Connection management with automatic session handling
+- ✅ Pool of reusable connections for efficiency
+- ✅ Smart caching with configurable expiration
+- ✅ Complete CRUD operation helpers
+- ✅ Advanced search functionality
+- ✅ Batch operations support
+- ✅ Parallel request execution
+- ✅ Comprehensive error handling
+- ✅ Type safety for Tryton data types
+- ✅ Server information methods
+- ✅ Cache management utilities
+- ✅ Connection cloning support
+- ✅ Configurable client options
+
+### Documentation
+
+- Complete API documentation in README.md
+- Basic usage examples
+- Advanced usage patterns
+- Error handling examples
+- Configuration options documentation
+
+### Examples
+
+- `examples/basic.js` - Simple usage example
+- `examples/test.js` - Comprehensive test suite
+- `examples/advanced.js` - Advanced patterns and best practices
+
+### Compatibility
+
+- Node.js >= 12.0.0
+- Tryton Server 6.x and 7.x
+- JSON-RPC protocol support
+- No external dependencies required
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3995df2
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+GNU GENERAL PUBLIC LICENSE
+Version 3, 29 June 2007
+
+Copyright (C) 2007 Free Software Foundation, Inc.
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We guarantee these rights by giving you
+certain conditions under which you can use, copy, distribute and modify
+the work.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+[Full GPL v3 license text would continue here...]
diff --git a/examples/advanced.js b/examples/advanced.js
new file mode 100644
index 0000000..8758c92
--- /dev/null
+++ b/examples/advanced.js
@@ -0,0 +1,200 @@
+/**
+ * Advanced usage examples for Tryton RPC Client
+ */
+
+const { TrytonClient, Fault, ProtocolError } = require("../src");
+
+async function advancedExamples() {
+ console.log("🔬 Advanced Tryton RPC Client Examples");
+ console.log("=====================================\n");
+
+ const client = new TrytonClient({
+ hostname: "localhost",
+ database: "tryton",
+ username: "admin",
+ password: "admin",
+ options: {
+ verbose: false,
+ cache: true,
+ keepMax: 6,
+ timeout: 45000,
+ },
+ });
+
+ try {
+ await client.connect();
+ console.log("✅ Connected to Tryton server\n");
+
+ // Example 1: Batch operations
+ console.log("📦 Example 1: Batch Operations");
+ console.log("------------------------------");
+
+ const batchData = [
+ { name: "Company A", code: "COMP_A" },
+ { name: "Company B", code: "COMP_B" },
+ { name: "Company C", code: "COMP_C" },
+ ];
+
+ const createdIds = await client.create("party.party", batchData);
+ console.log("Created parties:", createdIds);
+
+ // Read them back
+ const createdParties = await client.read("party.party", createdIds, [
+ "id",
+ "name",
+ "code",
+ ]);
+ console.log("Created party details:", createdParties);
+ console.log();
+
+ // Example 2: Complex search with domain
+ console.log("🔍 Example 2: Complex Search");
+ console.log("---------------------------");
+
+ const complexDomain = [
+ "OR",
+ [["name", "like", "Company%"]],
+ [["code", "like", "COMP_%"]],
+ ];
+
+ const foundIds = await client.search("party.party", complexDomain);
+ console.log("Found IDs with complex domain:", foundIds);
+
+ const foundParties = await client.searchRead(
+ "party.party",
+ complexDomain,
+ ["id", "name", "code"],
+ 0, // offset
+ 10 // limit
+ );
+ console.log("Found parties:", foundParties);
+ console.log();
+
+ // Example 3: Update operations
+ console.log("✏️ Example 3: Update Operations");
+ console.log("------------------------------");
+
+ if (createdIds.length > 0) {
+ await client.write("party.party", [createdIds[0]], {
+ name: "Updated Company Name",
+ });
+ console.log(`Updated party ${createdIds[0]}`);
+
+ // Verify update
+ const updated = await client.read(
+ "party.party",
+ [createdIds[0]],
+ ["id", "name", "code"]
+ );
+ console.log("Updated party:", updated[0]);
+ }
+ console.log();
+
+ // Example 4: Parallel operations
+ console.log("⚡ Example 4: Parallel Operations");
+ console.log("--------------------------------");
+
+ const parallelCalls = [
+ { method: "model.party.party.search_count", args: [[]] },
+ { method: "model.company.company.search_count", args: [[]] },
+ { method: "common.version", args: [] },
+ ];
+
+ const parallelResults = await client.callParallel(parallelCalls);
+ console.log("Parallel results:");
+ console.log("- Total parties:", parallelResults[0]);
+ console.log("- Total companies:", parallelResults[1]);
+ console.log("- Server version:", parallelResults[2]);
+ console.log();
+
+ // Example 5: Working with dates
+ console.log("📅 Example 5: Working with Dates");
+ console.log("-------------------------------");
+
+ const today = new Date();
+ const tomorrow = new Date(today);
+ tomorrow.setDate(tomorrow.getDate() + 1);
+
+ console.log("Today:", today.toISOString());
+ console.log("Tomorrow:", tomorrow.toISOString());
+
+ // Note: Date handling depends on your specific Tryton models
+ // This is just to show how dates are serialized
+ console.log();
+
+ // Example 6: Error handling patterns
+ console.log("⚠️ Example 6: Error Handling");
+ console.log("---------------------------");
+
+ try {
+ await client.call("nonexistent.model.method", []);
+ } catch (error) {
+ if (error instanceof Fault) {
+ console.log("✅ Caught RPC Fault:", error.faultCode);
+ } else if (error instanceof ProtocolError) {
+ console.log("✅ Caught Protocol Error:", error.errcode);
+ } else {
+ console.log("✅ Caught Generic Error:", error.message);
+ }
+ }
+
+ try {
+ await client.read("party.party", [999999], ["name"]);
+ } catch (error) {
+ console.log(
+ "✅ Caught read error (likely record not found):",
+ error.message
+ );
+ }
+ console.log();
+
+ // Example 7: Cache usage
+ console.log("💾 Example 7: Cache Usage");
+ console.log("------------------------");
+
+ // First call - will hit server
+ console.time("First call");
+ const result1 = await client.read("party.party", [1], ["name"]);
+ console.timeEnd("First call");
+
+ // Second call - should be faster due to cache
+ console.time("Second call (cached)");
+ const result2 = await client.read("party.party", [1], ["name"]);
+ console.timeEnd("Second call (cached)");
+
+ console.log(
+ "Results are equal:",
+ JSON.stringify(result1) === JSON.stringify(result2)
+ );
+
+ // Clear cache
+ client.clearCache();
+ console.log("Cache cleared");
+ console.log();
+
+ // Cleanup: Delete created test records
+ console.log("🧹 Cleanup: Deleting test records");
+ console.log("---------------------------------");
+
+ if (createdIds.length > 0) {
+ try {
+ await client.delete("party.party", createdIds);
+ console.log("✅ Deleted test records:", createdIds);
+ } catch (error) {
+ console.log("⚠️ Could not delete test records:", error.message);
+ }
+ }
+ } catch (error) {
+ console.error("❌ Advanced examples failed:", error.message);
+ console.error(error.stack);
+ } finally {
+ client.close();
+ console.log("\n👋 Advanced examples completed");
+ }
+}
+
+if (require.main === module) {
+ advancedExamples().catch(console.error);
+}
+
+module.exports = { advancedExamples };
diff --git a/examples/basic.js b/examples/basic.js
new file mode 100644
index 0000000..e095314
--- /dev/null
+++ b/examples/basic.js
@@ -0,0 +1,68 @@
+/**
+ * Basic usage example of Tryton RPC Client
+ * Simple example similar to the README
+ */
+
+const { TrytonClient } = require("../src/client");
+
+async function basicExample() {
+ console.log("📖 Basic Tryton RPC Client Example");
+ console.log("==================================\n");
+
+ // Create client
+ const client = new TrytonClient({
+ hostname: "https://demo7.6.tryton.org", // Using HTTPS demo server
+ database: "demo7.6",
+ username: "admin",
+ password: "admin",
+ port: 8000,
+ language: "en",
+ });
+
+ try {
+ // Connect
+ console.log("Connecting...");
+ await client.connect();
+ console.log("✅ Connected!\n");
+
+ // Read a party
+ console.log("Reading party with ID 1...");
+ const party = await client.read(
+ "party.party",
+ [1],
+ ["id", "name", "code"]
+ );
+ console.log("Party:", party[0]);
+ console.log();
+
+ // Create a new party
+ console.log("Creating new party...");
+ const newIds = await client.create("party.party", [
+ { name: "Test Party from JS" },
+ ]);
+ console.log("Created party with ID:", newIds[0]);
+ console.log();
+
+ // Search for parties
+ console.log("Searching for parties...");
+ const searchResults = await client.searchRead(
+ "party.party",
+ [["name", "like", "Test%"]],
+ ["id", "name"],
+ 0, // offset
+ 5 // limit
+ );
+ console.log("Found parties:", searchResults);
+ } catch (error) {
+ console.error("❌ Error:", error.message);
+ } finally {
+ client.close();
+ console.log("\n👋 Connection closed");
+ }
+}
+
+if (require.main === module) {
+ basicExample().catch(console.error);
+}
+
+module.exports = { basicExample };
diff --git a/examples/test-parties.js b/examples/test-parties.js
new file mode 100644
index 0000000..adf9b03
--- /dev/null
+++ b/examples/test-parties.js
@@ -0,0 +1,126 @@
+/**
+ * Test para obtener terceros (parties) de Tryton
+ * Muestra el nombre de cada tercero y el total de terceros
+ */
+
+const { TrytonClient } = require("../src/client");
+
+async function testParties() {
+ console.log("🏢 Test de Terceros (Parties) en Tryton");
+ console.log("=======================================\n");
+
+ // Configuración del servidor Tryton
+ // Modifica estos valores según tu servidor
+ //
+ // Ejemplos de configuración:
+ //
+ // Para servidor local:
+ // hostname: "localhost" o "127.0.0.1"
+ //
+ // Para servidor remoto HTTP:
+ // hostname: "mi-servidor.com"
+ //
+ // Para servidor remoto HTTPS:
+ // hostname: "https://mi-servidor.com"
+ //
+ // Para servidor demo:
+ // hostname: "https://demo7.6.tryton.org", database: "demo7.6"
+ const config = {
+ hostname: "https://naliia.onecluster.com.co", // Sin barra final
+ database: "tryton", // Cambia por el nombre de tu base de datos
+ username: "admin", // Tu usuario de Tryton
+ password: "admin", // Tu contraseña
+ port: 8000, // Puerto del servidor (8000 para HTTP, 8443 para HTTPS)
+ language: "es", // Idioma (es, en, fr, etc.)
+ options: {
+ verbose: true, // Activar para ver qué está pasando
+ },
+ };
+
+ // Crear cliente
+ const client = new TrytonClient(config);
+
+ try {
+ console.log("📡 Conectando al servidor Tryton...");
+ await client.connect();
+ console.log("✅ Conexión exitosa!\n");
+
+ // 1. Obtener el total de terceros
+ console.log("🔢 Obteniendo cantidad total de terceros...");
+ const totalParties = await client.searchCount("party.party", []);
+ console.log(`📊 Total de terceros en el sistema: ${totalParties}\n`);
+
+ // 2. Obtener todos los terceros con información básica
+ console.log("👥 Obteniendo lista de todos los terceros...");
+ const parties = await client.searchRead(
+ "party.party",
+ [], // Sin filtros, obtener todos
+ ["id", "name", "code"], // Campos que queremos
+ 0, // offset
+ null, // sin límite para obtener todos
+ null, // sin orden específico
+ {} // contexto vacío
+ );
+
+ console.log(
+ `\n📋 Lista completa de terceros (${parties.length} encontrados):`
+ );
+ console.log("=" + "=".repeat(60));
+
+ // Mostrar cada tercero
+ parties.forEach((party, index) => {
+ const number = (index + 1).toString().padStart(3, " ");
+ const id = party.id.toString().padStart(4, " ");
+ const code = party.code ? `[${party.code}]` : "[Sin código]";
+ const name = party.name || "Sin nombre";
+
+ console.log(`${number}. ID:${id} ${code.padEnd(12)} ${name}`);
+ });
+
+ console.log("=" + "=".repeat(60));
+ console.log(`\n✨ Resumen:`);
+ console.log(` • Total de terceros: ${totalParties}`);
+ console.log(` • Terceros mostrados: ${parties.length}`);
+ console.log(
+ ` • Terceros con nombre: ${
+ parties.filter((p) => p.name && p.name.trim()).length
+ }`
+ );
+ console.log(
+ ` • Terceros con código: ${
+ parties.filter((p) => p.code && p.code.trim()).length
+ }`
+ );
+
+ // Mostrar algunos terceros destacados si los hay
+ const partiesWithNames = parties.filter(
+ (p) => p.name && p.name.trim().length > 0
+ );
+ if (partiesWithNames.length > 0) {
+ console.log(`\n🌟 Algunos terceros destacados:`);
+ partiesWithNames.slice(0, 5).forEach((party) => {
+ console.log(` • ${party.name} (ID: ${party.id})`);
+ });
+ if (partiesWithNames.length > 5) {
+ console.log(` • ... y ${partiesWithNames.length - 5} más`);
+ }
+ }
+ } catch (error) {
+ console.error("\n❌ Error durante la ejecución:", error.message);
+ if (error.stack) {
+ console.error("\n📍 Stack trace:");
+ console.error(error.stack);
+ }
+ } finally {
+ console.log("\n🔌 Cerrando conexión...");
+ client.close();
+ console.log("👋 ¡Adiós!");
+ }
+}
+
+// Ejecutar si se llama directamente
+if (require.main === module) {
+ testParties().catch(console.error);
+}
+
+module.exports = { testParties };
diff --git a/examples/test.js b/examples/test.js
new file mode 100644
index 0000000..971593a
--- /dev/null
+++ b/examples/test.js
@@ -0,0 +1,154 @@
+/**
+ * Test script for Tryton RPC Client
+ * Equivalent to the Python test_client.py
+ */
+
+const { TrytonClient } = require("../src/client");
+
+async function main() {
+ console.log("🚀 Testing Tryton RPC Client for JavaScript");
+ console.log("============================================\n");
+
+ // Create client instance (equivalent to Python version)
+ const client = new TrytonClient({
+ hostname: "https://demo7.6.tryton.org", // Explicitly use HTTPS
+ database: "demo7.6",
+ username: "admin",
+ password: "admin",
+ port: 8000, // Keep original port but force HTTPS
+ language: "en",
+ options: {
+ verbose: true, // Enable logging to see requests/responses
+ cache: true, // Enable caching
+ keepMax: 4, // Maximum pooled connections
+ },
+ });
+
+ try {
+ console.log("📡 Connecting to Tryton server...");
+ await client.connect();
+ console.log("✅ Connected successfully!\n");
+
+ console.log("📋 Client configuration:");
+ console.log(JSON.stringify(client.getConfig(), null, 2));
+ console.log();
+
+ // Test 1: Read party record (equivalent to Python test)
+ console.log("🔍 Test 1: Reading party record...");
+ const readResult = await client.call("model.party.party.read", [
+ [1],
+ ["id", "name", "code"],
+ {},
+ ]);
+ console.log("📄 Read result:", JSON.stringify(readResult, null, 2));
+ console.log();
+
+ // Test 2: Create party record (equivalent to Python test)
+ console.log("➕ Test 2: Creating new party record...");
+ const createResult = await client.call("model.party.party.create", [
+ [{ name: "Desde JavaScript" }],
+ {},
+ ]);
+ console.log("🆕 Create result (new IDs):", createResult);
+ console.log();
+
+ // Test 3: Using helper methods
+ console.log("🛠️ Test 3: Using helper methods...");
+
+ // Read using helper method
+ const parties = await client.read(
+ "party.party",
+ [1],
+ ["id", "name", "code"]
+ );
+ console.log("👥 Parties (helper method):", parties);
+
+ // Search for parties
+ const partyIds = await client.search(
+ "party.party",
+ [["name", "like", "%"]],
+ 0,
+ 5
+ );
+ console.log("🔎 Found party IDs:", partyIds);
+
+ // Search and read in one call
+ const partyRecords = await client.searchRead(
+ "party.party",
+ [["id", "in", partyIds.slice(0, 3)]],
+ ["id", "name", "code"]
+ );
+ console.log("📋 Party records:", partyRecords);
+
+ // Count parties
+ const partyCount = await client.searchCount("party.party", []);
+ console.log("📊 Total parties count:", partyCount);
+ console.log();
+
+ // Test 4: Server information
+ console.log("ℹ️ Test 4: Server information...");
+
+ try {
+ const version = await client.getVersion();
+ console.log("🏷️ Server version:", version);
+ } catch (error) {
+ console.log("⚠️ Could not get version:", error.message);
+ }
+
+ try {
+ const databases = await client.listDatabases();
+ console.log("🗄️ Available databases:", databases);
+ } catch (error) {
+ console.log("⚠️ Could not list databases:", error.message);
+ }
+ console.log();
+
+ // Test 5: Error handling
+ console.log("❌ Test 5: Error handling...");
+ try {
+ await client.call("invalid.method.name", []);
+ } catch (error) {
+ console.log("✅ Correctly caught error:", error.message);
+ }
+ console.log();
+
+ // Test 6: Multiple calls
+ console.log("🔄 Test 6: Multiple calls...");
+ const multipleCalls = [
+ { method: "model.party.party.read", args: [[1], ["name"]] },
+ { method: "model.party.party.search_count", args: [[]] },
+ ];
+
+ const multiResults = await client.callMultiple(multipleCalls);
+ console.log("🔢 Multiple call results:", multiResults);
+ console.log();
+
+ console.log("✅ All tests completed successfully!");
+ } catch (error) {
+ console.error("❌ Test failed with error:", error.message);
+ console.error("Stack trace:", error.stack);
+ } finally {
+ console.log("\n🔌 Closing connection...");
+ client.close();
+ console.log("👋 Goodbye!");
+ }
+}
+
+// Handle unhandled promise rejections
+process.on("unhandledRejection", (reason, promise) => {
+ console.error("Unhandled Rejection at:", promise, "reason:", reason);
+ process.exit(1);
+});
+
+// Handle uncaught exceptions
+process.on("uncaughtException", (error) => {
+ console.error("Uncaught Exception:", error);
+ process.exit(1);
+});
+
+// Run the test
+if (require.main === module) {
+ main().catch(console.error);
+}
+
+module.exports = { main };
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..e0f99bc
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1246 @@
+{
+ "name": "tryton-rpc-client-js",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "tryton-rpc-client-js",
+ "version": "1.0.0",
+ "license": "GPL-3.0",
+ "devDependencies": {
+ "eslint": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
+ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
+ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
+ "deprecated": "Use @eslint/config-array instead",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.3",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "deprecated": "Use @eslint/object-schema instead",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
+ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.1",
+ "@humanwhocodes/config-array": "^0.13.0",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0aab5e5
--- /dev/null
+++ b/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "tryton-rpc-client-js",
+ "version": "1.0.0",
+ "description": "JavaScript RPC Client for Tryton ERP Server",
+ "main": "src/client.js",
+ "scripts": {
+ "test": "node examples/test.js",
+ "example": "node examples/basic.js",
+ "lint": "eslint src/ examples/",
+ "start": "node examples/test.js"
+ },
+ "keywords": [
+ "tryton",
+ "rpc",
+ "client",
+ "javascript",
+ "nodejs",
+ "erp",
+ "json-rpc"
+ ],
+ "author": "Your Name",
+ "license": "GPL-3.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/your-username/tryton-rpc-client-js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/your-username/tryton-rpc-client-js/issues"
+ },
+ "homepage": "https://github.com/your-username/tryton-rpc-client-js#readme",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "files": [
+ "src/",
+ "examples/",
+ "README.md",
+ "LICENSE",
+ "CHANGELOG.md"
+ ],
+ "devDependencies": {
+ "eslint": "^8.0.0"
+ },
+ "dependencies": {},
+ "optionalDependencies": {},
+ "peerDependencies": {}
+}
diff --git a/src/cache.js b/src/cache.js
new file mode 100644
index 0000000..9b28d17
--- /dev/null
+++ b/src/cache.js
@@ -0,0 +1,254 @@
+/**
+ * Cache system similar to Python's CacheDict from Tryton
+ * Implements LRU (Least Recently Used) cache using JavaScript Map
+ */
+
+class CacheDict extends Map {
+ /**
+ * Create a new CacheDict
+ * @param {number} cacheLen - Maximum number of items to cache
+ * @param {Function} defaultFactory - Factory function for missing keys
+ */
+ constructor(cacheLen = 10, defaultFactory = null) {
+ super();
+ this.cacheLen = cacheLen;
+ this.defaultFactory = defaultFactory;
+ }
+
+ /**
+ * Set a key-value pair and maintain LRU order
+ * @param {*} key - The key
+ * @param {*} value - The value
+ * @returns {CacheDict} - This instance for chaining
+ */
+ set(key, value) {
+ // If key exists, delete it first to move to end
+ if (this.has(key)) {
+ this.delete(key);
+ }
+
+ super.set(key, value);
+
+ // Remove oldest entries if cache is full
+ while (this.size > this.cacheLen) {
+ const firstKey = this.keys().next().value;
+ this.delete(firstKey);
+ }
+
+ return this;
+ }
+
+ /**
+ * Get a value and move it to end (most recently used)
+ * @param {*} key - The key to retrieve
+ * @returns {*} - The value
+ */
+ get(key) {
+ if (this.has(key)) {
+ const value = super.get(key);
+ // Move to end by re-setting
+ this.delete(key);
+ super.set(key, value);
+ return value;
+ }
+
+ // Handle missing key with default factory
+ if (this.defaultFactory) {
+ const value = this.defaultFactory();
+ this.set(key, value);
+ return value;
+ }
+
+ return undefined;
+ }
+
+ /**
+ * Override has() to update LRU order on access
+ * @param {*} key - The key to check
+ * @returns {boolean} - Whether the key exists
+ */
+ has(key) {
+ const exists = super.has(key);
+ if (exists) {
+ // Move to end on access
+ const value = super.get(key);
+ this.delete(key);
+ super.set(key, value);
+ }
+ return exists;
+ }
+
+ /**
+ * Get current cache size
+ * @returns {number} - Number of items in cache
+ */
+ get length() {
+ return this.size;
+ }
+
+ /**
+ * Clear all items from cache
+ */
+ clear() {
+ super.clear();
+ }
+
+ /**
+ * Convert cache to array for debugging
+ * @returns {Array} - Array of [key, value] pairs
+ */
+ toArray() {
+ return Array.from(this.entries());
+ }
+}
+
+/**
+ * Advanced cache for Tryton RPC with expiration support
+ */
+class TrytonCache {
+ constructor(cacheLen = 1024) {
+ this.store = new CacheDict(cacheLen, () => new CacheDict(cacheLen));
+ }
+
+ /**
+ * Check if a prefix is cached
+ * @param {string} prefix - Method prefix
+ * @returns {boolean} - Whether prefix exists
+ */
+ cached(prefix) {
+ return this.store.has(prefix);
+ }
+
+ /**
+ * Set cache entry with expiration
+ * @param {string} prefix - Method prefix
+ * @param {string} key - Cache key
+ * @param {number|Date} expire - Expiration time
+ * @param {*} value - Value to cache
+ */
+ set(prefix, key, expire, value) {
+ let expiration;
+
+ if (typeof expire === "number") {
+ // Assume seconds, convert to Date
+ expiration = new Date(Date.now() + expire * 1000);
+ } else if (expire instanceof Date) {
+ expiration = expire;
+ } else {
+ throw new Error("Invalid expiration type");
+ }
+
+ // Deep copy value to avoid mutations
+ const cachedValue = this._deepCopy(value);
+
+ this.store.get(prefix).set(key, {
+ expire: expiration,
+ value: cachedValue,
+ });
+ }
+
+ /**
+ * Get cached value if not expired
+ * @param {string} prefix - Method prefix
+ * @param {string} key - Cache key
+ * @returns {*} - Cached value
+ * @throws {Error} - If key not found or expired
+ */
+ get(prefix, key) {
+ const now = new Date();
+
+ if (!this.store.has(prefix)) {
+ throw new Error("Key not found");
+ }
+
+ const prefixCache = this.store.get(prefix);
+ if (!prefixCache.has(key)) {
+ throw new Error("Key not found");
+ }
+
+ const entry = prefixCache.get(key);
+
+ if (entry.expire < now) {
+ prefixCache.delete(key);
+ throw new Error("Key expired");
+ }
+
+ console.log(`(cached) ${prefix} ${key}`);
+ return this._deepCopy(entry.value);
+ }
+
+ /**
+ * Clear cache for a specific prefix or all
+ * @param {string} [prefix] - Optional prefix to clear
+ */
+ clear(prefix = null) {
+ if (prefix) {
+ if (this.store.has(prefix)) {
+ this.store.get(prefix).clear();
+ }
+ } else {
+ this.store.clear();
+ }
+ }
+
+ /**
+ * Deep copy objects to prevent mutations
+ * @param {*} obj - Object to copy
+ * @returns {*} - Deep copied object
+ * @private
+ */
+ _deepCopy(obj) {
+ if (obj === null || typeof obj !== "object") {
+ return obj;
+ }
+
+ if (obj instanceof Date) {
+ return new Date(obj.getTime());
+ }
+
+ if (obj instanceof Array) {
+ return obj.map((item) => this._deepCopy(item));
+ }
+
+ if (obj instanceof Buffer) {
+ return Buffer.from(obj);
+ }
+
+ if (typeof obj === "object") {
+ const copy = {};
+ for (const key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ copy[key] = this._deepCopy(obj[key]);
+ }
+ }
+ return copy;
+ }
+
+ return obj;
+ }
+
+ /**
+ * Get cache statistics
+ * @returns {Object} - Cache statistics
+ */
+ getStats() {
+ const stats = {
+ totalPrefixes: this.store.size,
+ totalEntries: 0,
+ prefixes: {},
+ };
+
+ for (const [prefix, prefixCache] of this.store.entries()) {
+ const count = prefixCache.size;
+ stats.totalEntries += count;
+ stats.prefixes[prefix] = count;
+ }
+
+ return stats;
+ }
+}
+
+module.exports = {
+ CacheDict,
+ TrytonCache,
+};
diff --git a/src/client.js b/src/client.js
new file mode 100644
index 0000000..69b3417
--- /dev/null
+++ b/src/client.js
@@ -0,0 +1,409 @@
+/**
+ * Main Tryton RPC Client
+ * JavaScript implementation of sabatron-tryton-rpc-client
+ */
+
+const { ServerProxy, ServerPool } = require("./jsonrpc");
+
+/**
+ * Main client class for connecting to Tryton server via RPC
+ */
+class TrytonClient {
+ /**
+ * Create a new Tryton client
+ * @param {Object} config - Configuration object
+ * @param {string} config.hostname - Server hostname
+ * @param {string} config.database - Database name
+ * @param {string} config.username - Username
+ * @param {string} config.password - Password
+ * @param {number} [config.port=8000] - Server port
+ * @param {string} [config.language='en'] - Language code
+ * @param {Object} [config.options={}] - Additional options
+ */
+ constructor({
+ hostname,
+ database,
+ username,
+ password,
+ port = 8000,
+ language = "en",
+ options = {},
+ }) {
+ // Extract protocol from hostname if present
+ if (hostname.startsWith("https://")) {
+ this.hostname = hostname.replace("https://", "");
+ this.useHttps = true;
+ } else if (hostname.startsWith("http://")) {
+ this.hostname = hostname.replace("http://", "");
+ this.useHttps = false;
+ } else {
+ this.hostname = hostname;
+ this.useHttps = port === 443 || port === 8443;
+ }
+
+ this.database = database;
+ this.username = username;
+ this.password = password;
+ this.port = port;
+ this.language = language;
+ this.options = options;
+ this.connection = null;
+ this.session = null;
+ }
+
+ /**
+ * Alternative constructor for backward compatibility
+ * @param {string} hostname - Server hostname
+ * @param {string} database - Database name
+ * @param {string} username - Username
+ * @param {string} password - Password
+ * @param {number} [port=8000] - Server port
+ * @param {string} [language='en'] - Language code
+ * @returns {TrytonClient} - New client instance
+ */
+ static create(
+ hostname,
+ database,
+ username,
+ password,
+ port = 8000,
+ language = "en"
+ ) {
+ return new TrytonClient({
+ hostname,
+ database,
+ username,
+ password,
+ port,
+ language,
+ });
+ }
+
+ /**
+ * Connect to Tryton server and authenticate
+ * @returns {Promise} - True if connection successful
+ * @throws {Error} - If connection or authentication fails
+ */
+ async connect() {
+ try {
+ // Create proxy for login
+ const proxy = new ServerProxy(
+ this.hostname,
+ this.port,
+ this.database,
+ {
+ verbose: this.options.verbose || false,
+ connectTimeout: this.options.connectTimeout,
+ timeout: this.options.timeout,
+ useHttps: this.useHttps,
+ }
+ );
+
+ // Perform login
+ const parameters = {
+ password: this.password,
+ };
+
+ const result = await proxy.request("common.db.login", [
+ this.username,
+ parameters,
+ this.language,
+ ]);
+
+ // Close temporary proxy
+ proxy.close();
+
+ // Create session string
+ this.session = [this.username, ...result].join(":");
+
+ // Create connection pool with session
+ this.connection = new ServerPool(
+ this.hostname,
+ this.port,
+ this.database,
+ {
+ session: this.session,
+ cache: this.options.cache !== false ? [] : null, // Enable cache by default
+ verbose: this.options.verbose || false,
+ connectTimeout: this.options.connectTimeout,
+ timeout: this.options.timeout,
+ keepMax: this.options.keepMax || 4,
+ useHttps: this.useHttps,
+ }
+ );
+
+ return true;
+ } catch (error) {
+ throw new Error(`Connection failed: ${error.message}`);
+ }
+ }
+
+ /**
+ * Call RPC method on server
+ * @param {string} methodName - RPC method name (e.g., 'model.party.party.read')
+ * @param {Array} args - Method arguments
+ * @returns {Promise<*>} - Method result
+ * @throws {Error} - If not connected or method call fails
+ */
+ async call(methodName, args = []) {
+ if (!this.connection) {
+ throw new Error("Not connected. Call connect() first.");
+ }
+
+ return this.connection.withConnection(async (conn) => {
+ return conn.request(methodName, args);
+ });
+ }
+
+ /**
+ * Call multiple RPC methods in sequence
+ * @param {Array<{method: string, args: Array}>} calls - Array of method calls
+ * @returns {Promise} - Array of results
+ */
+ async callMultiple(calls) {
+ const results = [];
+ for (const call of calls) {
+ const result = await this.call(call.method, call.args);
+ results.push(result);
+ }
+ return results;
+ }
+
+ /**
+ * Call multiple RPC methods in parallel
+ * @param {Array<{method: string, args: Array}>} calls - Array of method calls
+ * @returns {Promise} - Array of results
+ */
+ async callParallel(calls) {
+ const promises = calls.map((call) => this.call(call.method, call.args));
+ return Promise.all(promises);
+ }
+
+ /**
+ * Helper method to read records from a model
+ * @param {string} model - Model name (e.g., 'party.party')
+ * @param {Array} ids - Record IDs to read
+ * @param {Array} fields - Fields to read
+ * @param {Object} [context={}] - Context dictionary
+ * @returns {Promise} - Array of records
+ */
+ async read(model, ids, fields, context = {}) {
+ return this.call(`model.${model}.read`, [ids, fields, context]);
+ }
+
+ /**
+ * Helper method to create records in a model
+ * @param {string} model - Model name (e.g., 'party.party')
+ * @param {Array