{"id":19889539,"url":"https://github.com/tsmx/json-tools","last_synced_at":"2026-05-12T11:36:26.923Z","repository":{"id":116727446,"uuid":"429172073","full_name":"tsmx/json-tools","owner":"tsmx","description":"A JSON toolset based on json-traverse.","archived":false,"fork":false,"pushed_at":"2024-11-28T20:56:59.000Z","size":966,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-11T19:23:46.051Z","etag":null,"topics":["json","object-manipulation","tool","toolset"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tsmx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-17T19:19:18.000Z","updated_at":"2024-11-28T20:57:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"6b228c40-8c75-4230-b0d6-03efe8862319","html_url":"https://github.com/tsmx/json-tools","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsmx%2Fjson-tools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsmx%2Fjson-tools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsmx%2Fjson-tools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsmx%2Fjson-tools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tsmx","download_url":"https://codeload.github.com/tsmx/json-tools/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241317601,"owners_count":19943202,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["json","object-manipulation","tool","toolset"],"created_at":"2024-11-12T18:10:39.883Z","updated_at":"2026-05-12T11:36:26.915Z","avatar_url":"https://github.com/tsmx.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n![npm (scoped)](https://img.shields.io/npm/v/@tsmx/json-tools)\n![node-current (scoped)](https://img.shields.io/node/v/@tsmx/json-tools)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/tsmx/json-tools/git-build.yml?branch=master)](https://img.shields.io/github/actions/workflow/status/tsmx/json-tools/git-build.yml?branch=master)\n[![Coverage Status](https://coveralls.io/repos/github/tsmx/json-tools/badge.svg?branch=master)](https://coveralls.io/github/tsmx/json-tools?branch=master)\n\n# json-tools\n\n\u003e A comprehensive toolkit for analyzing, transforming, encrypting/decrypting and obfuscating JSON data. Ideal for pre-processing in your AI apps securing data and saving tokens.\n\n🔒 [encryption functions](#encryption-functions-encrypt--decrypt) to protect sensitive data with AES-256-GCM including tamper-safety and decrypt back again\n- credit card numbers (supporting Visa, Mastercard and Amex numbers)\n- IP addresses\n- arbitrary strings\n- values/keys by RegEx\n\n🥷 [obfuscation functions](#obfuscation-functions-obfuscate) to obscure sensible data\n- credit card numbers (supporting Visa, Mastercard and Amex numbers)\n- IP addresses\n- values by key\n- numbers\n- arbitrary strings\n- values/keys by RegEx\n\n⚡ [transformation functions](#transformation-functions-transform) to generate alternative formats out of a JSON object\n- array\n- map\n- properties string\n- [**token-optimized LLM representation**](#transformtollmobj-compactarrays)\n\n🔍 [utility functions](#utility-functions) for basic analytics of JSON objects\n - check complexity\n - determine nesting depth\n - analyze type stats\n\nSee the [API reference](#api-reference) down below for the full specs and examples.\n\n👉 Practical use-case: Suppose you have the following JSON object that serves as input in your AI application:\n\n```JSON\n{\n    \"accounts\": [\n        { \"id\": 1, \"name\": \"Joe\", \"creditCard\": \"4111-1111-1111-1111\" },\n        { \"id\": 2, \"name\": \"Sue\", \"creditCard\": \"5555-5555-5555-4444\" }\n    ],\n    \"visits\": [\n        { \"visitorId\": 1, \"timestamp\": \"2025-01-01T12:00:00Z\", \"ip\": \"192.168.1.1\", \"site\": \"index.html\"},\n        { \"visitorId\": 1, \"timestamp\": \"2025-01-01T13:05:00Z\", \"ip\": \"192.168.1.2\", \"site\": \"shop.html\"},\n        { \"visitorId\": 2, \"timestamp\": \"2025-01-01T14:00:00Z\", \"ip\": \"192.168.1.2\", \"site\": \"login.html\"},\n        { \"visitorId\": 2, \"timestamp\": \"2025-01-01T14:10:00Z\", \"ip\": \"192.168.1.1\", \"site\": \"index.html\"}\n    ]\n}\n```\n\nBefore feeding the data into any LLM, you need to...\n- obfuscate sensible data: IP addresses, credit card numbers\n- optimize the data representation to save tokens\n\nTo achieve this, simply do:\n\n```javascript\nconst jt = require('@tsmx/json-tools');\n\njt.obfuscate.ipAddresses(obj);\njt.obfuscate.creditCards(obj);\nconst result = jt.transform.toLLM(obj, true);\n```\n\nThis gives you the following result.\n\n```\naccounts[2](id,name,creditCard)\n -1\n  Joe\n  ***\n -2\n  Sue\n  ***\nvisits[4](visitorId,timestamp,ip,site)\n -1\n  2025-01-01T12:00:00Z\n  ***\n  index.html\n -1\n  2025-01-01T13:05:00Z\n  ***\n  shop.html\n -2\n  2025-01-01T14:00:00Z\n  ***\n  login.html\n -2\n  2025-01-01T14:10:00Z\n  ***\n  index.html\n```\n\n💰 For this example, the token count is reduced from 265 to 139 according to [OpenAI Tokenizer](https://platform.openai.com/tokenizer) for GPT-4o giving you a saving of 48% plus the safety of not exposing sensible data to the LLM.\n\n## API Reference\n\n### Encryption Functions (`encrypt` / `decrypt`)\n\nEncrypts sensitive string values in-place using **AES-256-GCM**, which provides both confidentiality and tamper-safety via an authentication tag. Any attempt to decrypt a tampered value will throw an error.\n\nEncrypted values are stored in the format `ENCRYPTED|IV|cipherText|authTag` directly in the object. This prefix allows the single `decrypt` function to reliably find and restore all encrypted values regardless of which `encrypt` function was used.\n\nThe `key` must be either a 32-character plain string or a 64-character hex string (both represent 32 bytes as required by AES-256).\n\n#### `encrypt.strings(obj, key)`\n\nEncrypts all string values in a JSON object using AES-256-GCM.\n\n**Parameters:**\n- `obj` (Object): The object to encrypt\n- `key` (string): The 32-character (or 64-character hex) encryption key\n\n**Example:**\n```javascript\nconst jt = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  lastName: 'Smith',\n  age: 30\n};\n\njt.encrypt.strings(input, 'mySecretKeyWith32CharactersXXXXX');\n// Result: {\n//   firstName: 'ENCRYPTED|a1b2...|c3d4...|e5f6...',\n//   lastName:  'ENCRYPTED|...',\n//   age: 30\n// }\n```\n\n#### `encrypt.creditCards(obj, key)`\n\nEncrypts credit card values in a JSON object using AES-256-GCM. Supports Visa, MasterCard, and Amex card numbers separated by dashes, dots, whitespaces, or without delimiters.\n\n**Parameters:**\n- `obj` (Object): The object to encrypt\n- `key` (string): The 32-character (or 64-character hex) encryption key\n\n**Example:**\n```javascript\nconst jt = require('@tsmx/json-tools');\n\nconst input = {\n  cardNumber: '4012-8888-8888-1881',\n  cardType: 'Visa'\n};\n\njt.encrypt.creditCards(input, 'mySecretKeyWith32CharactersXXXXX');\n// Result: { cardNumber: 'ENCRYPTED|...', cardType: 'Visa' }\n```\n\n#### `encrypt.ipAddresses(obj, key)`\n\nEncrypts IP address values (IPv4 and IPv6) in a JSON object using AES-256-GCM.\n\n**Parameters:**\n- `obj` (Object): The object to encrypt\n- `key` (string): The 32-character (or 64-character hex) encryption key\n\n**Example:**\n```javascript\nconst jt = require('@tsmx/json-tools');\n\nconst input = {\n  serverIp: '192.168.1.1',\n  clientIp: '10.0.0.5'\n};\n\njt.encrypt.ipAddresses(input, 'mySecretKeyWith32CharactersXXXXX');\n// Result: { serverIp: 'ENCRYPTED|...', clientIp: 'ENCRYPTED|...' }\n```\n\n#### `encrypt.keyRegex(obj, pattern, key)`\n\nEncrypts all values of a JSON object where the key matches a given RegEx pattern (case-insensitive).\n\n**Parameters:**\n- `obj` (Object): The object to encrypt\n- `pattern` (string): The RegEx pattern to match keys\n- `key` (string): The 32-character (or 64-character hex) encryption key\n\n**Example:**\n```javascript\nconst jt = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  lastName: 'Smith',\n  city: 'New York'\n};\n\njt.encrypt.keyRegex(input, 'name', 'mySecretKeyWith32CharactersXXXXX');\n// Result: { firstName: 'ENCRYPTED|...', lastName: 'ENCRYPTED|...', city: 'New York' }\n```\n\n#### `encrypt.valueRegex(obj, pattern, key)`\n\nEncrypts all values of a JSON object where the value matches a given RegEx pattern (case-insensitive).\n\n**Parameters:**\n- `obj` (Object): The object to encrypt\n- `pattern` (string): The RegEx pattern to match values\n- `key` (string): The 32-character (or 64-character hex) encryption key\n\n**Example:**\n```javascript\nconst jt = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  city: 'New York',\n  country: 'United States'\n};\n\njt.encrypt.valueRegex(input, 'ork', 'mySecretKeyWith32CharactersXXXXX');\n// Result: { firstName: 'John', city: 'ENCRYPTED|...', country: 'United States' }\n```\n\n#### `decrypt(obj, key)`\n\nDecrypts all values in a JSON object that were previously encrypted with any of the `encrypt` functions. Identifies encrypted values by the `ENCRYPTED|` prefix. Throws an error if a value carrying the prefix has been tampered with.\n\n**Parameters:**\n- `obj` (Object): The object to decrypt\n- `key` (string): The 32-character (or 64-character hex) encryption key\n\n**Example:**\n```javascript\nconst jt = require('@tsmx/json-tools');\n\nconst input = {\n  cardNumber: '4012-8888-8888-1881',\n  serverIp: '192.168.1.1',\n  name: 'John'\n};\n\njt.encrypt.creditCards(input, 'mySecretKeyWith32CharactersXXXXX');\njt.encrypt.ipAddresses(input, 'mySecretKeyWith32CharactersXXXXX');\n// input is now: { cardNumber: 'ENCRYPTED|...', serverIp: 'ENCRYPTED|...', name: 'John' }\n\njt.decrypt(input, 'mySecretKeyWith32CharactersXXXXX');\n// input is restored to: { cardNumber: '4012-8888-8888-1881', serverIp: '192.168.1.1', name: 'John' }\n```\n\n### Obfuscation Functions (`obfuscate`)\n\n#### `obfuscate.strings(obj, replacement, retain, minreplace)`\n\nObfuscates all string values in a JSON object by replacing characters with a replacement character while retaining a specified number of left-most characters.\n\n**Parameters:**\n- `obj` (Object): The object to obfuscate\n- `replacement` (string, optional): The replacement character (default: `*`)\n- `retain` (number, optional): The number of left-most characters to retain (default: `3`)\n- `minreplace` (number, optional): The minimal number of replacement characters to use (default: `3`)\n\n**Example:**\n```javascript\nconst { obfuscate } = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  lastName: 'Smith',\n  city: 'New York'\n};\n\nobfuscate.strings(input);\n// Result: { firstName: 'Joh***', lastName: 'Smi***', city: 'New***' }\n```\n\n#### `obfuscate.numbers(obj, replacement)`\n\nObfuscates all number values in a JSON object by replacing them with a given string.\n\n**Parameters:**\n- `obj` (Object): The object to obfuscate\n- `replacement` (string, optional): The replacement string (default: `***`)\n\n**Example:**\n```javascript\nconst { obfuscate } = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  age: 30,\n  salary: 50000\n};\n\nobfuscate.numbers(input);\n// Result: { firstName: 'John', age: '***', salary: '***' }\n```\n\n#### `obfuscate.ipAddresses(obj, replacement)`\n\nObfuscates IP address values (IPv4 and IPv6) in a JSON object by replacing them with a given string.\n\n**Parameters:**\n- `obj` (Object): The object to obfuscate\n- `replacement` (string, optional): The replacement string (default: `***`)\n\n**Example:**\n```javascript\nconst { obfuscate } = require('@tsmx/json-tools');\n\nconst input = {\n  serverIp: '192.168.1.1',\n  clientIp: '10.0.0.5'\n};\n\nobfuscate.ipAddresses(input);\n// Result: { serverIp: '***', clientIp: '***' }\n```\n\n#### `obfuscate.creditCards(obj, replacement)`\n\nObfuscates credit card values in a JSON object by replacing them with a given string. Supports Visa, MasterCard, and Amex card numbers separated by dashes, dots, whitespaces, or without delimiters.\n\n**Parameters:**\n- `obj` (Object): The object to obfuscate\n- `replacement` (string, optional): The replacement string (default: `***`)\n\n**Example:**\n```javascript\nconst { obfuscate } = require('@tsmx/json-tools');\n\nconst input = {\n  cardNumber: '4012-8888-8888-1881',\n  cardType: 'Visa'\n};\n\nobfuscate.creditCards(input);\n// Result: { cardNumber: '***', cardType: 'Visa' }\n```\n\n#### `obfuscate.keyRegex(obj, pattern, replacement)`\n\nObfuscates all values of a JSON object where the key matches a given RegEx pattern (case-insensitive).\n\n**Parameters:**\n- `obj` (Object): The object to obfuscate\n- `pattern` (string): The RegEx pattern to match keys\n- `replacement` (string, optional): The replacement string (default: `***`)\n\n**Example:**\n```javascript\nconst { obfuscate } = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  lastName: 'Smith',\n  email: 'john@example.com'\n};\n\nobfuscate.keyRegex(input, 'name');\n// Result: { firstName: '***', lastName: '***', email: 'john@example.com' }\n```\n\n#### `obfuscate.valueRegex(obj, pattern, replacement)`\n\nObfuscates all values of a JSON object where the value matches a given RegEx pattern (case-insensitive).\n\n**Parameters:**\n- `obj` (Object): The object to obfuscate\n- `pattern` (string): The RegEx pattern to match values\n- `replacement` (string, optional): The replacement string (default: `***`)\n\n**Example:**\n```javascript\nconst { obfuscate } = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  city: 'New York',\n  country: 'United States'\n};\n\nobfuscate.valueRegex(input, 'ork');\n// Result: { firstName: 'John', city: '***', country: 'United States' }\n```\n\n### Transformation Functions (`transform`)\n\n#### `transform.toMap(obj)`\n\nConverts a JSON object to a JavaScript Map containing all root-level object properties as entries with their property names as keys.\n\n**Parameters:**\n- `obj` (Object): The object to convert\n\n**Returns:** Map - A Map containing all root-level properties\n\n**Example:**\n```javascript\nconst { transform } = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  lastName: 'Smith',\n  age: 30\n};\n\nconst result = transform.toMap(input);\n// Result: Map { 'firstName' =\u003e 'John', 'lastName' =\u003e 'Smith', 'age' =\u003e 30 }\n```\n\n#### `transform.toArray(obj)`\n\nConverts a JSON object to an Array containing {key, value} objects for all root-level properties.\n\n**Parameters:**\n- `obj` (Object): The object to convert\n\n**Returns:** Array - An Array of {key, value} objects\n\n**Example:**\n```javascript\nconst { transform } = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  lastName: 'Smith',\n  age: 30\n};\n\nconst result = transform.toArray(input);\n// Result: \n// [\n//   { key: 'firstName', value: 'John' },\n//   { key: 'lastName', value: 'Smith' },\n//   { key: 'age', value: 30 }\n// ]\n```\n\n#### `transform.toProperties(obj, expandArrays)`\n\nConverts a JSON object to a properties file string. Nested subobjects and their values are resolved into properties with full dot-separated paths (e.g., `country.name=USA`). Line endings are `\\r\\n`.\n\n**Parameters:**\n- `obj` (Object): The object to convert\n- `expandArrays` (boolean, optional): If true, creates an entry for each array element with zero-based index (default: `false`)\n\n**Returns:** String - A properties file formatted string\n\n**Example:**\n```javascript\nconst { transform } = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  lastName: 'Smith',\n  country: {\n    name: 'USA',\n    code: 'US'\n  }\n};\n\nconst result = transform.toProperties(input);\n// Result:\n// firstName=John\n// lastName=Smith\n// country.name=USA\n// country.code=US\n```\n\n#### `transform.toPropertiesFlat(obj, expandArrays)`\n\nConverts a JSON object to a properties file string considering only the root level. Nested subobjects and arrays are printed as stringified JSON.\n\n**Parameters:**\n- `obj` (Object): The object to convert\n- `expandArrays` (boolean, optional): If true, creates an entry for each array element with zero-based index (default: `false`)\n\n**Returns:** String - A flat properties file formatted string\n\n**Example:**\n```javascript\nconst { transform } = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  lastName: 'Smith',\n  country: {\n    name: 'USA',\n    code: 'US'\n  }\n};\n\nconst result = transform.toPropertiesFlat(input);\n// Result:\n// firstName=John\n// lastName=Smith\n// country={\"name\":\"USA\",\"code\":\"US\"}\n```\n\n#### `transform.toLLM(obj, compactArrays)`\n\nConverts a JSON object to an LLM-friendly, token-saving notation optimized for further processing in AI applications.\n\n**Parameters:**\n- `obj` (any): The object to convert\n- `compactArrays` (boolean, optional): If true, arrays containing only identical objects (same keys and order) will be further compacted (default: `false`)\n\n**Returns:** String - A token-optimized LLM notation string\n\n**Example 1 - Simple object:**\n```javascript\nconst { transform } = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  lastName: 'Smith',\n  age: 30\n};\n\nconst result = transform.toLLM(input);\n// Result:\n// firstName=John\n// lastName=Smith\n// age=30\n```\n\n**Example 2 - Array with non-identical objects (different key order):**\n```javascript\nconst { transform } = require('@tsmx/json-tools');\n\nconst input = {\n  accounts: [\n    { id: 1, name: 'Joe' },\n    { name: 'Sue', id: 2 }\n  ]\n};\n\nconst result = transform.toLLM(input);\n// Result:\n// accounts[2]\n//  -id=1\n//   name=Joe\n//  -name=Sue\n//   id=2\n```\n\n**Example 3 - Array with identical objects (compacted):**\n```javascript\nconst { transform } = require('@tsmx/json-tools');\n\nconst input = {\n  accounts: [\n    { id: 1, name: 'Joe' },\n    { id: 2, name: 'Sue' },\n    { id: 3, name: 'Alice' }\n  ]\n};\n\nconst result = transform.toLLM(input, true);\n// Result:\n// accounts[3](id, name)\n//  -1\n//   Joe\n//  -2\n//   Sue\n//  -3\n//   Alice\n```\n\n### Utility Functions\n\n#### `getDepth(obj, includeArrays)`\n\nRetrieves the nesting level (depth) of a JSON object. The root level is considered to be zero.\n\n**Parameters:**\n- `obj` (Object): The object to inspect\n- `includeArrays` (boolean, optional): Whether to consider objects within arrays when calculating depth (default: `true`)\n\n**Returns:** Number - The zero-based depth of the JSON structure\n\n**Example:**\n```javascript\nconst jt  = require('@tsmx/json-tools');\n\nconst simple = { firstName: 'John', age: 30 };\nconst nested = { user: { firstName: 'John', address: { city: 'NYC' } } };\n\njt.getDepth(simple); // Result: 0\njt.getDepth(nested); // Result: 2\n```\n\n#### `isSimple(obj, includeArrays)`\n\nChecks if a JSON object is simple, meaning it has no nested objects (depth == 0).\n\n**Parameters:**\n- `obj` (Object): The object to inspect\n- `includeArrays` (boolean, optional): Whether to consider objects within arrays (default: `true`)\n\n**Returns:** Boolean - True if the object is simple\n\n**Example:**\n```javascript\nconst jt  = require('@tsmx/json-tools');\n\nconst simple = { firstName: 'John', age: 30 };\nconst nested = { user: { firstName: 'John' } };\n\njt.isSimple(simple); // Result: true\njt.isSimple(nested); // Result: false\n```\n\n#### `isComplex(obj, includeArrays)`\n\nChecks if a JSON object is complex, meaning it has nested objects (depth \u003e 0).\n\n**Parameters:**\n- `obj` (Object): The object to inspect\n- `includeArrays` (boolean, optional): Whether to consider objects within arrays (default: `true`)\n\n**Returns:** Boolean - True if the object is complex\n\n**Example:**\n```javascript\nconst jt  = require('@tsmx/json-tools');\n\nconst simple = { firstName: 'John', age: 30 };\nconst nested = { user: { firstName: 'John' } };\n\njt.isComplex(simple); // Result: false\njt.isComplex(nested); // Result: true\n```\n\n#### `typeStats(obj)`\n\nAnalyzes all values of an object and returns the number of occurrences per type in a Map. Performs deep parsing including subobjects and array elements.\n\n**Parameters:**\n- `obj` (Object): The object to analyze\n\n**Returns:** Map - A Map containing the count for every type found (e.g., `{ 'string' =\u003e 5, 'number' =\u003e 2, 'object' =\u003e 1 }`)\n\n**Example:**\n```javascript\nconst jt  = require('@tsmx/json-tools');\n\nconst input = {\n  firstName: 'John',\n  lastName: 'Smith',\n  age: 30,\n  active: true,\n  country: { name: 'USA' }\n};\n\nconst result = jt.typeStats(input);\n// Result: Map {\n//   'string' =\u003e 3,\n//   'number' =\u003e 1,\n//   'boolean' =\u003e 1,\n//   'object' =\u003e 1\n// }\n```\n\n---","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsmx%2Fjson-tools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftsmx%2Fjson-tools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsmx%2Fjson-tools/lists"}