{"id":22351023,"url":"https://github.com/testingrequired/reqlang","last_synced_at":"2025-10-08T02:59:00.072Z","repository":{"id":264281459,"uuid":"743298881","full_name":"testingrequired/reqlang","owner":"testingrequired","description":"A file format specification for defining HTTP requests, response assertions, and associated data/configuration in \"request files\".","archived":false,"fork":false,"pushed_at":"2025-01-29T05:57:43.000Z","size":2366,"stargazers_count":3,"open_issues_count":24,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-01-29T06:28:15.619Z","etag":null,"topics":["http","http-client","http-requests","language-server-protocol","specification","testing","testing-tools"],"latest_commit_sha":null,"homepage":"https://testingrequired.github.io/reqlang/reqlang/","language":"Rust","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/testingrequired.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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":"2024-01-14T22:53:15.000Z","updated_at":"2025-01-29T05:55:02.000Z","dependencies_parsed_at":"2024-12-13T03:24:15.031Z","dependency_job_id":"90f9a2fe-6b15-4d94-86ac-d6c9d55442cf","html_url":"https://github.com/testingrequired/reqlang","commit_stats":null,"previous_names":["testingrequired/reqlang"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testingrequired%2Freqlang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testingrequired%2Freqlang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testingrequired%2Freqlang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testingrequired%2Freqlang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/testingrequired","download_url":"https://codeload.github.com/testingrequired/reqlang/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236518731,"owners_count":19162119,"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":["http","http-client","http-requests","language-server-protocol","specification","testing","testing-tools"],"created_at":"2024-12-04T12:12:18.997Z","updated_at":"2025-10-08T02:59:00.052Z","avatar_url":"https://github.com/testingrequired.png","language":"Rust","readme":"# Request Language\n\nA file format specification for defining HTTP requests, response assertions, and configuration in \"request files\".\n\n## Goals\n\n- Can be treated as a markdown file\n- HTTP request and response messages\n- Easy to read, write, and diff\n- Lives in source control\n- Templating with variables, prompts, and secret values\n- Environments with environment specific variable values\n- Client/implementation agnostic\n- Statically typed [expression language](https://github.com/testingrequired/reqlang-expr) in the templates\n\n### Future\n\n- Chaining requests\n- Response body mapping/transformation/extraction\n- Authenticated requests (e.g. OAuth2) configuration\n- Project workspaces\n\n## Request Files\n\nRequest files (`*.reqlang`) are templated markdown files containing an HTTP request, HTTP response assertion, and configuration.\n\n### Living Syntax\n\nThis is a living syntax subject to change wildly at anytime. The core concepts and goals will remain the same however.\n\n### Example\n\n[post.reqlang](./examples/valid/post.reqlang):\n\n````reqlang\n```%config\nsecrets = [\"super_secret_value\"]\n\n[[prompts]]\nname = \"prompt_value\"\n\n[[vars]]\nname = \"test_value\"\n\n[envs.test]\ntest_value = \"test_value\"\n\n[envs.prod]\ntest_value = \"prod_value\"\n\n[envs.local]\ntest_value = \"local_value\"\n```\n\n```%request\nPOST https://httpbin.org/post HTTP/1.1\n\n{\n  \"env\": \"{{@env}}\",\n  \"value\": \"{{:test_value}}\",\n  \"prompted_value\": \"{{?prompt_value}}\",\n  \"secret_value\": \"{{!super_secret_value}}\"\n}\n```\n````\n\n### Request\n\nThe request is written as a [HTTP request message](https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages#http_requests) inside of a `%request` markdown code block.\n\n````reqlang\n```%request\nGET https://example.com HTTP/1.1\n```\n````\n\n### Response\n\nThe response assertion is the (optional) expected HTTP response message the actual response will be compared to. It's written inside of a `%response` markdown code block.\n\n````reqlang\n```%request\nGET https://example.com HTTP/1.1\n```\n\n```%response\nHTTP/1.1 200 OK\n```\n````\n\n#### Matching Rules\n\nClient implementations can choose how to match the response against the expected response. Here are a list of recommended ways to match.\n\n- Exact match `status code`\n- Exact match `status text`\n- Exact match `header value` of headers present in the expected response\n- Exact match `body`\n\n### Configuration\n\nThe configuration is TOML written in a `%config` markdown code block. Its where variables, environments, prompts, and secrets are declared/defined.\n\n#### Secrets\n\nSecrets are protected values referenced by a name and declares what secrets will be required. How secret values are fetched is up to client implementations. They can be referenced using the `{{!secret_name}}` syntax.\n\nSecrets are optional but if they are declared, they must be at the top of the config block (due to how TOML parses tables).\n\n```toml\nsecrets = [\"api_key\"]\n```\n\n##### Usage\n\n````reqlang\n```%config\nsecrets = [\"api_key\"]\n```\n\n```%request\nGET https://example.com HTTP/1.1\nx-api-key: {{!api_key}}\n```\n````\n\n##### Goals\n\n- Secret fetching is outside the scope of the request file\n\n###### Future\n\n- Configuring secret fetching in the workspace\n\n#### Variables \u0026 Environments\n\nVariables contain environmental variables that can be used in the request or response. A list of variable names is first declared.\n\nVariables can be templated using the `{{:var_name}}` syntax. The environment of the request execution can be referenced using the `{{@env}}` syntax.\n\n```toml\n[[vars]]\nname = \"user_id\"\n\n[[vars]]\nname = \"item_id\"\n```\n\nThen enviroments are declared with the appropriate values.\n\n```toml\n[[vars]]\nname = \"user_id\"\n\n[[vars]]\nname = \"item_id\"\n\n[envs.dev]\nuser_id = 12345\nitem_id = \"abcd\"\n\n[envs.prod]\nuser_id = 67890\nitem_id = \"efgh\"\n```\n\n##### Default values\n\n```toml\n[[vars]]\nname = \"user_id\"\ndefault = \"12345\"\n\n[[vars]]\nname = \"item_id\"\n\n[envs.dev]\nitem_id = \"abcd\"\n\n[envs.prod]\nuser_id = \"67890\"\nitem_id = \"efgh\"\n```\n\n##### Usage\n\n````reqlang\n```%config\n[[vars]]\nname = \"user_id\"\n\n[[vars]]\nname = \"item_id\"\n\n[envs.dev]\nuser_id = 12345\nitem_id = \"abcd\"\n\n[envs.prod]\nuser_id = 67890\nitem_id = \"efgh\"\n```\n\n```%request\nGET https://{{@env}}.example.com/users/{{:user_id}}/items/{{:item_id}} HTTP/1.1\n```\n````\n\n###### Warning\n\nBe sure to declare env definition blocks using the `[env.ENV]` syntax in TOML. You can use `env.ENV.name = value` but they must be at the top of the config block. This is due ot how TOML handles parsing tables.\n\n##### Goals\n\n- Clearly define everything the request and response will need\n- Declare environments once\n- Require variable declaration before definition\n\n###### Future\n\n- Value type\n\n#### Prompts\n\nPrompts are values provided by the user at request execution time. These are \"inputs\" to the request file. They can be templated in the request and responses using the `{{?prompt_name}}` syntax.\n\n```toml\n[[prompts]]\nname = \"tags\"\ndescription = \"Tags included as a query param\" # Optional\ndefault = \"tag1,tag2\" # Optional\n```\n\n##### Usage\n\n````reqlang\n```%config\n[[prompts]]\nname = \"tags\"\n```\n\n```%request\nGET https://example.com/posts?tags={{?tags}} HTTP/1.1\n```\n````\n\n### Examples\n\nSee [all examples](./examples) for more request files.\n\n## Libraries\n\n### Rust\n\nThe [reqlang](./reqlang/) crate is a library working with request files.\n\n- [API Docs](https://testingrequired.github.io/reqlang/reqlang/)\n\n```rust\nuse reqlang::prelude::*;\n\nlet request_file_text = fs::read_to_string(\"./path/to/requestfile.reqlang\")\n  .expect(\"Should have been able to read the file\");\n\nconst ast = Ast::from(\u0026request_file_text);\n\nconst parsed_request_file = parse(\u0026ast).expect(\"should be a valid request file\");\n```\n\n## Tooling\n\n[![build-artifacts](https://github.com/testingrequired/reqlang/actions/workflows/build-artifacts.yml/badge.svg)](https://github.com/testingrequired/reqlang/actions/workflows/build-artifacts.yml)\n\nThese act as both tooling for request file and reference implementations for clients.\n\n### CLI\n\nThe [`reqlang`](./cli) CLI validates and exports requests in to a variety of formats (`http`, `curl`, `json`).\n\n```shell\nreqlang\n```\n\n```\nCommand to work with request files\n\nUsage: reqlang [COMMAND]\n\nCommands:\n  export  Export request to specified format\n  ast     Produce an AST for a request file\n  parse   Parse a request file\n  run     Run a request file\n  help    Print this message or the help of the given subcommand(s)\n\nOptions:\n  -h, --help     Print help\n  -V, --version  Print version\n```\n\n#### Run\n\nExecute the request from a request file.\n\n```\nUsage: reqlang run [OPTIONS] \u003cpath\u003e\n\nArguments:\n  \u003cpath\u003e  Path to request file\n\nOptions:\n  -e, --env \u003cenv\u003e         Resolve with an environment\n  -P, --prompt \u003cprompts\u003e  Input a prompt value\n  -S, --secret \u003csecrets\u003e  Input a secret value\n  -f, --format \u003cformat\u003e   Format the response [default: http] [possible values: http, json, body]\n  -t, --test              Test if the response matches the expected response, if defined\n  -h, --help              Print help\n```\n\n##### Examples\n\n```shell\nreqlang run ./examples/valid/status_code.reqlang --prompt status_code=200\n```\n\n```\nHTTP/1.1 200 OK\ncontent-type: text/html; charset=utf-8\nconnection: keep-alive\ncontent-length: 0\nserver: gunicorn/19.9.0\naccess-control-allow-credentials: true\naccess-control-allow-origin: *\n```\n\n##### Testing Responses\n\nRun the response assertion, if defined in the request file, the response will be compared to the expected response.\n\n```shell\nreqlang run examples/valid/mismatch_response.reqlang --test\n```\n\nSee: [mismatch_response.reqlang](./examples/valid/mismatch_response.reqlang)\n\n```diff\nHTTP/1.1 200 OK\nconnection: keep-alive\nserver: gunicorn/19.9.0\naccess-control-allow-origin: *\naccess-control-allow-credentials: true\ndate: Sun, 02 Feb 2025 03:55:33 GMT\ncontent-type: application/json\ncontent-length: 429\n\n{\n  \"slideshow\": {\n    \"author\": \"Yours Truly\",\n    \"date\": \"date of publication\",\n    \"slides\": [\n      {\n        \"title\": \"Wake up to WonderWidgets!\",\n        \"type\": \"all\"\n      },\n      {\n        \"items\": [\n          \"Why \u003cem\u003eWonderWidgets\u003c/em\u003e are great\",\n          \"Who \u003cem\u003ebuys\u003c/em\u003e WonderWidgets\"\n        ],\n        \"title\": \"Overview\",\n        \"type\": \"all\"\n      }\n    ],\n    \"title\": \"Sample Slide Show\"\n  }\n}\n\nResponse assertion failed:\n\n-HTTP/1.1 201 Created\n+HTTP/1.1 200 OK\n-x-test-value: ...\n\n {\n   \"slideshow\": {\n-    \"author\": \"Yours Truly\",\n+    \"author\": \"Yours Truly\",\n+    \"date\": \"date of publication\",\n     \"slides\": [\n       {\n         \"title\": \"Wake up to WonderWidgets!\",\n         \"type\": \"all\"\n       },\n       {\n         \"items\": [\n           \"Why \u003cem\u003eWonderWidgets\u003c/em\u003e are great\",\n           \"Who \u003cem\u003ebuys\u003c/em\u003e WonderWidgets\"\n         ],\n         \"title\": \"Overview\",\n         \"type\": \"all\"\n       }\n     ],\n-    \"title\": \"Test Slide Show\"\n-  },\n-  \"extra\": true\n+    \"title\": \"Sample Slide Show\"\n+  }\n }\n-\n```\n\n#### Parse\n\nValidate and parse request files. It returns a JSON object with info about the request file: environment names, variables, prompts, secrets, the (untemplated) request itself.\n\n```\nUsage: reqlang parse \u003cpath\u003e\n\nArguments:\n  \u003cpath\u003e  Path to request file\n\nOptions:\n  -h, --help  Print help\n```\n\n##### Examples\n\n```shell\nreqlang parse ./examples/valid/status_code.reqlang\n```\n\n```json\n{\n  \"vars\": [\"test_value\"],\n  \"envs\": [\"prod\", \"test\", \"local\"],\n  \"prompts\": [\"prompt_value\"],\n  \"secrets\": [\"super_secret_value\"],\n  \"request\": {\n    \"verb\": \"POST\",\n    \"target\": \"https://httpbin.org/post\",\n    \"http_version\": \"1.1\",\n    \"headers\": [],\n    \"body\": \"{\\n  \\\"env\\\": \\\"{{@env}}\\\",\\n  \\\"value\\\": \\\"{{:test_value}}\\\",\\n  \\\"prompted_value\\\": \\\"{{?prompt_value}}\\\",\\n  \\\"secret_value\\\": \\\"{{!super_secret_value}}\\\"\\n}\\n\\n\"\n  }\n}\n```\n\n##### Filtering\n\nUse tools like `jq` to extract specific information from the parsed request.\n\n###### Environment Names\n\nLet a list of environment names defined in the request file.\n\n```shell\nreqlang parse ./examples/valid/post.reqlang | jq '.envs'\n```\n\n```json\n[\"local\", \"test\", \"prod\"]\n```\n\n###### Variables\n\nLet a list of variables provided by the request file.\n\n```shell\nreqlang parse ./examples/valid/post.reqlang | jq '.vars'\n```\n\n```json\n[\"test_value\"]\n```\n\n###### Prompts\n\nLet a list of prompts required by the request file.\n\n```shell\nreqlang parse ./examples/valid/post.reqlang | jq '.prompts'\n```\n\n```json\n[\"prompt_value\"]\n```\n\n###### Secrets\n\nLet a list of secrets required by the request file.\n\n```shell\nreqlang parse ./examples/valid/post.reqlang | jq '.secrets'\n```\n\n```json\n[\"super_secret_value\"]\n```\n\n###### Config Location In Request File\n\nGet the span of the config, if defined, in the request file. Otherwise it's `null`.\n\n```shell\nreqlang parse ./examples/valid/post.reqlang | jq '.full.config[1]'\n```\n\n```json\n{\n  \"start\": 0,\n  \"end\": 204\n}\n```\n\n###### Request Location In Request File\n\nGet the span of the request in the request file.\n\n```shell\nreqlang parse ./examples/valid/post.reqlang | jq '.full.request[1]'\n```\n\n```json\n{\n  \"start\": 208,\n  \"end\": 388\n}\n```\n\n###### Response Location In Request File\n\nGet the span of the response, if defined, in the request file. Otherwise it's `null`.\n\n```shell\nreqlang parse ./examples/valid/post.reqlang | jq '.full.response[1]'\n```\n\n```json\nnull\n```\n\n###### Ref Locations In Request File\n\nGet the span of all the template references (variables, prompts, secrets, providers), if defined, in the request file.\n\n```shell\nreqlang parse ./examples/valid/post.reqlang | jq '.full.refs'\n```\n\n```json\n[\n  [\n    {\n      \"Provider\": \"env\"\n    },\n    {\n      \"start\": 208,\n      \"end\": 388\n    }\n  ],\n  [\n    {\n      \"Variable\": \"test_value\"\n    },\n    {\n      \"start\": 208,\n      \"end\": 388\n    }\n  ],\n  [\n    {\n      \"Prompt\": \"prompt_value\"\n    },\n    {\n      \"start\": 208,\n      \"end\": 388\n    }\n  ],\n  [\n    {\n      \"Secret\": \"super_secret_value\"\n    },\n    {\n      \"start\": 208,\n      \"end\": 388\n    }\n  ]\n]\n```\n\n##### Validation Errors\n\nIf the request file is invalid, a list of errors will be returned instead.\n\n```shell\nreqlang parse examples/invalid/empty.reqlang\n```\n\n```json\n[\n  {\n    \"range\": {\n      \"start\": {\n        \"line\": 0,\n        \"character\": 0\n      },\n      \"end\": {\n        \"line\": 0,\n        \"character\": 0\n      }\n    },\n    \"severity\": 1,\n    \"message\": \"ParseError: Request file is an empty file\"\n  }\n]\n```\n\n#### AST\n\nProduce an AST for a request file.\n\n```\nUsage: reqlang ast \u003cpath\u003e\n\nArguments:\n  \u003cpath\u003e  Path to request file\n\nOptions:\n  -h, --help  Print help\n```\n\n##### Examples\n\n```shell\nreqlang ast examples/valid/as_markdown.reqlang\n```\n\n```json\n[\n  [\n    {\n      \"Comment\": \"# Request Files Are Markdown Files\\n\\nAnything outside of the config, request, or response code blocks is treated as markdown. This lets you document your request files in a way that is easy to read and understand.\\n\\n## Config\\n\\nPrompt the user for the `status_code` to return.\\n\\n\"\n    },\n    {\n      \"start\": 0,\n      \"end\": 275\n    }\n  ],\n  [\n    {\n      \"ConfigBlock\": [\n        \"[prompts]\\n# Status code the response will return\\nstatus_code = \\\"\\\"\",\n        {\n          \"start\": 286,\n          \"end\": 352\n        }\n      ]\n    },\n    {\n      \"start\": 275,\n      \"end\": 355\n    }\n  ],\n  [\n    {\n      \"Comment\": \"\\n\\n## Request\\n\\nThis will respond with the prompted `status_code`.\\n\\n\"\n    },\n    {\n      \"start\": 355,\n      \"end\": 421\n    }\n  ],\n  [\n    {\n      \"RequestBlock\": [\n        \"GET https://httpbin.org/status/{{?status_code}} HTTP/1.1\",\n        {\n          \"start\": 433,\n          \"end\": 490\n        }\n      ]\n    },\n    {\n      \"start\": 421,\n      \"end\": 493\n    }\n  ]\n]\n```\n\n##### Filtering\n\n###### Comments\n\n```shell\nreqlang ast examples/valid/as_markdown.reqlang | jq 'map(select(.[0] | has(\"Comment\")))'\n```\n\n```json\n[\n  [\n    {\n      \"Comment\": \"# Request Files Are Markdown Files\\n\\nAnything outside of the config, request, or response code blocks is treated as markdown. This lets you document your request files in a way that is easy to read and understand.\\n\\n## Config\\n\\nPrompt the user for the `status_code` to return.\\n\\n\"\n    },\n    {\n      \"start\": 0,\n      \"end\": 275\n    }\n  ],\n  [\n    {\n      \"Comment\": \"\\n\\n## Request\\n\\nThis will respond with the prompted `status_code`.\\n\\n\"\n    },\n    {\n      \"start\": 355,\n      \"end\": 421\n    }\n  ]\n]\n```\n\n#### Export\n\nParse and template the request file then export it in different formats.\n\n```\nUsage: reqlang export [OPTIONS] \u003cpath\u003e\n\nArguments:\n  \u003cpath\u003e  Path to request file\n\nOptions:\n  -e, --env \u003cenv\u003e         Resolve with an environment\n  -P, --prompt \u003cprompts\u003e  Pass prompt values to resolve with\n  -S, --secret \u003csecrets\u003e  Pass secret values to resolve with\n  -f, --format \u003cformat\u003e   Format to export [default: json] [possible values: http, curl, json, body]\n  -h, --help              Print help\n```\n\n##### Examples\n\n###### JSON\n\n```shell\nreqlang export examples/valid/status_code.reqlang --prompt status_code=200 --format json\n\n# This is the same thing\nreqlang export examples/valid/status_code.reqlang --prompt status_code=200\n```\n\n```json\n{\n  \"verb\": \"GET\",\n  \"target\": \"https://httpbin.org/status/200\",\n  \"http_version\": \"1.1\",\n  \"headers\": [],\n  \"body\": \"\"\n}\n```\n\n###### HTTP Request Message\n\n```shell\nreqlang export examples/valid/status_code.reqlang --prompt status_code=201 --format http\n```\n\n```\nGET https://httpbin.org/status/201 HTTP/1.1\n```\n\n##### Curl command\n\n```shell\nreqlang export examples/valid/status_code.reqlang --prompt status_code=400 --format curl\n```\n\n```shell\ncurl https://httpbin.org/status/400 --http1.1 -v\n```\n\n##### Body Text\n\n```shell\nreqlang export examples/valid/base64decode.reqlang --format body\n```\n\n```\nHTTPBIN is awesome\n\n```\n\n##### Validation Errors\n\nIf the request file is invalid or there were errors templating, a list of errors will be returned instead.\n\n```shell\nreqlang export examples/invalid/empty.reqlang\n```\n\n```json\n[\n  {\n    \"range\": {\n      \"start\": {\n        \"line\": 0,\n        \"character\": 0\n      },\n      \"end\": {\n        \"line\": 0,\n        \"character\": 0\n      }\n    },\n    \"severity\": 1,\n    \"message\": \"ParseError: Request file is an empty file\"\n  }\n]\n```\n\n### CLI in Docker\n\nThe `reqlang` CLI can be run from a docker image.\n\n#### Building\n\n```shell\ndocker build -t reqlang:0.1.0 .\n```\n\n#### Running\n\nA directory of request files can be mounted inside the container's `/usr/local/src` directory to make them accessible.\n\n```shell\ndocker run --rm --read-only \\\n    -v \"/$PWD/examples\":/usr/local/src/examples:ro \\\n    reqlang:0.1.0 \\\n    export \\\n    ./examples/valid/delay.reqlang \\\n    -f curl \\\n    -P seconds=5 | bash\n```\n\n```\n# HTTP/1.1 201 CREATED\n# Date: Sat, 14 Dec 2024 19:20:26 GMT\n# Content-Type: text/html; charset=utf-8\n# Content-Length: 0\n# Connection: keep-alive\n# Server: gunicorn/19.9.0\n# Access-Control-Allow-Origin: *\n# Access-Control-Allow-Credentials: true\n```\n\n### VS Code\n\nThe [VS Code extension](./vsc/#readme) acts as an in-editor REST client.\n\n![VS Code Extension Screenshot](./vsc/screenshot.png)\n\n### Desktop Client\n\nThe [desktop client](./reqlang-client) is a very simple GUI written in [egui](https://github.com/emilk/egui). It's mostly used for testing.\n\n![GUI Client Screenshot](./reqlang-client/screenshot.png)\n\n### Web Client\n\nThe [web client](./reqlang-web-client/) is a React app powered by a Rust API.\n\n```shell\nreqlang-web-client\n\n# Server is running! http://localhost:3000\n```\n\n#### Port\n\nThe port defaults to a random open port but it can be set using the `REQLANG_WEB_CLIENT_PORT` environment variable.\n\n## Contributing\n\nPlease see [CONTRIBUTING.md](./CONTRIBUTING.md) for details on how to contribute.\n\n## Development Log\n\nYou can follow the development in this [Bluesky thread](https://bsky.app/profile/testingrequired.com/post/3lcftvxbp622b).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftestingrequired%2Freqlang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftestingrequired%2Freqlang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftestingrequired%2Freqlang/lists"}