diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 11da4de7..30e26868 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -67,11 +67,16 @@ jobs: - name: Build console (release with features) run: cargo build --release --target ${{ matrix.target }} --bin console --features explain --verbose + + - name: Build stacker-cli (release) + run: cargo build --release --target ${{ matrix.target }} --bin stacker-cli --verbose + - name: Prepare binaries run: | mkdir -p artifacts cp target/${{ matrix.target }}/release/server artifacts/server cp target/${{ matrix.target }}/release/console artifacts/console + cp target/${{ matrix.target }}/release/stacker-cli artifacts/stacker-cli tar -czf ${{ matrix.artifact_name }}.tar.gz -C artifacts . - name: Upload binaries uses: actions/upload-artifact@v4 diff --git a/.sqlx/query-4476636012dbc3320496e7d0f1bc7ab98a9e430127baa928fdf87ff8ef85d3c7.json b/.sqlx/query-4476636012dbc3320496e7d0f1bc7ab98a9e430127baa928fdf87ff8ef85d3c7.json new file mode 100644 index 00000000..3efaec53 --- /dev/null +++ b/.sqlx/query-4476636012dbc3320496e7d0f1bc7ab98a9e430127baa928fdf87ff8ef85d3c7.json @@ -0,0 +1,53 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO chat_conversations (user_id, project_id, messages)\n VALUES ($1, NULL, $2)\n ON CONFLICT (user_id) WHERE project_id IS NULL\n DO UPDATE SET messages = EXCLUDED.messages, updated_at = NOW()\n RETURNING id, user_id, project_id, messages, created_at, updated_at", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "project_id", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "messages", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "updated_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Jsonb" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false + ] + }, + "hash": "4476636012dbc3320496e7d0f1bc7ab98a9e430127baa928fdf87ff8ef85d3c7" +} diff --git a/.sqlx/query-5aaa47a88d81ba70ae7da455ac5c98fc77ae7c6422dc8eea31fd89863c83ffd3.json b/.sqlx/query-5aaa47a88d81ba70ae7da455ac5c98fc77ae7c6422dc8eea31fd89863c83ffd3.json new file mode 100644 index 00000000..ceb32634 --- /dev/null +++ b/.sqlx/query-5aaa47a88d81ba70ae7da455ac5c98fc77ae7c6422dc8eea31fd89863c83ffd3.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM chat_conversations WHERE user_id = $1 AND project_id IS NULL", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "5aaa47a88d81ba70ae7da455ac5c98fc77ae7c6422dc8eea31fd89863c83ffd3" +} diff --git a/.sqlx/query-7e50789875fbdf752993b3fabe6031811acea54e4d47bbad2950494e626b807c.json b/.sqlx/query-7e50789875fbdf752993b3fabe6031811acea54e4d47bbad2950494e626b807c.json new file mode 100644 index 00000000..f0fad73d --- /dev/null +++ b/.sqlx/query-7e50789875fbdf752993b3fabe6031811acea54e4d47bbad2950494e626b807c.json @@ -0,0 +1,53 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, user_id, project_id, messages, created_at, updated_at\n FROM chat_conversations\n WHERE user_id = $1 AND project_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "project_id", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "messages", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "updated_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + "Int4" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false + ] + }, + "hash": "7e50789875fbdf752993b3fabe6031811acea54e4d47bbad2950494e626b807c" +} diff --git a/.sqlx/query-a0aeb1857bed3967f43b4d88d939af74e5fcec7b951cf1f28eef3d4ba9459717.json b/.sqlx/query-a0aeb1857bed3967f43b4d88d939af74e5fcec7b951cf1f28eef3d4ba9459717.json new file mode 100644 index 00000000..8f185554 --- /dev/null +++ b/.sqlx/query-a0aeb1857bed3967f43b4d88d939af74e5fcec7b951cf1f28eef3d4ba9459717.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM chat_conversations WHERE user_id = $1 AND project_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "a0aeb1857bed3967f43b4d88d939af74e5fcec7b951cf1f28eef3d4ba9459717" +} diff --git a/.sqlx/query-b33f8552276056b53e184e7e51c6cbee7b72e99977fd028a28d3e2f45be14225.json b/.sqlx/query-b33f8552276056b53e184e7e51c6cbee7b72e99977fd028a28d3e2f45be14225.json new file mode 100644 index 00000000..b07273bf --- /dev/null +++ b/.sqlx/query-b33f8552276056b53e184e7e51c6cbee7b72e99977fd028a28d3e2f45be14225.json @@ -0,0 +1,52 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, user_id, project_id, messages, created_at, updated_at\n FROM chat_conversations\n WHERE user_id = $1 AND project_id IS NULL", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "project_id", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "messages", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "updated_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false + ] + }, + "hash": "b33f8552276056b53e184e7e51c6cbee7b72e99977fd028a28d3e2f45be14225" +} diff --git a/.sqlx/query-b8e47b0c9f1eeb571001c340c3f6593303ef635f9a93707996f86ae4a5db1e99.json b/.sqlx/query-b8e47b0c9f1eeb571001c340c3f6593303ef635f9a93707996f86ae4a5db1e99.json new file mode 100644 index 00000000..339ec891 --- /dev/null +++ b/.sqlx/query-b8e47b0c9f1eeb571001c340c3f6593303ef635f9a93707996f86ae4a5db1e99.json @@ -0,0 +1,54 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO chat_conversations (user_id, project_id, messages)\n VALUES ($1, $2, $3)\n ON CONFLICT (user_id, project_id) WHERE project_id IS NOT NULL\n DO UPDATE SET messages = EXCLUDED.messages, updated_at = NOW()\n RETURNING id, user_id, project_id, messages, created_at, updated_at", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "project_id", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "messages", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "updated_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Int4", + "Jsonb" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false + ] + }, + "hash": "b8e47b0c9f1eeb571001c340c3f6593303ef635f9a93707996f86ae4a5db1e99" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index d316282e..8f9cf859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,97 @@ All notable changes to this project will be documented in this file. +## [0.2.3] — 2026-02-23 + +### Changed — `stacker init` now generates `.stacker/` directory + +- `stacker init` now creates `.stacker/Dockerfile` and `.stacker/docker-compose.yml` alongside `stacker.yml`, so the project is ready to deploy immediately without running `deploy --dry-run` first +- Dockerfile generation is skipped when `app.image` or `app.dockerfile` is set in the config +- Compose generation is skipped when `deploy.compose_file` is set + +### Changed — `stacker deploy` reuses existing `.stacker/` artifacts + +- `deploy` no longer errors when `.stacker/Dockerfile` or `.stacker/docker-compose.yml` already exist (e.g. from `stacker init`) +- Existing artifacts are reused; pass `--force-rebuild` to regenerate them + +### Added — `--ai-provider`, `--ai-model`, `--ai-api-key` flags on `stacker-cli init` + +- The `stacker-cli` binary (`console/main.rs`) now supports all AI-related flags that the standalone `stacker` binary already had: + - `--ai-provider ` — openai, anthropic, ollama, custom + - `--ai-model ` — e.g. `qwen2.5-coder`, `deepseek-r1`, `gpt-4o` + - `--ai-api-key ` — API key for cloud AI providers + +### Added — AI troubleshooting suggestions on deploy failures + +- On `stacker deploy` failures (`DeployFailed`), CLI now attempts AI-assisted troubleshooting automatically +- It sends the deploy error plus generated `.stacker/Dockerfile` and `.stacker/docker-compose.yml` snippets to the configured AI provider +- If AI is unavailable or not configured, CLI prints deterministic fallback hints for common issues (for example `npm ci` failures, obsolete compose `version`, missing files, permissions, and network timeouts) + +### Fixed + +- `stacker-cli init --with-ai --ai-model qwen2.5-coder` no longer fails with an unrecognised flag error +- `stacker deploy` after `stacker init` no longer fails with `DockerfileExists` error + +## 2026-02-23 + +### Added - Configurable AI Request Timeout + +- New `timeout` field in `ai` config section of `stacker.yml` (default: 300 seconds) +- `STACKER_AI_TIMEOUT` environment variable overrides the config value +- Timeout applies to all AI providers (OpenAI, Anthropic, Ollama, Custom) +- Useful for large models on slower hardware: `STACKER_AI_TIMEOUT=900 stacker init --with-ai` +- Example stacker.yml: + ```yaml + ai: + enabled: true + provider: ollama + model: deepseek-r1 + timeout: 600 # 10 minutes + ``` +- 9 new tests for timeout resolution + +### Added - Stacker CLI: AI-Powered Project Initialization + +#### AI Scanner Module (`src/cli/ai_scanner.rs`) +- New `scan_project()` function performs deep project scanning, reading key config files (`package.json`, `requirements.txt`, `Cargo.toml`, `Dockerfile`, `docker-compose.yml`, `.env`, etc.) to build rich context for AI generation +- `build_generation_prompt()` constructs detailed prompts including detected app type, file contents, existing infrastructure, and env var keys (values redacted for security) +- `generate_config_with_ai()` sends project context to the configured AI provider and returns a tailored `stacker.yml` +- `strip_code_fences()` strips markdown code fences from AI responses +- System prompt encodes the full `stacker.yml` schema so the AI generates valid, deployable configs +- 16 unit tests + +#### AI-Powered `stacker init --with-ai` (`src/console/commands/cli/init.rs`) +- `stacker init --with-ai` now scans the project and calls the AI to generate a tailored `stacker.yml` with appropriate services, proxy, monitoring, and hooks +- New CLI flags on `stacker init`: + - `--ai-provider ` — AI provider: `openai`, `anthropic`, `ollama`, `custom` (default: `ollama`) + - `--ai-model ` — Model name (e.g. `gpt-4o`, `claude-sonnet-4-20250514`, `llama3`) + - `--ai-api-key ` — API key (or use environment variables) +- `resolve_ai_config()` resolves AI configuration with priority: CLI flag → environment variable → defaults +- Environment variable support: `STACKER_AI_PROVIDER`, `STACKER_AI_MODEL`, `STACKER_AI_API_KEY`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY` +- Graceful fallback: if AI generation fails (provider unreachable, invalid YAML), automatically falls back to template-based generation +- AI-generated configs include a review header noting the provider and model used +- If AI output fails validation, raw draft is saved to `stacker.yml.ai-draft` for manual review +- 8 new unit tests (18 total in init.rs), 3 new integration tests (11 total in cli_init.rs) + +#### Usage Examples +```bash +# AI-powered init with local Ollama +stacker init --with-ai + +# AI-powered init with OpenAI +stacker init --with-ai --ai-provider openai --ai-api-key sk-... + +# AI-powered init with Anthropic (key from env) +export ANTHROPIC_API_KEY=sk-ant-... +stacker init --with-ai --ai-provider anthropic + +# Falls back to template if AI fails +stacker init --with-ai # no Ollama running → template fallback +``` + +### Test Results +- **467 tests** (426 unit + 41 integration), 0 failures + ## 2026-02-18 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 4acfbd21..6d5cee69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,8 +91,8 @@ dependencies = [ "flate2", "foldhash", "futures-core", - "h2", - "http", + "h2 0.3.27", + "http 0.2.12", "httparse", "httpdate", "itoa", @@ -128,7 +128,7 @@ checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" dependencies = [ "bytestring", "cfg-if", - "http", + "http 0.2.12", "regex", "regex-lite", "serde", @@ -533,6 +533,21 @@ dependencies = [ "serde_json", ] +[[package]] +name = "assert_cmd" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c5bcfa8749ac45dd12cb11055aeeb6b27a3895560d60d71e3c23bf979e60514" +dependencies = [ + "anstyle", + "bstr", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -858,6 +873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", + "regex-automata", "serde", ] @@ -1099,6 +1115,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "combine" version = "4.6.7" @@ -1615,6 +1640,12 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.7" @@ -1907,6 +1938,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + [[package]] name = "flume" version = "0.11.1" @@ -2207,7 +2247,26 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.0", "indexmap", "slab", "tokio", @@ -2336,6 +2395,16 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -2343,7 +2412,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.4.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -2357,7 +2449,7 @@ dependencies = [ "async-channel 1.9.0", "base64 0.13.1", "futures-lite 1.13.0", - "http", + "http 0.2.12", "infer", "pin-project-lite", "rand 0.7.3", @@ -2399,9 +2491,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -2413,6 +2505,28 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2 0.4.13", + "http 1.4.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -2420,12 +2534,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.32", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.8.1", + "pin-project-lite", + "tokio", +] + [[package]] name = "iana-time-zone" version = "0.1.64" @@ -2932,6 +3060,31 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "mockito" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90820618712cab19cfc46b274c6c22546a82affcb3c3bdf0f29e3db8e1bb92c0" +dependencies = [ + "assert-json-diff", + "bytes", + "colored", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-util", + "log", + "pin-project-lite", + "rand 0.9.2", + "regex", + "serde_json", + "serde_urlencoded", + "similar", + "tokio", +] + [[package]] name = "mutually_exclusive_features" version = "0.1.0" @@ -2974,6 +3127,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -3598,6 +3757,36 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "predicates" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" + +[[package]] +name = "predicates-tree" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -3887,10 +4076,10 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", "hyper-tls", "ipnet", "js-sys", @@ -4550,6 +4739,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + [[package]] name = "siphasher" version = "1.0.2" @@ -4973,7 +5168,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.2.2" +version = "0.2.3" dependencies = [ "actix", "actix-casbin-auth", @@ -4983,6 +5178,7 @@ dependencies = [ "actix-web-actors", "aes-gcm", "anyhow", + "assert_cmd", "async-trait", "base64 0.22.1", "brotli 3.5.0", @@ -5001,6 +5197,8 @@ dependencies = [ "hmac", "indexmap", "lapin", + "mockito", + "predicates", "rand 0.8.5", "redis", "regex", @@ -5017,6 +5215,7 @@ dependencies = [ "sqlx", "sqlx-adapter", "ssh-key", + "tempfile", "tera", "thiserror 1.0.69", "tokio", @@ -5194,6 +5393,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "thin-vec" version = "0.2.14" @@ -5660,6 +5865,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "waker-fn" version = "1.2.0" @@ -6175,7 +6389,7 @@ dependencies = [ "futures", "futures-timer", "http-types", - "hyper", + "hyper 0.14.32", "log", "once_cell", "regex", diff --git a/Cargo.toml b/Cargo.toml index 805044a3..3ae2416c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stacker" -version = "0.2.2" +version = "0.2.3" edition = "2021" default-run= "server" @@ -16,6 +16,10 @@ path = "src/console/main.rs" name = "console" required-features = ["explain"] +[[bin]] +path = "src/bin/stacker.rs" +name = "stacker-cli" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -93,3 +97,7 @@ explain = ["actix-casbin-auth/explain", "actix-casbin-auth/logging"] [dev-dependencies] glob = "0.3" wiremock = "0.5.22" +assert_cmd = "2.0" +predicates = "3.0" +tempfile = "3" +mockito = "1" diff --git a/docs/STACKER_YML_REFERENCE.md b/docs/STACKER_YML_REFERENCE.md new file mode 100644 index 00000000..56e2c954 --- /dev/null +++ b/docs/STACKER_YML_REFERENCE.md @@ -0,0 +1,1152 @@ +# stacker.yml Configuration Reference + +> **Stacker CLI v0.2.3** — The single-file deployment configuration for containerised applications. + +`stacker.yml` is the only file you need to add to your project. Stacker reads it to auto-generate Dockerfiles, docker-compose definitions, and deploy your application locally or to cloud infrastructure. + +--- + +## Table of Contents + +- [Quick Start](#quick-start) +- [Minimal Example](#minimal-example) +- [Full Example](#full-example) +- [Top-Level Fields](#top-level-fields) + - [name](#name) · [version](#version) · [organization](#organization) +- [app — Application Source](#app) + - [type](#apptype) · [path](#apppath) · [dockerfile](#appdockerfile) · [image](#appimage) · [build](#appbuild) +- [services — Sidecar Containers](#services) +- [proxy — Reverse Proxy](#proxy) + - [type](#proxytype) · [auto_detect](#proxyauto_detect) · [domains](#proxydomains) · [config](#proxyconfig) +- [deploy — Deployment Target](#deploy) + - [target](#deploytarget) · [compose_file](#deploycompose_file) · [cloud](#deploycloud) · [server](#deployserver) +- [ai — AI Assistant](#ai) +- [monitoring — Health & Metrics](#monitoring) + - [status_panel](#monitoringstatus_panel) · [healthcheck](#monitoringhealthcheck) · [metrics](#monitoringmetrics) +- [hooks — Lifecycle Scripts](#hooks) +- [env / env_file — Environment Variables](#env--env_file) +- [Environment Variable Interpolation](#environment-variable-interpolation) +- [Auto-Detection](#auto-detection) +- [Generated Dockerfiles](#generated-dockerfiles) +- [Validation Rules](#validation-rules) +- [CLI Commands Reference](#cli-commands-reference) +- [Recipes](#recipes) +- [FAQ](#faq) + +--- + +## Quick Start + +```bash +# 1. Install stacker +curl -fsSL https://stacker.try.direct/install.sh | bash + +# 2. Initialize in your project directory +cd my-project +stacker init + +# 3. Review the generated config +cat stacker.yml + +# 4. Deploy locally +stacker deploy --target local + +# 5. Check status +stacker status +``` + +--- + +## Minimal Example + +The smallest valid `stacker.yml`: + +```yaml +name: my-app +app: + type: static + path: ./public +deploy: + target: local +``` + +This tells Stacker to: +1. Generate an nginx-based Dockerfile serving static files from `./public` +2. Create a docker-compose.yml with the app service +3. Deploy locally via `docker compose up` + +--- + +## Full Example + +A production-ready configuration using all available sections: + +```yaml +name: my-saas-app +version: "2.0" +organization: acme-corp + +app: + type: node + path: ./src + build: + context: . + args: + NODE_ENV: production + +services: + - name: postgres + image: postgres:16 + ports: + - "5432:5432" + environment: + POSTGRES_DB: myapp + POSTGRES_USER: app + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - pgdata:/var/lib/postgresql/data + + - name: redis + image: redis:7-alpine + ports: + - "6379:6379" + + - name: worker + image: myapp-worker:latest + depends_on: + - postgres + - redis + environment: + REDIS_URL: redis://redis:6379 + +proxy: + type: nginx + auto_detect: true + domains: + - domain: app.example.com + ssl: auto + upstream: app:3000 + - domain: api.example.com + ssl: auto + upstream: app:3000 + +deploy: + target: cloud + cloud: + provider: hetzner + region: fsn1 + size: cx21 + ssh_key: ~/.ssh/id_ed25519 + +ai: + enabled: true + provider: ollama + model: llama3 + endpoint: http://localhost:11434 + timeout: 600 + tasks: + - dockerfile + - troubleshoot + +monitoring: + status_panel: true + healthcheck: + endpoint: /health + interval: 30s + metrics: + enabled: true + telegraf: true + +hooks: + pre_build: ./scripts/pre-build.sh + post_deploy: ./scripts/post-deploy.sh + on_failure: ./scripts/notify-failure.sh + +env_file: .env + +env: + APP_PORT: "3000" + LOG_LEVEL: info + NODE_ENV: production +``` + +--- + +## Top-Level Fields + +### `name` + +**Required** · `string` · Max 128 characters + +The project name. Used as the docker-compose project name, container name prefix, and displayed in status output. + +```yaml +name: my-awesome-app +``` + +### `version` + +*Optional* · `string` · Default: none + +A version label for the configuration. Informational only — does not affect behaviour. + +```yaml +version: "1.0" +``` + +### `organization` + +*Optional* · `string` · Default: none + +Organisation slug. Used for scoping cloud deployments and linking to your TryDirect account. + +```yaml +organization: acme-corp +``` + +--- + +## `app` + +**Application source configuration.** Tells Stacker what kind of app you're building and where the source code lives. + +### `app.type` + +*Optional* · `enum` · Default: `static` + +The application framework/runtime. Determines which Dockerfile template is generated. + +| Value | Description | Default Base Image | Default Port | +|-------|-------------|-------------------|--------------| +| `static` | Static HTML/CSS/JS site | `nginx:alpine` | 80 | +| `node` | Node.js application | `node:20-alpine` | 3000 | +| `python` | Python application | `python:3.12-slim` | 8000 | +| `rust` | Rust application | `rust:1.77-alpine` | 8080 | +| `go` | Go application | `golang:1.22-alpine` | 8080 | +| `php` | PHP application | `php:8.3-fpm-alpine` | 9000 | +| `custom` | User-provided Dockerfile | — | — | + +```yaml +app: + type: node +``` + +> **Tip:** If you omit `type`, Stacker auto-detects it from your project files. +> See [Auto-Detection](#auto-detection). + +### `app.path` + +*Optional* · `string` (path) · Default: `.` + +Path to the application source directory, relative to the `stacker.yml` location. + +```yaml +app: + path: ./src +``` + +### `app.dockerfile` + +*Optional* · `string` (path) · Default: none + +Path to a custom Dockerfile. When set, Stacker uses your Dockerfile instead of generating one. Requires `type: custom` or will override the generated template. + +```yaml +app: + type: custom + dockerfile: ./docker/Dockerfile.prod +``` + +### `app.image` + +*Optional* · `string` · Default: none + +Use a pre-built Docker image instead of building from source. Mutually exclusive with `dockerfile` and auto-generation. + +```yaml +app: + type: custom + image: ghcr.io/myorg/myapp:latest +``` + +### `app.build` + +*Optional* · `object` · Default: none + +Docker build configuration. Controls the build context and build arguments passed to `docker build`. + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `context` | `string` | `.` | Build context directory | +| `args` | `map` | `{}` | Build arguments (`--build-arg`) | + +```yaml +app: + type: node + build: + context: . + args: + NODE_ENV: production + API_URL: https://api.example.com +``` + +--- + +## `services` + +*Optional* · `array` · Default: `[]` + +Additional containers deployed alongside your main application — databases, caches, message queues, workers, etc. Each entry maps directly to a service in the generated `docker-compose.yml`. + +### Service Definition Fields + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `name` | `string` | **yes** | — | Service name (used as container/hostname) | +| `image` | `string` | **yes** | — | Docker image reference | +| `ports` | `string[]` | no | `[]` | Port mappings (`"host:container"`) | +| `environment` | `map` | no | `{}` | Environment variables | +| `volumes` | `string[]` | no | `[]` | Volume mounts (`"name:/path"` or `"./host:/container"`) | +| `depends_on` | `string[]` | no | `[]` | Services this depends on (started first) | + +```yaml +services: + - name: postgres + image: postgres:16 + ports: + - "5432:5432" + environment: + POSTGRES_DB: myapp + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - pgdata:/var/lib/postgresql/data + + - name: redis + image: redis:7-alpine + ports: + - "6379:6379" + + - name: minio + image: minio/minio:latest + ports: + - "9000:9000" + - "9001:9001" + environment: + MINIO_ROOT_USER: admin + MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD} + volumes: + - minio-data:/data +``` + +> **Note:** Stacker detects port conflicts across services during validation. +> If two services bind the same host port, you'll get a warning (`W001`). + +--- + +## `proxy` + +*Optional* · `object` · Default: `type: none, auto_detect: true` + +Reverse proxy configuration. Stacker can auto-detect a running proxy or generate configuration for one. + +### `proxy.type` + +*Optional* · `enum` · Default: `none` + +| Value | Description | +|-------|-------------| +| `nginx` | Standard Nginx reverse proxy | +| `nginx-proxy-manager` | Nginx Proxy Manager (NPM) with web UI | +| `traefik` | Traefik reverse proxy with auto-discovery | +| `none` | No proxy configured | + +```yaml +proxy: + type: nginx +``` + +### `proxy.auto_detect` + +*Optional* · `bool` · Default: `true` + +When enabled, Stacker scans running Docker containers for an existing reverse proxy before deploying. If found, it connects your app to the existing proxy instead of creating a new one. + +Detection checks for these container images (in priority order): +1. `jc21/nginx-proxy-manager` / `nginx-proxy-manager` → `nginx-proxy-manager` +2. `traefik` → `traefik` +3. `nginx` → `nginx` + +```yaml +proxy: + auto_detect: false # Don't look for existing proxies +``` + +### `proxy.domains` + +*Optional* · `array` · Default: `[]` + +Domain routing rules. Each entry generates a proxy virtual host configuration. + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `domain` | `string` | **yes** | — | Domain name (e.g. `app.example.com`) | +| `upstream` | `string` | **yes** | — | Backend address (e.g. `app:3000`, `http://web:8080`) | +| `ssl` | `enum` | no | `off` | SSL certificate mode | + +**SSL modes:** + +| Value | Description | +|-------|-------------| +| `auto` | Automatic certificate provisioning (Let's Encrypt) | +| `manual` | Use manually provided certificates | +| `off` | No SSL (HTTP only) | + +```yaml +proxy: + type: nginx + domains: + - domain: app.example.com + ssl: auto + upstream: app:3000 + + - domain: api.example.com + ssl: auto + upstream: app:3000 + + - domain: staging.example.com + ssl: off + upstream: app:3000 +``` + +### `proxy.config` + +*Optional* · `string` (path) · Default: none + +Path to a custom proxy configuration file. When set, Stacker uses your config instead of generating one. + +```yaml +proxy: + type: nginx + config: ./nginx/custom.conf +``` + +--- + +## `deploy` + +**Deployment target configuration.** Controls where and how your stack is deployed. + +### `deploy.target` + +*Optional* · `enum` · Default: `local` + +| Value | Description | +|-------|-------------| +| `local` | Deploy on the local machine via `docker compose` | +| `cloud` | Provision cloud infrastructure and deploy (requires `deploy.cloud`) | +| `server` | Deploy to an existing remote server via SSH (requires `deploy.server`) | + +```yaml +deploy: + target: local +``` + +### `deploy.compose_file` + +*Optional* · `string` (path) · Default: none + +Use a custom docker-compose file instead of the auto-generated one. Stacker will skip generation and use this file directly. + +```yaml +deploy: + target: local + compose_file: ./docker-compose.prod.yml +``` + +### `deploy.cloud` + +*Required when `target: cloud`* · `object` + +Cloud infrastructure provisioning settings. Stacker uses Terraform/Ansible under the hood to create servers and deploy your stack. + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `provider` | `enum` | **yes** | — | Cloud provider | +| `region` | `string` | no | Provider default | Data center region | +| `size` | `string` | no | Provider default | Server size/type | +| `ssh_key` | `string` (path) | no | none | Path to SSH private key | + +**Supported cloud providers:** + +| Value | Provider | Example Regions | Example Sizes | +|-------|----------|----------------|---------------| +| `hetzner` | Hetzner Cloud | `fsn1`, `nbg1`, `hel1` | `cx21`, `cx31`, `cx41` | +| `digitalocean` | DigitalOcean | `nyc1`, `sfo3`, `ams3` | `s-1vcpu-1gb`, `s-2vcpu-4gb` | +| `aws` | Amazon Web Services | `us-east-1`, `eu-west-1` | `t3.micro`, `t3.small` | +| `linode` | Linode (Akamai) | `us-east`, `eu-west` | `g6-nanode-1`, `g6-standard-2` | +| `vultr` | Vultr | `ewr`, `lhr`, `fra` | `vc2-1c-1gb`, `vc2-2c-4gb` | + +```yaml +deploy: + target: cloud + cloud: + provider: hetzner + region: fsn1 + size: cx21 + ssh_key: ~/.ssh/id_ed25519 +``` + +> **Important:** Cloud deployment requires authentication. +> Run `stacker login` first to store your TryDirect credentials. + +### `deploy.server` + +*Required when `target: server`* · `object` + +Remote server settings for deploying to an existing machine via SSH. + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `host` | `string` | **yes** | — | Server hostname or IP address | +| `user` | `string` | no | `root` | SSH username | +| `ssh_key` | `string` (path) | no | none | Path to SSH private key | +| `port` | `integer` | no | `22` | SSH port | + +```yaml +deploy: + target: server + server: + host: 203.0.113.42 + user: deploy + ssh_key: ~/.ssh/deploy_key + port: 22 +``` + +--- + +## `ai` + +*Optional* · `object` · Default: `enabled: false` + +AI/LLM assistant configuration. When enabled, `stacker ai ask` uses the configured provider to answer questions about your Dockerfile, docker-compose, and deployment. + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `enabled` | `bool` | no | `false` | Enable AI features | +| `provider` | `enum` | no | `openai` | LLM provider | +| `model` | `string` | no | Provider default | Model name | +| `api_key` | `string` | no* | none | API key (supports `${VAR}` syntax) | +| `endpoint` | `string` | no | Provider default | Custom API endpoint URL | +| `timeout` | `integer` | no | `300` | Request timeout in seconds (increase for slow models / weak hardware) | +| `tasks` | `string[]` | no | `[]` | Allowed AI task types | + +**Supported providers:** + +| Value | Provider | Default Endpoint | Requires API Key | +|-------|----------|-----------------|------------------| +| `openai` | OpenAI | `https://api.openai.com/v1` | Yes | +| `anthropic` | Anthropic | `https://api.anthropic.com/v1` | Yes | +| `ollama` | Ollama (local) | `http://localhost:11434` | No | +| `custom` | Any OpenAI-compatible API | Must specify `endpoint` | Varies | + +**Task types** (used for prompt specialisation): +- `dockerfile` — Dockerfile optimisation and generation +- `troubleshoot` — Debugging deployment issues +- `compose` — docker-compose configuration help +- `security` — Security review and hardening + +```yaml +# Using OpenAI +ai: + enabled: true + provider: openai + model: gpt-4 + api_key: ${OPENAI_API_KEY} + tasks: + - dockerfile + - troubleshoot + +# Using local Ollama +ai: + enabled: true + provider: ollama + model: llama3 + endpoint: http://localhost:11434 + timeout: 600 # 10 minutes for large models on slower hardware + +# Using a custom OpenAI-compatible API (e.g. Groq, Together AI) +ai: + enabled: true + provider: custom + model: mixtral-8x7b-32768 + api_key: ${GROQ_API_KEY} + endpoint: https://api.groq.com/openai/v1 +``` + +--- + +## `monitoring` + +*Optional* · `object` · Default: `status_panel: false` + +Monitoring and health check configuration. + +### `monitoring.status_panel` + +*Optional* · `bool` · Default: `false` + +Enable the Stacker status panel — a web UI showing container health, resource usage, and deployment status. + +```yaml +monitoring: + status_panel: true +``` + +### `monitoring.healthcheck` + +*Optional* · `object` · Default: none + +Application health check settings. + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `endpoint` | `string` | `/health` | HTTP path to probe | +| `interval` | `string` | `30s` | Time between checks | + +```yaml +monitoring: + healthcheck: + endpoint: /api/health + interval: 15s +``` + +### `monitoring.metrics` + +*Optional* · `object` · Default: none + +Metrics collection settings. + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `enabled` | `bool` | `false` | Enable metrics collection | +| `telegraf` | `bool` | `false` | Deploy Telegraf agent for metrics | + +```yaml +monitoring: + metrics: + enabled: true + telegraf: true +``` + +--- + +## `hooks` + +*Optional* · `object` · Default: none + +Lifecycle hook scripts. Stacker runs these at specific points during the build and deploy process. + +| Field | Type | Description | When it runs | +|-------|------|-------------|------| +| `pre_build` | `string` (path) | Script to run before Docker build | Before `docker build` | +| `post_deploy` | `string` (path) | Script to run after successful deployment | After `docker compose up` succeeds | +| `on_failure` | `string` (path) | Script to run on deployment failure | When any deploy step fails | + +```yaml +hooks: + pre_build: ./scripts/pre-build.sh + post_deploy: ./scripts/seed-database.sh + on_failure: ./scripts/alert-team.sh +``` + +> Hook scripts must be executable (`chmod +x`). + +--- + +## `env` / `env_file` + +### `env` + +*Optional* · `map` · Default: `{}` + +Inline environment variables passed to all containers. Supports `${VAR}` interpolation. + +```yaml +env: + APP_PORT: "3000" + LOG_LEVEL: info + DATABASE_URL: postgres://app:${DB_PASSWORD}@postgres:5432/myapp +``` + +### `env_file` + +*Optional* · `string` (path) · Default: none + +Path to a `.env` file. Loaded before the config is parsed, so variables defined here can be referenced with `${VAR}` syntax anywhere in `stacker.yml`. + +```yaml +env_file: .env +``` + +Example `.env`: +``` +DB_PASSWORD=s3cret +MINIO_PASSWORD=admin123 +OPENAI_API_KEY=sk-... +``` + +--- + +## Environment Variable Interpolation + +Any value in `stacker.yml` can reference environment variables using `${VAR_NAME}` syntax. Variables are resolved from the process environment at parse time. + +```yaml +name: ${PROJECT_NAME} +app: + type: node +services: + - name: postgres + image: postgres:${PG_VERSION} + environment: + POSTGRES_PASSWORD: ${DB_PASSWORD} +deploy: + target: cloud + cloud: + provider: ${CLOUD_PROVIDER} +ai: + api_key: ${OPENAI_API_KEY} +``` + +**Rules:** +- Syntax: `${VARIABLE_NAME}` (curly braces required) +- Undefined variables cause a parse error (fail-fast, no silent empty strings) +- Interpolation happens before YAML parsing +- Works in all string values including paths, URLs, and map values + +--- + +## Auto-Detection + +When you run `stacker init` without specifying `--app-type`, Stacker scans your project directory and looks for these marker files: + +| Files Found | Detected Type | +|-------------|---------------| +| `package.json` | `node` | +| `requirements.txt`, `Pipfile`, `pyproject.toml`, `setup.py` | `python` | +| `Cargo.toml` | `rust` | +| `go.mod` | `go` | +| `composer.json` | `php` | +| `index.html`, `*.html` | `static` | + +Detection priority is top-to-bottom. If none of these files are found, it defaults to `static`. + +--- + +## Generated Dockerfiles + +When you run `stacker deploy`, Stacker generates a Dockerfile in `.stacker/Dockerfile` based on `app.type`. Here's what each template produces: + +### `static` +```dockerfile +FROM nginx:alpine +COPY . /usr/share/nginx/html +EXPOSE 80 +``` + +### `node` +```dockerfile +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --production +COPY . . +EXPOSE 3000 +CMD ["node", "server.js"] +``` + +### `python` +```dockerfile +FROM python:3.12-slim +WORKDIR /app +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt +COPY . . +EXPOSE 8000 +CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] +``` + +### `rust` +```dockerfile +FROM rust:1.77-alpine +WORKDIR /app +RUN apk add --no-cache musl-dev +COPY . . +RUN cargo build --release +EXPOSE 8080 +CMD ["./target/release/app"] +``` + +### `go` +```dockerfile +FROM golang:1.22-alpine +WORKDIR /app +COPY go.mod ./ +COPY go.sum ./ +RUN go mod download +COPY . . +RUN go build -o /app/server . +EXPOSE 8080 +CMD ["/app/server"] +``` + +### `php` +```dockerfile +FROM php:8.3-fpm-alpine +WORKDIR /var/www/html +RUN docker-php-ext-install pdo pdo_mysql +COPY . . +EXPOSE 9000 +``` + +### `custom` +No Dockerfile is generated. You must provide either `app.dockerfile` or `app.image`. + +> **Customisation:** To modify the generated Dockerfile, deploy once with `--dry-run`, edit `.stacker/Dockerfile`, then deploy again with `--force-rebuild`. + +--- + +## Validation Rules + +Stacker validates your configuration both syntactically (YAML structure) and semantically (cross-field logic). Run `stacker config validate` to check. + +### Errors (deployment will fail) + +| Code | Rule | Field | +|------|------|-------| +| `E001` | Cloud deployment requires `deploy.cloud.provider` | `deploy.cloud.provider` | +| `E002` | Server deployment requires `deploy.server.host` | `deploy.server.host` | +| `E003` | Custom app type requires `app.image` or `app.dockerfile` | `app` | + +### Warnings (deployment may have issues) + +| Code | Rule | Field | +|------|------|-------| +| `W001` | Port conflict — multiple services bind the same host port | `services.ports` | + +### Example output + +``` +$ stacker config validate +Configuration issues: + - [E001] Cloud provider configuration is required for cloud deployment (deploy.cloud.provider) + - [W001] Port 8080 is used by multiple services: api, worker (services.ports) +``` + +--- + +## CLI Commands Reference + +| Command | Description | +|---------|-------------| +| `stacker init` | Initialize a new project — generates `stacker.yml` and `.stacker/` directory (Dockerfile + docker-compose.yml) | +| `stacker deploy` | Build and deploy the stack (reuses existing `.stacker/` artifacts if present) | +| `stacker status` | Show container status | +| `stacker logs` | Show container logs | +| `stacker destroy` | Tear down the stack | +| `stacker config validate` | Validate `stacker.yml` | +| `stacker config show` | Display resolved configuration | +| `stacker config fix` | Interactively fix missing required config fields | +| `stacker login` | Authenticate with TryDirect | +| `stacker ai ask` | Ask the AI assistant a question | +| `stacker proxy add` | Add a reverse-proxy domain entry | +| `stacker proxy detect` | Detect running reverse proxies | +| `stacker update` | Check for CLI updates | + +### `stacker init` flags + +| Flag | Description | +|------|-------------| +| `--app-type ` | Application type: `static`, `node`, `python`, `rust`, `go`, `php`, `custom` | +| `--with-proxy` | Include reverse-proxy (nginx) configuration | +| `--with-ai` | Use AI to scan the project and generate a tailored `stacker.yml` | +| `--ai-provider ` | AI provider: `openai`, `anthropic`, `ollama`, `custom` (default: `ollama`) | +| `--ai-model ` | AI model name (e.g. `gpt-4o`, `claude-sonnet-4-20250514`, `qwen2.5-coder`, `deepseek-r1`) | +| `--ai-api-key ` | AI API key (or set `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` env var) | + +`stacker init` generates: +- `stacker.yml` — project configuration +- `.stacker/Dockerfile` — generated Dockerfile (skipped if `app.image` or `app.dockerfile` is set) +- `.stacker/docker-compose.yml` — generated compose definition (skipped if `deploy.compose_file` is set) + +```bash +# Init +stacker init # Auto-detect project type +stacker init --app-type node --with-proxy # Explicit type + proxy +stacker init --with-ai # AI-powered generation (Ollama default) +stacker init --with-ai --ai-model qwen2.5-coder # Specify Ollama model +stacker init --with-ai --ai-provider ollama --ai-model deepseek-r1 +stacker init --with-ai --ai-provider openai --ai-api-key sk-... +stacker init --with-ai --ai-provider anthropic --ai-model claude-sonnet-4-20250514 + +# AI init environment variables (override CLI defaults) +# STACKER_AI_PROVIDER — AI provider (openai, anthropic, ollama, custom) +# STACKER_AI_MODEL — Model name +# STACKER_AI_API_KEY — API key (generic, provider-specific vars also supported) +# STACKER_AI_ENDPOINT — Custom endpoint URL +# STACKER_AI_TIMEOUT — Request timeout in seconds (default: 300) +# OPENAI_API_KEY — OpenAI API key (used when provider is openai) +# ANTHROPIC_API_KEY — Anthropic API key (used when provider is anthropic) +STACKER_AI_TIMEOUT=900 stacker init --with-ai # 15 min timeout for slow models +``` + +### `stacker deploy` flags + +```bash +stacker deploy --target local # Deploy locally +stacker deploy --target cloud # Deploy to cloud +stacker deploy --target local --dry-run # Generate files without deploying +stacker deploy --file custom.yml # Use a custom config file +stacker deploy --force-rebuild # Force regenerate .stacker/ artifacts + +``` + +> +> **Troubleshooting:** On deploy build/runtime failures, Stacker attempts AI-assisted diagnosis using your configured AI provider. If AI is unavailable, it prints fallback fix suggestions. +> **Note:** `deploy` reuses existing `.stacker/Dockerfile` and `.stacker/docker-compose.yml` if present (e.g. from `stacker init`). Use `--force-rebuild` to regenerate them. + +### Other commands + +```bash +# Logs +stacker logs # All services +stacker logs --service postgres # Specific service +stacker logs --follow # Stream logs +stacker logs --tail 100 # Last 100 lines +stacker logs --since 1h # Logs from the last hour + +# Status +stacker status # Table format +stacker status --json # JSON output +stacker status --watch # Auto-refresh + +# Destroy +stacker destroy --confirm # Required flag (safety guard) +stacker destroy --confirm --volumes # Also remove volumes + +# Config +stacker config validate # Check stacker.yml +stacker config validate --file prod.yml +stacker config show # Display resolved config + +# AI +stacker ai ask "How can I optimise this Dockerfile?" +stacker ai ask "Why is my container crashing?" --context ./logs.txt + +# Proxy +stacker proxy add example.com --upstream http://app:3000 --ssl auto +stacker proxy detect + +# Update +stacker update # Check stable channel +stacker update --channel beta # Check beta channel + +# Config +stacker config fix # Interactively fix missing fields +stacker config fix --file prod.yml # Fix a specific config file +``` + +--- + +## Recipes + +### Static website +```yaml +name: my-website +app: + type: static + path: ./dist +deploy: + target: local +``` + +### Node.js API with PostgreSQL +```yaml +name: my-api +app: + type: node + path: . +services: + - name: postgres + image: postgres:16 + ports: + - "5432:5432" + environment: + POSTGRES_DB: api_db + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - pgdata:/var/lib/postgresql/data +deploy: + target: local +env: + DATABASE_URL: postgres://postgres:${DB_PASSWORD}@postgres:5432/api_db +``` + +### Python Django with Redis and Nginx +```yaml +name: django-app +app: + type: python + path: . + build: + args: + DJANGO_SETTINGS_MODULE: myapp.settings.production +services: + - name: redis + image: redis:7-alpine + - name: celery + image: django-app:latest + depends_on: + - redis + environment: + CELERY_BROKER_URL: redis://redis:6379/0 +proxy: + type: nginx + domains: + - domain: myapp.example.com + ssl: auto + upstream: app:8000 +deploy: + target: cloud + cloud: + provider: hetzner + region: fsn1 + size: cx21 + ssh_key: ~/.ssh/id_ed25519 +``` + +### Rust API deployed to existing server +```yaml +name: rust-api +app: + type: rust + path: . +deploy: + target: server + server: + host: api.example.com + user: deploy + ssh_key: ~/.ssh/deploy_key +monitoring: + status_panel: true + healthcheck: + endpoint: /api/health + interval: 15s +``` + +### Pre-built image (no source) +```yaml +name: wordpress-site +app: + type: custom + image: wordpress:6-apache +services: + - name: mysql + image: mysql:8 + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: wordpress + volumes: + - db-data:/var/lib/mysql +proxy: + type: nginx + domains: + - domain: blog.example.com + ssl: auto + upstream: app:80 +deploy: + target: local +``` + +### Multi-environment with interpolation +```yaml +name: ${APP_NAME} +version: ${APP_VERSION} +app: + type: node + build: + args: + NODE_ENV: ${NODE_ENV} + API_URL: ${API_URL} +services: + - name: postgres + image: postgres:${PG_VERSION} + environment: + POSTGRES_PASSWORD: ${DB_PASSWORD} +deploy: + target: ${DEPLOY_TARGET} +``` + +Run with different environments: +```bash +# Development +APP_NAME=myapp APP_VERSION=dev NODE_ENV=development \ + API_URL=http://localhost:3000 PG_VERSION=16 \ + DB_PASSWORD=devpass DEPLOY_TARGET=local \ + stacker deploy + +# Production +APP_NAME=myapp APP_VERSION=1.2.3 NODE_ENV=production \ + API_URL=https://api.example.com PG_VERSION=16 \ + DB_PASSWORD=$PROD_DB_PASSWORD DEPLOY_TARGET=cloud \ + stacker deploy +``` + +--- + +## FAQ + +**Q: Where are generated files stored?** +A: In the `.stacker/` directory. This includes `Dockerfile`, `docker-compose.yml`, and any proxy configuration. Add `.stacker/` to your `.gitignore`. + +**Q: Can I edit the generated Dockerfile?** +A: Yes. After `stacker init` (or `stacker deploy --dry-run`), edit `.stacker/Dockerfile`, then `stacker deploy` to build from your modified version. Stacker reuses existing `.stacker/` files unless `--force-rebuild` is passed. + +**Q: What if I already have a Dockerfile?** +A: Set `app.type: custom` and `app.dockerfile: ./Dockerfile`. Stacker will use yours instead of generating one. + +**Q: Do I need Docker installed?** +A: Yes. Stacker requires Docker (with Compose v2) for local deployments. For cloud deployments, Docker is provisioned on the remote server automatically. + +**Q: How do I keep secrets out of stacker.yml?** +A: Use environment variable interpolation (`${SECRET_VAR}`) and store actual values in `.env` (referenced via `env_file: .env`). Never commit `.env` to version control. + +**Q: Can I use Stacker with an existing docker-compose.yml?** +A: Yes. Set `deploy.compose_file: ./docker-compose.yml` and Stacker will use it directly without generating a new one. + +**Q: What cloud providers are supported?** +A: Hetzner, DigitalOcean, AWS, Linode, and Vultr. You must `stacker login` first and have the appropriate API keys configured in your TryDirect account. + +--- + +## File Structure + +After `stacker init`, your project will look like: + +``` +my-project/ +├── stacker.yml ← Your configuration (you write this) +├── .stacker/ ← Generated artifacts (auto-created) +│ ├── Dockerfile ← Generated Dockerfile +│ └── docker-compose.yml ← Generated compose definition +├── .env ← Secrets (optional, gitignored) +├── src/ ← Your application source +└── scripts/ ← Hook scripts (optional) + ├── pre-build.sh + ├── post-deploy.sh + └── notify-failure.sh +``` + +--- + +*Stacker CLI is part of the [TryDirect](https://try.direct) platform.* diff --git a/migrations/20260219120000_create_chat_conversations.down.sql b/migrations/20260219120000_create_chat_conversations.down.sql new file mode 100644 index 00000000..d3a54996 --- /dev/null +++ b/migrations/20260219120000_create_chat_conversations.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS chat_conversations; diff --git a/migrations/20260219120000_create_chat_conversations.up.sql b/migrations/20260219120000_create_chat_conversations.up.sql new file mode 100644 index 00000000..b06f60d4 --- /dev/null +++ b/migrations/20260219120000_create_chat_conversations.up.sql @@ -0,0 +1,18 @@ +-- Chat conversations: persists AI chat history per user per project +CREATE TABLE chat_conversations ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id VARCHAR(255) NOT NULL, + project_id INTEGER, -- NULL = canvas / onboarding mode + messages JSONB NOT NULL DEFAULT '[]'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- One row per (user, project) pair; partial indexes allow NULL project_id +CREATE UNIQUE INDEX idx_chat_conv_user_project + ON chat_conversations(user_id, project_id) + WHERE project_id IS NOT NULL; + +CREATE UNIQUE INDEX idx_chat_conv_user_no_project + ON chat_conversations(user_id) + WHERE project_id IS NULL; diff --git a/migrations/20260219130000_casbin_chat_rules.down.sql b/migrations/20260219130000_casbin_chat_rules.down.sql new file mode 100644 index 00000000..f4f6558d --- /dev/null +++ b/migrations/20260219130000_casbin_chat_rules.down.sql @@ -0,0 +1,3 @@ +DELETE FROM public.casbin_rule +WHERE ptype = 'p' + AND v1 = '/chat/history'; diff --git a/migrations/20260219130000_casbin_chat_rules.up.sql b/migrations/20260219130000_casbin_chat_rules.up.sql new file mode 100644 index 00000000..9447d738 --- /dev/null +++ b/migrations/20260219130000_casbin_chat_rules.up.sql @@ -0,0 +1,11 @@ +-- Allow authenticated users and admins to access chat history endpoints + +INSERT INTO public.casbin_rule (ptype, v0, v1, v2, v3, v4, v5) +VALUES + ('p', 'group_user', '/chat/history', 'GET', '', '', ''), + ('p', 'group_user', '/chat/history', 'PUT', '', '', ''), + ('p', 'group_user', '/chat/history', 'DELETE', '', '', ''), + ('p', 'group_admin', '/chat/history', 'GET', '', '', ''), + ('p', 'group_admin', '/chat/history', 'PUT', '', '', ''), + ('p', 'group_admin', '/chat/history', 'DELETE', '', '', '') +ON CONFLICT ON CONSTRAINT unique_key_sqlx_adapter DO NOTHING; diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 00000000..6f0b486a --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Stacker CLI installer +# +# Usage: +# curl -fsSL https://get.stacker.dev/install.sh | bash +# curl -fsSL https://get.stacker.dev/install.sh | bash -s -- --channel beta +# +# Environment variables: +# STACKER_INSTALL_DIR — where to install (default: /usr/local/bin) +# STACKER_CHANNEL — release channel: stable, beta (default: stable) +# STACKER_VERSION — pin to a specific version (e.g. 0.2.2) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +set -euo pipefail + +REPO="trydirect/stacker" +INSTALL_DIR="${STACKER_INSTALL_DIR:-/usr/local/bin}" +CHANNEL="${STACKER_CHANNEL:-stable}" +VERSION="${STACKER_VERSION:-latest}" +BINARY_NAME="stacker" + +# ── Helpers ────────────────────────────────────────── + +info() { printf "\033[1;34m▸\033[0m %s\n" "$*"; } +ok() { printf "\033[1;32m✓\033[0m %s\n" "$*"; } +err() { printf "\033[1;31m✗\033[0m %s\n" "$*" >&2; exit 1; } + +need() { + command -v "$1" >/dev/null 2>&1 || err "Required command not found: $1" +} + +# ── Detect platform ───────────────────────────────── + +detect_os() { + case "$(uname -s)" in + Linux*) echo "linux" ;; + Darwin*) echo "darwin" ;; + *) err "Unsupported OS: $(uname -s)" ;; + esac +} + +detect_arch() { + case "$(uname -m)" in + x86_64|amd64) echo "x86_64" ;; + arm64|aarch64) echo "aarch64" ;; + *) err "Unsupported architecture: $(uname -m)" ;; + esac +} + +# ── Resolve version ───────────────────────────────── + +resolve_version() { + if [ "$VERSION" = "latest" ]; then + need curl + VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" \ + | grep '"tag_name"' \ + | sed -E 's/.*"v?([^"]+)".*/\1/') + [ -n "$VERSION" ] || err "Could not determine latest version" + fi + echo "$VERSION" +} + +# ── Download & install ─────────────────────────────── + +download_and_install() { + local os arch version archive_name url tmpdir + + os=$(detect_os) + arch=$(detect_arch) + version=$(resolve_version) + + archive_name="stacker-v${version}-${arch}-${os}.tar.gz" + url="https://github.com/${REPO}/releases/download/v${version}/${archive_name}" + + info "Downloading stacker v${version} for ${os}/${arch}..." + info " ${url}" + + need curl + need tar + + tmpdir=$(mktemp -d) + trap 'rm -rf "$tmpdir"' EXIT + + curl -fsSL "$url" -o "${tmpdir}/${archive_name}" \ + || err "Download failed. Check the version exists: v${version}" + + tar -xzf "${tmpdir}/${archive_name}" -C "$tmpdir" \ + || err "Extraction failed" + + # Find the binary in the extracted archive + local bin_path + bin_path=$(find "$tmpdir" -name "$BINARY_NAME" -type f | head -1) + [ -n "$bin_path" ] || bin_path=$(find "$tmpdir" -name "stacker-cli" -type f | head -1) + [ -n "$bin_path" ] || err "Binary not found in archive" + + chmod +x "$bin_path" + + # Install + if [ -w "$INSTALL_DIR" ]; then + mv "$bin_path" "${INSTALL_DIR}/${BINARY_NAME}" + else + info "Installing to ${INSTALL_DIR} (requires sudo)..." + sudo mv "$bin_path" "${INSTALL_DIR}/${BINARY_NAME}" + fi + + ok "Installed stacker v${version} to ${INSTALL_DIR}/${BINARY_NAME}" +} + +# ── Verify install ─────────────────────────────────── + +verify() { + if command -v "$BINARY_NAME" >/dev/null 2>&1; then + ok "Verification: $($BINARY_NAME --version)" + else + info "Note: ${INSTALL_DIR} may not be in your PATH" + info " Add it: export PATH=\"${INSTALL_DIR}:\$PATH\"" + fi +} + +# ── Parse args ─────────────────────────────────────── + +while [ $# -gt 0 ]; do + case "$1" in + --channel) CHANNEL="$2"; shift 2 ;; + --version) VERSION="$2"; shift 2 ;; + --dir) INSTALL_DIR="$2"; shift 2 ;; + --help|-h) + echo "Usage: install.sh [--channel stable|beta] [--version X.Y.Z] [--dir /path]" + exit 0 + ;; + *) err "Unknown option: $1" ;; + esac +done + +# ── Main ───────────────────────────────────────────── + +info "Stacker CLI installer" +info " Channel: ${CHANNEL}" +info " Install dir: ${INSTALL_DIR}" +echo "" + +download_and_install +verify + +echo "" +ok "Done! Run 'stacker --help' to get started." diff --git a/src/bin/stacker.rs b/src/bin/stacker.rs new file mode 100644 index 00000000..ad7ca520 --- /dev/null +++ b/src/bin/stacker.rs @@ -0,0 +1,349 @@ +//! Standalone `stacker` CLI binary. +//! +//! Exposes the Stacker CLI commands directly at the top level: +//! +//! ```text +//! stacker init +//! stacker deploy --target local +//! stacker status +//! stacker logs --follow +//! stacker destroy --confirm +//! ``` +//! +//! Unlike the `console` binary (which nests these under `stacker` subcommand +//! alongside other admin tools), this binary is a lightweight entry point +//! designed for end-user distribution. + +use clap::{Parser, Subcommand}; + +#[derive(Parser, Debug)] +#[command( + name = "stacker", + version, + about = "Deploy apps from a stacker.yml config", + long_about = "Stacker CLI — build, deploy, and manage containerised applications\n\n\ + Create a stacker.yml configuration file, and Stacker will generate\n\ + Dockerfiles, docker-compose definitions, and deploy your stack locally\n\ + or to cloud providers with a single command." +)] +struct Cli { + #[command(subcommand)] + command: StackerCommands, +} + +#[derive(Debug, Subcommand)] +enum StackerCommands { + /// Authenticate with the TryDirect platform + Login { + /// Organisation slug (for multi-org accounts) + #[arg(long)] + org: Option, + /// Custom platform domain + #[arg(long)] + domain: Option, + /// API base URL (default: https://api.try.direct) + #[arg(long = "auth-url", visible_alias = "api-url")] + auth_url: Option, + }, + /// Initialize a new stacker project (generates stacker.yml + Dockerfile) + Init { + /// Application type: static, node, python, rust, go, php + #[arg(long, value_name = "TYPE")] + app_type: Option, + /// Include reverse-proxy configuration + #[arg(long)] + with_proxy: bool, + /// Use AI to scan the project and generate a tailored stacker.yml + #[arg(long)] + with_ai: bool, + /// Immediately run cloud setup wizard after init + #[arg(long)] + with_cloud: bool, + /// AI provider: openai, anthropic, ollama, custom (default: ollama) + #[arg(long, value_name = "PROVIDER")] + ai_provider: Option, + /// AI model name (e.g. gpt-4o, claude-sonnet-4-20250514, llama3) + #[arg(long, value_name = "MODEL")] + ai_model: Option, + /// AI API key (or set OPENAI_API_KEY / ANTHROPIC_API_KEY env var) + #[arg(long, value_name = "KEY")] + ai_api_key: Option, + }, + /// Build & deploy the stack + Deploy { + /// Deployment target: local, cloud, server + #[arg(long, value_name = "TARGET")] + target: Option, + /// Path to stacker.yml (default: ./stacker.yml) + #[arg(long, value_name = "FILE")] + file: Option, + /// Show what would be deployed without executing + #[arg(long)] + dry_run: bool, + /// Force rebuild of all containers + #[arg(long)] + force_rebuild: bool, + /// Project name on the Stacker server (overrides project.identity in stacker.yml) + #[arg(long, value_name = "NAME")] + project: Option, + /// Name of saved cloud credential to reuse (overrides deploy.cloud.key in stacker.yml) + #[arg(long, value_name = "KEY_NAME")] + key: Option, + /// Name of saved server to reuse (overrides deploy.cloud.server in stacker.yml) + #[arg(long, value_name = "SERVER_NAME")] + server: Option, + }, + /// Show container logs + Logs { + /// Show logs for a specific service only + #[arg(long)] + service: Option, + /// Follow log output (stream) + #[arg(long, short)] + follow: bool, + /// Number of lines to show from the end + #[arg(long)] + tail: Option, + /// Show logs since timestamp (e.g. "2h", "2024-01-01") + #[arg(long)] + since: Option, + }, + /// Show deployment status + Status { + /// Output in JSON format + #[arg(long)] + json: bool, + /// Watch for changes (refresh periodically) + #[arg(long)] + watch: bool, + }, + /// Tear down the deployed stack + Destroy { + /// Also remove named volumes + #[arg(long)] + volumes: bool, + /// Skip confirmation prompt (required) + #[arg(long, short = 'y')] + confirm: bool, + }, + /// Configuration management + Config { + #[command(subcommand)] + command: ConfigCommands, + }, + /// AI-assisted operations + Ai { + #[command(subcommand)] + command: AiCommands, + }, + /// Reverse-proxy management + Proxy { + #[command(subcommand)] + command: ProxyCommands, + }, + /// Check for updates and self-update + Update { + /// Release channel: stable, beta + #[arg(long)] + channel: Option, + }, +} + +#[derive(Debug, Subcommand)] +enum ConfigCommands { + /// Validate stacker.yml syntax and semantics + Validate { + #[arg(long, value_name = "FILE")] + file: Option, + }, + /// Show resolved configuration + Show { + #[arg(long, value_name = "FILE")] + file: Option, + }, + /// Print a full commented `stacker.yml` reference example + Example, + /// Interactively fix missing required config fields + Fix { + #[arg(long, value_name = "FILE")] + file: Option, + /// Enable interactive prompts (default: true) + #[arg(long, default_value_t = true)] + interactive: bool, + }, + /// Guided setup helpers + Setup { + #[command(subcommand)] + command: ConfigSetupCommands, + }, +} + +#[derive(Debug, Subcommand)] +enum ConfigSetupCommands { + /// Configure cloud deployment defaults in stacker.yml + Cloud { + #[arg(long, value_name = "FILE")] + file: Option, + }, + /// Advanced/debug: generate remote orchestrator payload and wire stacker.yml + RemotePayload { + #[arg(long, value_name = "FILE")] + file: Option, + #[arg(long, value_name = "OUT")] + out: Option, + }, +} + +#[derive(Debug, Subcommand)] +enum AiCommands { + /// Ask the AI a question about your stack + Ask { + /// The question to ask + question: String, + /// Path to a file to include as context + #[arg(long)] + context: Option, + /// Interactively configure AI in stacker.yml before asking + #[arg(long)] + configure: bool, + }, +} + +#[derive(Debug, Subcommand)] +enum ProxyCommands { + /// Add a reverse-proxy entry for a domain + Add { + /// Domain name (e.g. example.com) + domain: String, + /// Upstream service address (e.g. http://app:8080) + #[arg(long)] + upstream: Option, + /// SSL mode: auto, manual, off + #[arg(long)] + ssl: Option, + }, + /// Detect existing reverse-proxy containers + Detect, +} + +fn main() -> Result<(), Box> { + let cli = Cli::parse(); + let command = get_command(cli)?; + if let Err(err) = command.call() { + eprintln!("Error: {}", err); + std::process::exit(1); + } + Ok(()) +} + +fn get_command( + cli: Cli, +) -> Result, Box> { + let cmd: Box = match cli.command { + StackerCommands::Login { + org, + domain, + auth_url, + } => Box::new( + stacker::console::commands::cli::login::LoginCommand::new(org, domain, auth_url), + ), + StackerCommands::Init { + app_type, + with_proxy, + with_ai, + with_cloud, + ai_provider, + ai_model, + ai_api_key, + } => Box::new( + stacker::console::commands::cli::init::InitCommand::new( + app_type, with_proxy, with_ai, with_cloud, + ) + .with_ai_options(ai_provider, ai_model, ai_api_key), + ), + StackerCommands::Deploy { + target, + file, + dry_run, + force_rebuild, + project, + key, + server, + } => Box::new( + stacker::console::commands::cli::deploy::DeployCommand::new( + target, + file, + dry_run, + force_rebuild, + ) + .with_remote_overrides(project, key, server), + ), + StackerCommands::Logs { + service, + follow, + tail, + since, + } => Box::new(stacker::console::commands::cli::logs::LogsCommand::new( + service, follow, tail, since, + )), + StackerCommands::Status { json, watch } => Box::new( + stacker::console::commands::cli::status::StatusCommand::new(json, watch), + ), + StackerCommands::Destroy { volumes, confirm } => Box::new( + stacker::console::commands::cli::destroy::DestroyCommand::new(volumes, confirm), + ), + StackerCommands::Config { command: cfg_cmd } => match cfg_cmd { + ConfigCommands::Validate { file } => Box::new( + stacker::console::commands::cli::config::ConfigValidateCommand::new(file), + ), + ConfigCommands::Show { file } => Box::new( + stacker::console::commands::cli::config::ConfigShowCommand::new(file), + ), + ConfigCommands::Example => Box::new( + stacker::console::commands::cli::config::ConfigExampleCommand::new(), + ), + ConfigCommands::Fix { file, interactive } => Box::new( + stacker::console::commands::cli::config::ConfigFixCommand::new(file, interactive), + ), + ConfigCommands::Setup { command } => match command { + ConfigSetupCommands::Cloud { file } => Box::new( + stacker::console::commands::cli::config::ConfigSetupCloudCommand::new(file), + ), + ConfigSetupCommands::RemotePayload { file, out } => Box::new( + stacker::console::commands::cli::config::ConfigSetupRemotePayloadCommand::new(file, out), + ), + }, + }, + StackerCommands::Ai { command: ai_cmd } => match ai_cmd { + AiCommands::Ask { + question, + context, + configure, + } => Box::new( + stacker::console::commands::cli::ai::AiAskCommand::new(question, context) + .with_configure(configure), + ), + }, + StackerCommands::Proxy { + command: proxy_cmd, + } => match proxy_cmd { + ProxyCommands::Add { + domain, + upstream, + ssl, + } => Box::new( + stacker::console::commands::cli::proxy::ProxyAddCommand::new( + domain, upstream, ssl, + ), + ), + ProxyCommands::Detect => Box::new( + stacker::console::commands::cli::proxy::ProxyDetectCommand::new(), + ), + }, + StackerCommands::Update { channel } => Box::new( + stacker::console::commands::cli::update::UpdateCommand::new(channel), + ), + }; + + Ok(cmd) +} diff --git a/src/cli/ai_client.rs b/src/cli/ai_client.rs new file mode 100644 index 00000000..82e2b052 --- /dev/null +++ b/src/cli/ai_client.rs @@ -0,0 +1,1067 @@ +use crate::cli::config_parser::{AiConfig, AiProviderType, AppType}; +use crate::cli::error::CliError; +use std::io::{BufRead, BufReader, Write}; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Constants +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Default OpenAI-compatible endpoint. +pub const OPENAI_API_URL: &str = "https://api.openai.com/v1/chat/completions"; + +/// Default Anthropic endpoint. +pub const ANTHROPIC_API_URL: &str = "https://api.anthropic.com/v1/messages"; + +/// Default Ollama endpoint. +pub const OLLAMA_API_URL: &str = "http://localhost:11434/api/chat"; + +/// Ollama tags endpoint (for listing available models). +pub const OLLAMA_TAGS_URL: &str = "http://localhost:11434/api/tags"; + +/// Default model per provider when none is specified in config. +pub fn default_model(provider: AiProviderType) -> &'static str { + match provider { + AiProviderType::Openai => "gpt-4o", + AiProviderType::Anthropic => "claude-sonnet-4-20250514", + AiProviderType::Ollama => "llama3", + AiProviderType::Custom => "default", + } +} + +/// Preferred Ollama models for stacker.yml generation, in priority order. +/// The first available model from this list will be used. +const OLLAMA_PREFERRED_MODELS: &[&str] = &[ + "llama3", + "llama3.1", + "llama3.2", + "llama3:latest", + "codellama", + "mistral", + "mixtral", + "deepseek-r1", + "deepseek-coder", + "qwen2.5-coder", + "qwen2.5", + "phi3", + "gemma2", + "gpt-oss", +]; + +/// Default request timeout in seconds. +const DEFAULT_AI_TIMEOUT_SECS: u64 = 300; + +/// Resolve the AI request timeout in seconds. +/// +/// Priority: `STACKER_AI_TIMEOUT` env var > `AiConfig.timeout` value > 300s default. +/// A value of 0 means no timeout (unlimited). +pub fn resolve_timeout(config_timeout: u64) -> u64 { + if let Ok(val) = std::env::var("STACKER_AI_TIMEOUT") { + if let Ok(secs) = val.parse::() { + return secs; + } + } + if config_timeout > 0 { + config_timeout + } else { + DEFAULT_AI_TIMEOUT_SECS + } +} + +/// Query the local Ollama instance for available models. +/// Returns a list of model names, or an empty vec if Ollama is unreachable. +pub fn list_ollama_models(base_url: Option<&str>) -> Vec { + let tags_url = base_url + .map(|u| { + // Convert chat endpoint to tags endpoint + u.replace("/api/chat", "/api/tags") + .replace("/api/generate", "/api/tags") + }) + .unwrap_or_else(|| OLLAMA_TAGS_URL.to_string()); + + let client = match reqwest::blocking::Client::builder() + .timeout(std::time::Duration::from_secs(5)) + .build() + { + Ok(c) => c, + Err(_) => return Vec::new(), + }; + + let response = match client.get(&tags_url).send() { + Ok(r) if r.status().is_success() => r, + _ => return Vec::new(), + }; + + let json: serde_json::Value = match response.json() { + Ok(j) => j, + Err(_) => return Vec::new(), + }; + + json["models"] + .as_array() + .map(|models| { + models + .iter() + .filter_map(|m| m["name"].as_str().map(|s| s.to_string())) + .collect() + }) + .unwrap_or_default() +} + +/// Pick the best available Ollama model for config generation. +/// Checks the preferred list first, then falls back to the first available model. +/// Returns None if no models are available. +pub fn pick_ollama_model(base_url: Option<&str>) -> Option { + let available = list_ollama_models(base_url); + if available.is_empty() { + return None; + } + + // Check preferred models in priority order + for preferred in OLLAMA_PREFERRED_MODELS { + for avail in &available { + // Match base name (e.g. "deepseek-r1" matches "deepseek-r1:latest") + let avail_base = avail.split(':').next().unwrap_or(avail); + if avail_base == *preferred || avail == preferred { + return Some(avail.clone()); + } + } + } + + // No preferred model found — use the first non-embedding model + available + .into_iter() + .find(|m| !m.contains("embed")) + .or_else(|| Some("llama3".to_string())) +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// AiProvider trait — abstraction over LLM backends (DIP) +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Abstraction for LLM completion providers. +/// +/// Production: `OpenAiProvider`, `AnthropicProvider`, `OllamaProvider`. +/// Tests: `MockAiProvider` returns canned responses. +pub trait AiProvider: Send + Sync { + /// Provider name for error reporting. + fn name(&self) -> &str; + + /// Send a completion request and return the response text. + fn complete(&self, prompt: &str, context: &str) -> Result; +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// OpenAiProvider — OpenAI / OpenAI-compatible APIs +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Calls the OpenAI Chat Completions API (or any compatible endpoint). +/// Also works with Azure OpenAI, Together AI, Groq, etc. +pub struct OpenAiProvider { + pub endpoint: String, + pub api_key: String, + pub model: String, + pub timeout_secs: u64, +} + +impl OpenAiProvider { + pub fn from_config(config: &AiConfig) -> Result { + let api_key = config.api_key.clone().ok_or(CliError::AiProviderError { + provider: "openai".to_string(), + message: "api_key is required for OpenAI provider".to_string(), + })?; + + Ok(Self { + endpoint: config + .endpoint + .clone() + .unwrap_or_else(|| OPENAI_API_URL.to_string()), + api_key, + model: config + .model + .clone() + .unwrap_or_else(|| default_model(AiProviderType::Openai).to_string()), + timeout_secs: resolve_timeout(config.timeout), + }) + } +} + +impl AiProvider for OpenAiProvider { + fn name(&self) -> &str { + "openai" + } + + fn complete(&self, prompt: &str, context: &str) -> Result { + let body = serde_json::json!({ + "model": self.model, + "messages": [ + { "role": "system", "content": context }, + { "role": "user", "content": prompt } + ], + "temperature": 0.3 + }); + + let client = reqwest::blocking::Client::builder() + .timeout(std::time::Duration::from_secs(self.timeout_secs)) + .build() + .map_err(|e| CliError::AiProviderError { + provider: "openai".to_string(), + message: format!("Failed to build HTTP client: {}", e), + })?; + + let response = client + .post(&self.endpoint) + .header("Authorization", format!("Bearer {}", self.api_key)) + .header("Content-Type", "application/json") + .json(&body) + .send() + .map_err(|e| CliError::AiProviderError { + provider: "openai".to_string(), + message: format!("Request failed: {}", e), + })?; + + if !response.status().is_success() { + let status = response.status(); + let text = response.text().unwrap_or_default(); + return Err(CliError::AiProviderError { + provider: "openai".to_string(), + message: format!("HTTP {} — {}", status, text), + }); + } + + let json: serde_json::Value = response.json().map_err(|e| CliError::AiProviderError { + provider: "openai".to_string(), + message: format!("Failed to parse response: {}", e), + })?; + + json["choices"][0]["message"]["content"] + .as_str() + .map(|s| s.to_string()) + .ok_or_else(|| CliError::AiProviderError { + provider: "openai".to_string(), + message: "No content in response".to_string(), + }) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// AnthropicProvider — Claude API +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Calls the Anthropic Messages API. +pub struct AnthropicProvider { + pub endpoint: String, + pub api_key: String, + pub model: String, + pub timeout_secs: u64, +} + +impl AnthropicProvider { + pub fn from_config(config: &AiConfig) -> Result { + let api_key = config.api_key.clone().ok_or(CliError::AiProviderError { + provider: "anthropic".to_string(), + message: "api_key is required for Anthropic provider".to_string(), + })?; + + Ok(Self { + endpoint: config + .endpoint + .clone() + .unwrap_or_else(|| ANTHROPIC_API_URL.to_string()), + api_key, + model: config + .model + .clone() + .unwrap_or_else(|| default_model(AiProviderType::Anthropic).to_string()), + timeout_secs: resolve_timeout(config.timeout), + }) + } +} + +impl AiProvider for AnthropicProvider { + fn name(&self) -> &str { + "anthropic" + } + + fn complete(&self, prompt: &str, context: &str) -> Result { + let body = serde_json::json!({ + "model": self.model, + "max_tokens": 4096, + "system": context, + "messages": [ + { "role": "user", "content": prompt } + ] + }); + + let client = reqwest::blocking::Client::builder() + .timeout(std::time::Duration::from_secs(self.timeout_secs)) + .build() + .map_err(|e| CliError::AiProviderError { + provider: "anthropic".to_string(), + message: format!("Failed to build HTTP client: {}", e), + })?; + + let response = client + .post(&self.endpoint) + .header("x-api-key", &self.api_key) + .header("anthropic-version", "2023-06-01") + .header("Content-Type", "application/json") + .json(&body) + .send() + .map_err(|e| CliError::AiProviderError { + provider: "anthropic".to_string(), + message: format!("Request failed: {}", e), + })?; + + if !response.status().is_success() { + let status = response.status(); + let text = response.text().unwrap_or_default(); + return Err(CliError::AiProviderError { + provider: "anthropic".to_string(), + message: format!("HTTP {} — {}", status, text), + }); + } + + let json: serde_json::Value = response.json().map_err(|e| CliError::AiProviderError { + provider: "anthropic".to_string(), + message: format!("Failed to parse response: {}", e), + })?; + + json["content"][0]["text"] + .as_str() + .map(|s| s.to_string()) + .ok_or_else(|| CliError::AiProviderError { + provider: "anthropic".to_string(), + message: "No content in response".to_string(), + }) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// OllamaProvider — local Ollama instance +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Calls a local Ollama chat API. No API key required. +pub struct OllamaProvider { + pub endpoint: String, + pub model: String, + pub timeout_secs: u64, +} + +impl OllamaProvider { + pub fn from_config(config: &AiConfig) -> Self { + let endpoint = config + .endpoint + .clone() + .unwrap_or_else(|| OLLAMA_API_URL.to_string()); + + let model = match config.model.clone() { + Some(m) => { + // Verify the configured model is actually available + let available = list_ollama_models(Some(&endpoint)); + if available.is_empty() { + // Ollama unreachable — use the configured model as-is + m + } else if available.iter().any(|a| { + let base = a.split(':').next().unwrap_or(a); + let m_base = m.split(':').next().unwrap_or(&m); + a == &m || base == m_base + }) { + m + } else { + // Configured model not found — auto-detect + eprintln!(" ⚠ Model '{}' not found in Ollama, auto-detecting...", m); + match pick_ollama_model(Some(&endpoint)) { + Some(detected) => { + eprintln!(" Using Ollama model: {}", detected); + detected + } + None => m, // nothing else available, try anyway + } + } + } + None => { + // No model configured — auto-detect + match pick_ollama_model(Some(&endpoint)) { + Some(m) => { + eprintln!(" Using Ollama model: {}", m); + m + } + None => { + let default = default_model(AiProviderType::Ollama).to_string(); + eprintln!(" No models detected, trying default: {}", default); + default + } + } + } + }; + + let timeout_secs = resolve_timeout(config.timeout); + + Self { endpoint, model, timeout_secs } + } +} + +impl AiProvider for OllamaProvider { + fn name(&self) -> &str { + "ollama" + } + + fn complete(&self, prompt: &str, context: &str) -> Result { + let body = serde_json::json!({ + "model": self.model, + "stream": false, + "messages": [ + { "role": "system", "content": context }, + { "role": "user", "content": prompt } + ] + }); + + let client = reqwest::blocking::Client::builder() + .timeout(std::time::Duration::from_secs(self.timeout_secs)) + .build() + .map_err(|e| CliError::AiProviderError { + provider: "ollama".to_string(), + message: format!("Failed to build HTTP client: {}", e), + })?; + + let response = client + .post(&self.endpoint) + .header("Content-Type", "application/json") + .json(&body) + .send() + .map_err(|e| CliError::AiProviderError { + provider: "ollama".to_string(), + message: format!("Request failed (is Ollama running?): {}", e), + })?; + + if !response.status().is_success() { + let status = response.status(); + let text = response.text().unwrap_or_default(); + return Err(CliError::AiProviderError { + provider: "ollama".to_string(), + message: format!("HTTP {} — {}", status, text), + }); + } + + let json: serde_json::Value = response.json().map_err(|e| CliError::AiProviderError { + provider: "ollama".to_string(), + message: format!("Failed to parse response: {}", e), + })?; + + json["message"]["content"] + .as_str() + .map(|s| s.to_string()) + .ok_or_else(|| CliError::AiProviderError { + provider: "ollama".to_string(), + message: "No content in response".to_string(), + }) + } +} + +/// Stream a response from Ollama and print token chunks to stderr as they arrive. +/// Returns the full accumulated response text. +pub fn ollama_complete_streaming( + config: &AiConfig, + prompt: &str, + context: &str, +) -> Result { + let provider = OllamaProvider::from_config(config); + + let body = serde_json::json!({ + "model": provider.model, + "stream": true, + "messages": [ + { "role": "system", "content": context }, + { "role": "user", "content": prompt } + ] + }); + + let client = reqwest::blocking::Client::builder() + .timeout(std::time::Duration::from_secs(provider.timeout_secs)) + .build() + .map_err(|e| CliError::AiProviderError { + provider: "ollama".to_string(), + message: format!("Failed to build HTTP client: {}", e), + })?; + + let response = client + .post(&provider.endpoint) + .header("Content-Type", "application/json") + .json(&body) + .send() + .map_err(|e| CliError::AiProviderError { + provider: "ollama".to_string(), + message: format!("Request failed (is Ollama running?): {}", e), + })?; + + if !response.status().is_success() { + let status = response.status(); + let text = response.text().unwrap_or_default(); + return Err(CliError::AiProviderError { + provider: "ollama".to_string(), + message: format!("HTTP {} — {}", status, text), + }); + } + + let mut content = String::new(); + let mut reader = BufReader::new(response); + let mut line = String::new(); + + loop { + line.clear(); + let bytes = reader + .read_line(&mut line) + .map_err(|e| CliError::AiProviderError { + provider: "ollama".to_string(), + message: format!("Failed to read stream: {}", e), + })?; + if bytes == 0 { + break; + } + + let trimmed = line.trim(); + if trimmed.is_empty() { + continue; + } + + let json: serde_json::Value = serde_json::from_str(trimmed).map_err(|e| { + CliError::AiProviderError { + provider: "ollama".to_string(), + message: format!("Invalid streaming chunk: {}", e), + } + })?; + + if let Some(chunk) = json["message"]["content"].as_str() { + eprint!("{}", chunk); + let _ = std::io::stderr().flush(); + content.push_str(chunk); + } + + if json["done"].as_bool().unwrap_or(false) { + break; + } + } + + Ok(content) +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Provider factory +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Create the appropriate provider from an `AiConfig`. +/// Returns `AiNotConfigured` if AI is disabled. +pub fn create_provider(config: &AiConfig) -> Result, CliError> { + if !config.enabled { + return Err(CliError::AiNotConfigured); + } + + match config.provider { + AiProviderType::Openai | AiProviderType::Custom => { + Ok(Box::new(OpenAiProvider::from_config(config)?)) + } + AiProviderType::Anthropic => Ok(Box::new(AnthropicProvider::from_config(config)?)), + AiProviderType::Ollama => Ok(Box::new(OllamaProvider::from_config(config))), + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Prompt building +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Predefined AI task types that map to `AiConfig.tasks`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AiTask { + Dockerfile, + Compose, + Troubleshoot, + Optimize, +} + +impl AiTask { + pub fn as_str(&self) -> &'static str { + match self { + Self::Dockerfile => "dockerfile", + Self::Compose => "compose", + Self::Troubleshoot => "troubleshoot", + Self::Optimize => "optimize", + } + } +} + +/// Context for building AI prompts. +#[derive(Debug, Clone, Default)] +pub struct PromptContext { + pub project_type: Option, + pub files: Vec, + pub error_log: Option, + pub current_config: Option, +} + +/// System message providing context about the stacker CLI. +const SYSTEM_CONTEXT: &str = "\ +You are an expert DevOps assistant integrated into the `stacker` CLI tool. \ +Stacker helps developers deploy web applications using Docker, docker-compose, \ +Terraform, and Ansible. You provide concise, production-ready configurations. \ +Always use multi-stage builds when appropriate. Prefer Alpine-based images. \ +Include health checks. Follow Docker and security best practices."; + +/// Build a prompt for Dockerfile generation. +pub fn build_dockerfile_prompt(ctx: &PromptContext) -> (String, String) { + let project_type = ctx + .project_type + .map(|t| t.to_string()) + .unwrap_or_else(|| "unknown".to_string()); + + let files_list = if ctx.files.is_empty() { + "No files detected".to_string() + } else { + ctx.files.join(", ") + }; + + let prompt = format!( + "Generate an optimized Dockerfile for a {} project.\n\ + Detected files: {}\n\ + Requirements:\n\ + - Multi-stage build if applicable\n\ + - Alpine base image preferred\n\ + - Non-root user\n\ + - .dockerignore recommendations\n\ + Return only the Dockerfile content.", + project_type, files_list + ); + + (SYSTEM_CONTEXT.to_string(), prompt) +} + +/// Build a prompt for docker-compose generation/improvement. +pub fn build_compose_prompt(ctx: &PromptContext) -> (String, String) { + let project_type = ctx + .project_type + .map(|t| t.to_string()) + .unwrap_or_else(|| "unknown".to_string()); + + let current = ctx + .current_config + .as_deref() + .unwrap_or("No existing compose file"); + + let prompt = format!( + "Generate or improve a docker-compose.yml for a {} project.\n\ + Current config:\n```yaml\n{}\n```\n\ + Requirements:\n\ + - Named volumes for persistence\n\ + - Health checks for services\n\ + - Proper networking\n\ + - Resource limits\n\ + Return only the docker-compose.yml content.", + project_type, current + ); + + (SYSTEM_CONTEXT.to_string(), prompt) +} + +/// Build a prompt for troubleshooting deployment issues. +pub fn build_troubleshoot_prompt(ctx: &PromptContext) -> (String, String) { + let error = ctx + .error_log + .as_deref() + .unwrap_or("No error log provided"); + + let prompt = format!( + "Diagnose and fix the following deployment issue.\n\ + Error log:\n```\n{}\n```\n\ + Provide:\n\ + 1. Root cause analysis\n\ + 2. Step-by-step fix\n\ + 3. Prevention recommendations", + error + ); + + (SYSTEM_CONTEXT.to_string(), prompt) +} + +/// Build a prompt based on task type. +pub fn build_prompt(task: AiTask, ctx: &PromptContext) -> (String, String) { + match task { + AiTask::Dockerfile => build_dockerfile_prompt(ctx), + AiTask::Compose => build_compose_prompt(ctx), + AiTask::Troubleshoot => build_troubleshoot_prompt(ctx), + AiTask::Optimize => build_dockerfile_prompt(ctx), // reuse dockerfile optimization + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + + // ── Mock provider ─────────────────────────────── + + struct MockAiProvider { + response: String, + } + + impl MockAiProvider { + fn with_response(response: &str) -> Self { + Self { + response: response.to_string(), + } + } + } + + impl AiProvider for MockAiProvider { + fn name(&self) -> &str { + "mock" + } + + fn complete(&self, _prompt: &str, _context: &str) -> Result { + Ok(self.response.clone()) + } + } + + // ── Phase 7 tests ─────────────────────────────── + + #[test] + fn test_ai_provider_from_config_openai() { + let config = AiConfig { + enabled: true, + provider: AiProviderType::Openai, + model: Some("gpt-4o".to_string()), + api_key: Some("sk-test-key".to_string()), + endpoint: None, + timeout: 300, + tasks: vec!["dockerfile".to_string()], + }; + + let provider = create_provider(&config).unwrap(); + assert_eq!(provider.name(), "openai"); + } + + #[test] + fn test_ai_provider_from_config_ollama() { + let config = AiConfig { + enabled: true, + provider: AiProviderType::Ollama, + model: None, + api_key: None, + endpoint: Some("http://localhost:11434/api/chat".to_string()), + timeout: 300, + tasks: vec![], + }; + + let provider = create_provider(&config).unwrap(); + assert_eq!(provider.name(), "ollama"); + } + + #[test] + fn test_ai_provider_from_config_anthropic() { + let config = AiConfig { + enabled: true, + provider: AiProviderType::Anthropic, + model: Some("claude-sonnet-4-20250514".to_string()), + api_key: Some("sk-ant-test".to_string()), + endpoint: None, + timeout: 300, + tasks: vec![], + }; + + let provider = create_provider(&config).unwrap(); + assert_eq!(provider.name(), "anthropic"); + } + + #[test] + fn test_mock_ai_complete() { + let provider = MockAiProvider::with_response("Use FROM node:lts-alpine"); + let result = provider.complete("optimize dockerfile", "system context").unwrap(); + assert!(result.contains("node:lts-alpine")); + } + + #[test] + fn test_ai_build_prompt_for_dockerfile() { + let ctx = PromptContext { + project_type: Some(AppType::Node), + files: vec!["package.json".to_string(), "src/index.ts".to_string()], + error_log: None, + current_config: None, + }; + + let (system, prompt) = build_dockerfile_prompt(&ctx); + assert!(system.contains("DevOps")); + assert!(prompt.contains("node")); + assert!(prompt.contains("Dockerfile")); + assert!(prompt.contains("package.json")); + } + + #[test] + fn test_ai_build_prompt_for_troubleshoot() { + let ctx = PromptContext { + project_type: None, + files: vec![], + error_log: Some("connection refused on port 5432".to_string()), + current_config: None, + }; + + let (_, prompt) = build_troubleshoot_prompt(&ctx); + assert!(prompt.contains("connection refused")); + assert!(prompt.contains("Diagnose")); + } + + #[test] + fn test_ai_not_configured_returns_error() { + let config = AiConfig { + enabled: false, + ..Default::default() + }; + + let result = create_provider(&config); + assert!(result.is_err()); + let err = result.err().unwrap(); + match err { + CliError::AiNotConfigured => {} // expected + other => panic!("Expected AiNotConfigured, got: {:?}", other), + } + } + + // ── Additional tests ──────────────────────────── + + #[test] + fn test_openai_requires_api_key() { + let config = AiConfig { + enabled: true, + provider: AiProviderType::Openai, + api_key: None, + ..Default::default() + }; + + let result = create_provider(&config); + assert!(result.is_err()); + } + + #[test] + fn test_anthropic_requires_api_key() { + let config = AiConfig { + enabled: true, + provider: AiProviderType::Anthropic, + api_key: None, + ..Default::default() + }; + + let result = create_provider(&config); + assert!(result.is_err()); + } + + #[test] + fn test_ollama_no_api_key_needed() { + let config = AiConfig { + enabled: true, + provider: AiProviderType::Ollama, + api_key: None, + ..Default::default() + }; + + let provider = create_provider(&config).unwrap(); + assert_eq!(provider.name(), "ollama"); + } + + #[test] + fn test_custom_provider_uses_openai_compat() { + let config = AiConfig { + enabled: true, + provider: AiProviderType::Custom, + api_key: Some("custom-key".to_string()), + endpoint: Some("https://my-llm.local/v1/chat/completions".to_string()), + model: Some("my-model".to_string()), + timeout: 300, + tasks: vec![], + }; + + let provider = create_provider(&config).unwrap(); + // Custom uses OpenAI-compatible protocol + assert_eq!(provider.name(), "openai"); + } + + #[test] + fn test_default_models() { + assert_eq!(default_model(AiProviderType::Openai), "gpt-4o"); + assert_eq!(default_model(AiProviderType::Ollama), "llama3"); + assert!(default_model(AiProviderType::Anthropic).contains("claude")); + } + + #[test] + fn test_build_compose_prompt() { + let ctx = PromptContext { + project_type: Some(AppType::Python), + files: vec![], + error_log: None, + current_config: Some("version: '3'\nservices:\n web:\n image: python:3.11".to_string()), + }; + + let (_, prompt) = build_compose_prompt(&ctx); + assert!(prompt.contains("python")); + assert!(prompt.contains("docker-compose.yml")); + assert!(prompt.contains("python:3.11")); + } + + #[test] + fn test_build_prompt_dispatches_correctly() { + let ctx = PromptContext { + project_type: Some(AppType::Rust), + files: vec!["Cargo.toml".to_string()], + ..Default::default() + }; + + let (_, dockerfile_prompt) = build_prompt(AiTask::Dockerfile, &ctx); + assert!(dockerfile_prompt.contains("rust")); + + let (_, compose_prompt) = build_prompt(AiTask::Compose, &ctx); + assert!(compose_prompt.contains("docker-compose")); + + let troubleshoot_ctx = PromptContext { + error_log: Some("exit code 1".to_string()), + ..Default::default() + }; + let (_, troubleshoot_prompt) = build_prompt(AiTask::Troubleshoot, &troubleshoot_ctx); + assert!(troubleshoot_prompt.contains("exit code 1")); + } + + #[test] + fn test_ai_task_as_str() { + assert_eq!(AiTask::Dockerfile.as_str(), "dockerfile"); + assert_eq!(AiTask::Compose.as_str(), "compose"); + assert_eq!(AiTask::Troubleshoot.as_str(), "troubleshoot"); + assert_eq!(AiTask::Optimize.as_str(), "optimize"); + } + + #[test] + fn test_openai_provider_from_config_defaults() { + let config = AiConfig { + enabled: true, + provider: AiProviderType::Openai, + api_key: Some("sk-test".to_string()), + model: None, + endpoint: None, + timeout: 300, + tasks: vec![], + }; + + let provider = OpenAiProvider::from_config(&config).unwrap(); + assert_eq!(provider.endpoint, OPENAI_API_URL); + assert_eq!(provider.model, "gpt-4o"); + } + + #[test] + fn test_ollama_provider_from_config_defaults() { + let config = AiConfig { + enabled: true, + provider: AiProviderType::Ollama, + ..Default::default() + }; + + let provider = OllamaProvider::from_config(&config); + assert_eq!(provider.endpoint, OLLAMA_API_URL); + // Model is either auto-detected from running Ollama or falls back to default + assert!(!provider.model.is_empty(), "model must not be empty"); + } + + #[test] + fn test_ollama_provider_from_config_explicit_model() { + // Use unreachable endpoint so list_ollama_models returns empty, + // meaning the configured model is used as-is (no validation possible). + let config = AiConfig { + enabled: true, + provider: AiProviderType::Ollama, + model: Some("custom-model".to_string()), + endpoint: Some("http://127.0.0.1:1/api/chat".to_string()), + ..Default::default() + }; + + let provider = OllamaProvider::from_config(&config); + assert_eq!(provider.model, "custom-model"); + } + + #[test] + fn test_ollama_provider_autodetects_when_model_missing() { + // With Ollama running and a model that doesn't exist, auto-detection + // should kick in and pick an available model. + let config = AiConfig { + enabled: true, + provider: AiProviderType::Ollama, + model: Some("nonexistent-model-xyz".to_string()), + ..Default::default() + }; + + let provider = OllamaProvider::from_config(&config); + // If Ollama is running, model is auto-detected; if not, original is kept + assert!(!provider.model.is_empty()); + } + + #[test] + fn test_prompt_context_default() { + let ctx = PromptContext::default(); + assert!(ctx.project_type.is_none()); + assert!(ctx.files.is_empty()); + assert!(ctx.error_log.is_none()); + assert!(ctx.current_config.is_none()); + } + + // ── Timeout resolution tests ──────────────── + + #[test] + fn test_resolve_timeout_uses_config_value() { + // Clean env to avoid interference + std::env::remove_var("STACKER_AI_TIMEOUT"); + assert_eq!(resolve_timeout(600), 600); + assert_eq!(resolve_timeout(30), 30); + } + + #[test] + fn test_resolve_timeout_default_fallback() { + std::env::remove_var("STACKER_AI_TIMEOUT"); + // 0 means "use default" + assert_eq!(resolve_timeout(0), DEFAULT_AI_TIMEOUT_SECS); + } + + #[test] + fn test_resolve_timeout_env_overrides_config() { + std::env::set_var("STACKER_AI_TIMEOUT", "900"); + assert_eq!(resolve_timeout(300), 900); + std::env::remove_var("STACKER_AI_TIMEOUT"); + } + + #[test] + fn test_resolve_timeout_env_invalid_ignored() { + std::env::set_var("STACKER_AI_TIMEOUT", "not-a-number"); + assert_eq!(resolve_timeout(120), 120); + std::env::remove_var("STACKER_AI_TIMEOUT"); + } + + #[test] + fn test_provider_timeout_from_config() { + let config = AiConfig { + enabled: true, + provider: AiProviderType::Ollama, + timeout: 600, + ..Default::default() + }; + let provider = OllamaProvider::from_config(&config); + assert_eq!(provider.timeout_secs, 600); + } + + #[test] + fn test_openai_provider_timeout_from_config() { + std::env::remove_var("STACKER_AI_TIMEOUT"); + let config = AiConfig { + enabled: true, + provider: AiProviderType::Openai, + api_key: Some("sk-test".to_string()), + timeout: 120, + ..Default::default() + }; + let provider = OpenAiProvider::from_config(&config).unwrap(); + assert_eq!(provider.timeout_secs, 120); + } +} diff --git a/src/cli/ai_scanner.rs b/src/cli/ai_scanner.rs new file mode 100644 index 00000000..b49228db --- /dev/null +++ b/src/cli/ai_scanner.rs @@ -0,0 +1,709 @@ +use std::collections::HashMap; +use std::path::Path; + +use crate::cli::ai_client::AiProvider; +use crate::cli::detector::{detect_project, FileSystem, ProjectDetection, RealFileSystem}; +use crate::cli::error::CliError; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ProjectScanResult — rich project context for AI prompt +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Rich project context gathered by scanning files, used to build the AI prompt +/// for stacker.yml generation. +#[derive(Debug, Clone, Default)] +pub struct ProjectScanResult { + /// Base detection (app type, has_dockerfile, etc.) + pub detection: ProjectDetection, + + /// All filenames found at project root + pub root_files: Vec, + + /// Partial contents of key config files (package.json, requirements.txt, etc.) + /// Key = filename, Value = content (truncated to MAX_FILE_CONTENT_LEN) + pub file_contents: HashMap, + + /// Inferred project name (from directory name) + pub project_name: String, + + /// Existing Dockerfile content, if found + pub existing_dockerfile: Option, + + /// Existing docker-compose content, if found + pub existing_compose: Option, + + /// Existing .env keys (values redacted for safety) + pub env_keys: Vec, +} + +/// Max bytes to read from any single file for AI context. +const MAX_FILE_CONTENT_LEN: usize = 4096; + +/// Files worth reading for richer AI context, mapped by app type. +const CONTEXT_FILES: &[&str] = &[ + "package.json", + "requirements.txt", + "Pipfile", + "pyproject.toml", + "Cargo.toml", + "go.mod", + "composer.json", + "Gemfile", + "Makefile", + "README.md", + "Dockerfile", + "docker-compose.yml", + "docker-compose.yaml", + "compose.yml", + "compose.yaml", + ".env", + ".env.example", + "tsconfig.json", + "next.config.js", + "next.config.mjs", + "nuxt.config.ts", + "vite.config.ts", + "vite.config.js", + "webpack.config.js", + "angular.json", + "manage.py", + "setup.py", + "setup.cfg", + "pom.xml", + "build.gradle", +]; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// scan_project — deep project scan for AI context +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Scan a project directory gathering rich context for AI-powered config generation. +/// +/// This goes beyond `detect_project()` by also reading key config files +/// so the AI can make informed decisions about services, ports, env vars, etc. +pub fn scan_project(project_dir: &Path, fs: &dyn FileSystem) -> ProjectScanResult { + let detection = detect_project(project_dir, fs); + + let root_files = fs.list_dir(project_dir).unwrap_or_default(); + + let project_name = project_dir + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("my-app") + .to_string(); + + let mut file_contents = HashMap::new(); + let mut existing_dockerfile = None; + let mut existing_compose = None; + let mut env_keys = Vec::new(); + + for filename in &root_files { + // Only read files we recognise as valuable context + if !CONTEXT_FILES.iter().any(|cf| cf == filename) { + continue; + } + + let file_path = project_dir.join(filename); + let content = match std::fs::read_to_string(&file_path) { + Ok(c) => c, + Err(_) => continue, + }; + + // Truncate large files + let truncated = if content.len() > MAX_FILE_CONTENT_LEN { + format!("{}... (truncated)", &content[..MAX_FILE_CONTENT_LEN]) + } else { + content.clone() + }; + + // Capture special files + if filename == "Dockerfile" { + existing_dockerfile = Some(truncated.clone()); + } + + if filename == "docker-compose.yml" + || filename == "docker-compose.yaml" + || filename == "compose.yml" + || filename == "compose.yaml" + { + existing_compose = Some(truncated.clone()); + } + + // For .env files, extract keys only (redact values for security) + if filename == ".env" || filename == ".env.example" { + for line in content.lines() { + let trimmed = line.trim(); + if trimmed.is_empty() || trimmed.starts_with('#') { + continue; + } + if let Some(key) = trimmed.split('=').next() { + env_keys.push(key.trim().to_string()); + } + } + // Store only the key list, not values + file_contents.insert( + filename.clone(), + format!("# Environment keys: {}", env_keys.join(", ")), + ); + continue; + } + + file_contents.insert(filename.clone(), truncated); + } + + ProjectScanResult { + detection, + root_files, + file_contents, + project_name, + existing_dockerfile, + existing_compose, + env_keys, + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// AI prompt building for stacker.yml generation +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// System prompt that instructs the AI how to generate stacker.yml. +const SYSTEM_PROMPT: &str = "\ +You are an expert DevOps engineer integrated into the `stacker` CLI tool. \ +Your job is to generate a complete, production-ready `stacker.yml` configuration \ +based on the project files and context provided. + +The `stacker.yml` schema supports these top-level keys: +- name: (string, required) Project name +- version: (string) Version label +- app: Application source config + - type: static|node|python|rust|go|php|custom + - path: Source directory (default '.') + - dockerfile: Path to custom Dockerfile + - image: Pre-built Docker image + - build: { context: '.', args: { KEY: VALUE } } +- services: Array of sidecar containers + - name, image, ports[], environment{}, volumes[], depends_on[] +- proxy: Reverse proxy config + - type: nginx|nginx-proxy-manager|traefik|none + - auto_detect: bool + - domains: [{ domain, ssl: auto|manual|off, upstream }] +- deploy: { target: local|cloud|server } +- monitoring: { status_panel: bool, healthcheck: { endpoint, interval }, metrics: { enabled, telegraf } } +- hooks: { pre_build, post_deploy, on_failure } (paths to scripts) +- env_file: Path to .env file +- env: { KEY: VALUE } inline environment variables + +Rules: +1. Output ONLY valid YAML — no markdown fences, no explanations, no comments except brief inline ones. +2. Use ${VAR_NAME} syntax for secrets and sensitive values (DB passwords, API keys). +3. Include appropriate services (databases, caches, queues) based on detected dependencies. +4. Set proper port mappings avoiding conflicts. +5. Add volumes for data persistence. +6. Use depends_on for service ordering. +7. Add healthcheck and monitoring when appropriate. +8. If a Dockerfile already exists, set app.type to 'custom' and reference it via app.dockerfile. +9. If a docker-compose already exists, set deploy.compose_file to reference it. +10. Keep the configuration practical and deployable — don't add services that aren't needed."; + +/// Expose the system prompt used for AI-based stacker.yml generation. +pub fn generation_system_prompt() -> &'static str { + SYSTEM_PROMPT +} + +/// Build the user prompt from the scan result. +pub fn build_generation_prompt(scan: &ProjectScanResult) -> String { + let mut sections = Vec::new(); + + // Project overview + sections.push(format!( + "Project: {}\nDetected type: {}\nRoot files: {}", + scan.project_name, + scan.detection.app_type, + scan.root_files.join(", ") + )); + + // Existing infrastructure + if scan.detection.has_dockerfile { + sections.push("Has existing Dockerfile: yes".to_string()); + } + if scan.detection.has_compose { + sections.push("Has existing docker-compose: yes".to_string()); + } + if scan.detection.has_env_file { + sections.push(format!( + "Has .env file with keys: {}", + scan.env_keys.join(", ") + )); + } + + // File contents for context + for (filename, content) in &scan.file_contents { + sections.push(format!("--- {} ---\n{}", filename, content)); + } + + // Existing Dockerfile content + if let Some(ref df) = scan.existing_dockerfile { + sections.push(format!("--- Existing Dockerfile ---\n{}", df)); + } + + // Existing compose content + if let Some(ref dc) = scan.existing_compose { + sections.push(format!("--- Existing docker-compose ---\n{}", dc)); + } + + sections.push( + "Generate a complete stacker.yml for this project. Output ONLY valid YAML.".to_string(), + ); + + sections.join("\n\n") +} + +/// Build the `(system_prompt, user_prompt)` pair for stacker.yml generation. +pub fn build_generation_request(project_dir: &Path) -> (String, String) { + let fs = RealFileSystem; + let scan = scan_project(project_dir, &fs); + ( + generation_system_prompt().to_string(), + build_generation_prompt(&scan), + ) +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// generate_config_with_ai — core AI generation function +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Scan the project, send context to AI, and return generated stacker.yml content. +/// +/// The returned string is raw YAML ready to be written to disk. +/// The caller is responsible for writing the file and validating it. +pub fn generate_config_with_ai( + project_dir: &Path, + provider: &dyn AiProvider, +) -> Result { + let fs = RealFileSystem; + generate_config_with_ai_impl(project_dir, provider, &fs) +} + +/// Inner implementation taking a `FileSystem` for testability. +pub fn generate_config_with_ai_impl( + project_dir: &Path, + provider: &dyn AiProvider, + fs: &dyn FileSystem, +) -> Result { + let scan = scan_project(project_dir, fs); + let user_prompt = build_generation_prompt(&scan); + let raw_response = provider.complete(&user_prompt, SYSTEM_PROMPT)?; + + // Strip markdown fences if the model wrapped the YAML in ```yaml ... ``` + let yaml = strip_code_fences(&raw_response); + + // Validate that it's parseable YAML (but don't require it to be a valid StackerConfig + // yet — the caller will do from_str() and report detailed errors) + serde_yaml::from_str::(&yaml).map_err(|e| { + CliError::AiProviderError { + provider: provider.name().to_string(), + message: format!( + "AI generated invalid YAML: {}. Raw response:\n{}", + e, raw_response + ), + } + })?; + + Ok(yaml) +} + +/// Strip markdown code fences from AI response. +/// Handles ```yaml\n...\n```, ```yml\n...\n```, and ```\n...\n```. +pub fn strip_code_fences(text: &str) -> String { + let trimmed = text.trim(); + + // Check for opening fence + let without_open = if trimmed.starts_with("```yaml") + || trimmed.starts_with("```yml") + { + // Remove opening fence line + trimmed + .splitn(2, '\n') + .nth(1) + .unwrap_or(trimmed) + } else if trimmed.starts_with("```") { + trimmed + .splitn(2, '\n') + .nth(1) + .unwrap_or(trimmed) + } else { + return trimmed.to_string(); + }; + + // Remove closing fence + if without_open.trim_end().ends_with("```") { + let end = without_open.rfind("```").unwrap_or(without_open.len()); + without_open[..end].trim_end().to_string() + } else { + without_open.to_string() + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::config_parser::{AppType, StackerConfig}; + use crate::cli::detector::FileSystem; + + // ── Mock filesystem ───────────────────────────── + + struct MockFs { + files: Vec, + } + + impl MockFs { + fn with_files(files: &[&str]) -> Self { + Self { + files: files.iter().map(|s| s.to_string()).collect(), + } + } + } + + impl FileSystem for MockFs { + fn exists(&self, path: &Path) -> bool { + let name = path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or_default(); + self.files.contains(&name.to_string()) + } + + fn list_dir(&self, _path: &Path) -> Result, std::io::Error> { + Ok(self.files.clone()) + } + } + + // ── Mock AI provider ──────────────────────────── + + struct MockAi { + response: String, + } + + impl MockAi { + fn with_yaml(yaml: &str) -> Self { + Self { + response: yaml.to_string(), + } + } + + fn with_fenced_yaml(yaml: &str) -> Self { + Self { + response: format!("```yaml\n{}\n```", yaml), + } + } + } + + impl AiProvider for MockAi { + fn name(&self) -> &str { + "mock" + } + + fn complete(&self, _prompt: &str, _context: &str) -> Result { + Ok(self.response.clone()) + } + } + + struct FailingAi; + + impl AiProvider for FailingAi { + fn name(&self) -> &str { + "failing" + } + + fn complete(&self, _prompt: &str, _context: &str) -> Result { + Err(CliError::AiProviderError { + provider: "failing".to_string(), + message: "connection refused".to_string(), + }) + } + } + + // ── strip_code_fences ─────────────────────────── + + #[test] + fn test_strip_yaml_fences() { + let input = "```yaml\nname: test\napp:\n type: node\n```"; + let result = strip_code_fences(input); + assert_eq!(result, "name: test\napp:\n type: node"); + } + + #[test] + fn test_strip_yml_fences() { + let input = "```yml\nname: test\n```"; + let result = strip_code_fences(input); + assert_eq!(result, "name: test"); + } + + #[test] + fn test_strip_generic_fences() { + let input = "```\nname: test\n```"; + let result = strip_code_fences(input); + assert_eq!(result, "name: test"); + } + + #[test] + fn test_strip_no_fences() { + let input = "name: test\napp:\n type: node"; + let result = strip_code_fences(input); + assert_eq!(result, input); + } + + // ── scan_project ──────────────────────────────── + + #[test] + fn test_scan_project_basic() { + let fs = MockFs::with_files(&["package.json", "src", "README.md"]); + let result = scan_project(Path::new("/test-project"), &fs); + + assert_eq!(result.detection.app_type, AppType::Node); + assert_eq!(result.root_files.len(), 3); + // Note: file_contents won't have actual data since MockFs doesn't provide file I/O, + // but root_files and detection should work correctly. + } + + #[test] + fn test_scan_project_empty() { + let fs = MockFs::with_files(&[]); + let result = scan_project(Path::new("/empty-project"), &fs); + + assert_eq!(result.detection.app_type, AppType::Custom); + assert!(result.root_files.is_empty()); + assert!(result.file_contents.is_empty()); + } + + // ── build_generation_prompt ───────────────────── + + #[test] + fn test_prompt_includes_project_name() { + let scan = ProjectScanResult { + project_name: "my-web-app".to_string(), + detection: ProjectDetection { + app_type: AppType::Node, + ..Default::default() + }, + root_files: vec!["package.json".to_string(), "src".to_string()], + ..Default::default() + }; + + let prompt = build_generation_prompt(&scan); + assert!(prompt.contains("my-web-app")); + assert!(prompt.contains("node")); + assert!(prompt.contains("package.json")); + assert!(prompt.contains("Generate a complete stacker.yml")); + } + + #[test] + fn test_prompt_includes_env_keys() { + let scan = ProjectScanResult { + project_name: "app".to_string(), + detection: ProjectDetection { + has_env_file: true, + ..Default::default() + }, + env_keys: vec!["DATABASE_URL".to_string(), "SECRET_KEY".to_string()], + ..Default::default() + }; + + let prompt = build_generation_prompt(&scan); + assert!(prompt.contains("DATABASE_URL")); + assert!(prompt.contains("SECRET_KEY")); + } + + #[test] + fn test_prompt_includes_existing_dockerfile() { + let scan = ProjectScanResult { + project_name: "app".to_string(), + detection: ProjectDetection { + has_dockerfile: true, + ..Default::default() + }, + existing_dockerfile: Some("FROM node:20\nCOPY . .\nRUN npm install".to_string()), + ..Default::default() + }; + + let prompt = build_generation_prompt(&scan); + assert!(prompt.contains("Existing Dockerfile")); + assert!(prompt.contains("FROM node:20")); + } + + #[test] + fn test_prompt_includes_existing_compose() { + let scan = ProjectScanResult { + project_name: "app".to_string(), + detection: ProjectDetection { + has_compose: true, + ..Default::default() + }, + existing_compose: Some("version: '3'\nservices:\n web:\n build: .".to_string()), + ..Default::default() + }; + + let prompt = build_generation_prompt(&scan); + assert!(prompt.contains("Existing docker-compose")); + assert!(prompt.contains("services:")); + } + + #[test] + fn test_prompt_includes_file_contents() { + let mut file_contents = HashMap::new(); + file_contents.insert( + "package.json".to_string(), + r#"{"name":"test","dependencies":{"express":"^4.18"}}"#.to_string(), + ); + + let scan = ProjectScanResult { + project_name: "app".to_string(), + file_contents, + ..Default::default() + }; + + let prompt = build_generation_prompt(&scan); + assert!(prompt.contains("package.json")); + assert!(prompt.contains("express")); + } + + // ── generate_config_with_ai_impl ──────────────── + + #[test] + fn test_generate_with_ai_valid_yaml() { + let yaml = "name: ai-app\napp:\n type: node\n path: .\ndeploy:\n target: local\n"; + let provider = MockAi::with_yaml(yaml); + let fs = MockFs::with_files(&["package.json"]); + + let result = generate_config_with_ai_impl(Path::new("/test"), &provider, &fs); + assert!(result.is_ok()); + + let output = result.unwrap(); + assert!(output.contains("ai-app")); + assert!(output.contains("node")); + + // Should be parseable as StackerConfig + let config = StackerConfig::from_str(&output).unwrap(); + assert_eq!(config.name, "ai-app"); + assert_eq!(config.app.app_type, AppType::Node); + } + + #[test] + fn test_generate_with_ai_strips_fences() { + let yaml = "name: fenced-app\napp:\n type: python\n path: .\n"; + let provider = MockAi::with_fenced_yaml(yaml); + let fs = MockFs::with_files(&["requirements.txt"]); + + let result = generate_config_with_ai_impl(Path::new("/test"), &provider, &fs); + assert!(result.is_ok()); + + let output = result.unwrap(); + assert!(!output.contains("```")); + assert!(output.contains("fenced-app")); + } + + #[test] + fn test_generate_with_ai_invalid_yaml_errors() { + let provider = MockAi::with_yaml("not: valid: yaml: [broken"); + let fs = MockFs::with_files(&["index.html"]); + + let result = generate_config_with_ai_impl(Path::new("/test"), &provider, &fs); + assert!(result.is_err()); + + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("invalid YAML")); + } + + #[test] + fn test_generate_with_ai_provider_error() { + let provider = FailingAi; + let fs = MockFs::with_files(&["package.json"]); + + let result = generate_config_with_ai_impl(Path::new("/test"), &provider, &fs); + assert!(result.is_err()); + + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("connection refused")); + } + + #[test] + fn test_generate_with_ai_full_config() { + let yaml = r#"name: full-ai-app +version: "1.0" +app: + type: node + path: . + build: + context: . + args: + NODE_ENV: production +services: + - name: postgres + image: postgres:16 + ports: + - "5432:5432" + environment: + POSTGRES_DB: mydb + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - pgdata:/var/lib/postgresql/data + - name: redis + image: redis:7-alpine + ports: + - "6379:6379" +proxy: + type: nginx + domains: + - domain: app.localhost + ssl: "off" + upstream: app:3000 +deploy: + target: local +monitoring: + status_panel: true + healthcheck: + endpoint: /health + interval: 30s +env: + NODE_ENV: production +"#; + let provider = MockAi::with_yaml(yaml); + let fs = MockFs::with_files(&["package.json", "tsconfig.json"]); + + let result = generate_config_with_ai_impl(Path::new("/test"), &provider, &fs); + assert!(result.is_ok()); + + let output = result.unwrap(); + + // Verify it parses as a complete StackerConfig + // Note: ${DB_PASSWORD} will fail env resolution, so we set it + std::env::set_var("DB_PASSWORD", "test123"); + let config = StackerConfig::from_str(&output).unwrap(); + std::env::remove_var("DB_PASSWORD"); + + assert_eq!(config.name, "full-ai-app"); + assert_eq!(config.app.app_type, AppType::Node); + assert_eq!(config.services.len(), 2); + assert_eq!(config.services[0].name, "postgres"); + assert_eq!(config.services[1].name, "redis"); + assert_eq!(config.proxy.proxy_type, crate::cli::config_parser::ProxyType::Nginx); + assert!(config.monitoring.status_panel); + } + + // ── System prompt content ─────────────────────── + + #[test] + fn test_system_prompt_covers_schema() { + assert!(SYSTEM_PROMPT.contains("stacker.yml")); + assert!(SYSTEM_PROMPT.contains("services")); + assert!(SYSTEM_PROMPT.contains("proxy")); + assert!(SYSTEM_PROMPT.contains("deploy")); + assert!(SYSTEM_PROMPT.contains("monitoring")); + assert!(SYSTEM_PROMPT.contains("${VAR_NAME}")); + assert!(SYSTEM_PROMPT.contains("YAML")); + } +} diff --git a/src/cli/config_parser.rs b/src/cli/config_parser.rs new file mode 100644 index 00000000..ed061d74 --- /dev/null +++ b/src/cli/config_parser.rs @@ -0,0 +1,1598 @@ +use std::collections::HashMap; +use std::fmt; +use std::path::{Path, PathBuf}; + +use serde::{Deserialize, Deserializer, Serialize}; +use serde_valid::Validate; + +use crate::cli::error::{CliError, Severity, ValidationIssue}; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// AppType — discoverable project types +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum AppType { + Static, + Node, + Python, + Rust, + Go, + Php, + Custom, +} + +impl fmt::Display for AppType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Static => write!(f, "static"), + Self::Node => write!(f, "node"), + Self::Python => write!(f, "python"), + Self::Rust => write!(f, "rust"), + Self::Go => write!(f, "go"), + Self::Php => write!(f, "php"), + Self::Custom => write!(f, "custom"), + } + } +} + +impl Default for AppType { + fn default() -> Self { + Self::Static + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// DeployTarget — where to deploy +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum DeployTarget { + Local, + Cloud, + Server, +} + +impl fmt::Display for DeployTarget { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Local => write!(f, "local"), + Self::Cloud => write!(f, "cloud"), + Self::Server => write!(f, "server"), + } + } +} + +impl Default for DeployTarget { + fn default() -> Self { + Self::Local + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ProxyType — reverse proxy flavors +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum ProxyType { + Nginx, + NginxProxyManager, + Traefik, + None, +} + +impl fmt::Display for ProxyType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Nginx => write!(f, "nginx"), + Self::NginxProxyManager => write!(f, "nginx-proxy-manager"), + Self::Traefik => write!(f, "traefik"), + Self::None => write!(f, "none"), + } + } +} + +impl Default for ProxyType { + fn default() -> Self { + Self::None + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// SslMode — certificate handling +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum SslMode { + Auto, + Manual, + Off, +} + +impl Default for SslMode { + fn default() -> Self { + Self::Off + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// AiProviderType — supported LLM providers +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum AiProviderType { + Openai, + Anthropic, + Ollama, + Custom, +} + +impl fmt::Display for AiProviderType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Openai => write!(f, "openai"), + Self::Anthropic => write!(f, "anthropic"), + Self::Ollama => write!(f, "ollama"), + Self::Custom => write!(f, "custom"), + } + } +} + +impl Default for AiProviderType { + fn default() -> Self { + Self::Openai + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// CloudProvider — supported cloud infrastructure providers +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum CloudProvider { + Hetzner, + Digitalocean, + Aws, + Linode, + Vultr, +} + +/// Cloud orchestration mode. +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)] +#[serde(rename_all = "lowercase")] +pub enum CloudOrchestrator { + #[default] + Local, + Remote, +} + +impl fmt::Display for CloudProvider { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Hetzner => write!(f, "hetzner"), + Self::Digitalocean => write!(f, "digitalocean"), + Self::Aws => write!(f, "aws"), + Self::Linode => write!(f, "linode"), + Self::Vultr => write!(f, "vultr"), + } + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Configuration structs — nested sections of stacker.yml +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Application source configuration. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct AppSource { + #[serde(rename = "type", default)] + pub app_type: AppType, + + #[serde(default = "default_app_path")] + pub path: PathBuf, + + #[serde(default)] + pub dockerfile: Option, + + #[serde(default)] + pub image: Option, + + #[serde(default)] + pub build: Option, + + /// Explicit port mappings (e.g. `"8080:80"`). When empty the CLI + /// derives a default from `app_type`. + #[serde(default)] + pub ports: Vec, + + /// Volume mounts (e.g. `"./data:/app/data"`). + #[serde(default)] + pub volumes: Vec, + + /// Per-app environment variables. Merged with the top-level `env:` + /// section (app-level wins on conflict). + #[serde(default)] + pub environment: HashMap, +} + +fn default_app_path() -> PathBuf { + PathBuf::from(".") +} + +/// Docker build configuration. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct BuildConfig { + #[serde(default = "default_build_context")] + pub context: String, + + #[serde(default)] + pub args: HashMap, +} + +fn default_build_context() -> String { + ".".to_string() +} + +/// Additional container service alongside the app. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServiceDefinition { + pub name: String, + pub image: String, + + #[serde(default)] + pub ports: Vec, + + #[serde(default)] + pub environment: HashMap, + + #[serde(default)] + pub volumes: Vec, + + #[serde(default)] + pub depends_on: Vec, +} + +fn deserialize_services<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let value = serde_yaml::Value::deserialize(deserializer)?; + + match value { + serde_yaml::Value::Null => Ok(Vec::new()), + serde_yaml::Value::Sequence(_) => serde_yaml::from_value(value) + .map_err(serde::de::Error::custom), + serde_yaml::Value::Mapping(map) => { + let mut services = Vec::new(); + + for (key, service_value) in map { + let service_key = key + .as_str() + .ok_or_else(|| serde::de::Error::custom("services map key must be a string"))? + .to_string(); + + let mut service_map = match service_value { + serde_yaml::Value::Mapping(m) => m, + _ => { + return Err(serde::de::Error::custom( + "each services map item must be an object", + )); + } + }; + + let has_name = service_map.keys().any(|k| k.as_str() == Some("name")); + if !has_name { + service_map.insert( + serde_yaml::Value::String("name".to_string()), + serde_yaml::Value::String(service_key), + ); + } + + let service: ServiceDefinition = + serde_yaml::from_value(serde_yaml::Value::Mapping(service_map)) + .map_err(serde::de::Error::custom)?; + services.push(service); + } + + Ok(services) + } + _ => Err(serde::de::Error::custom( + "services must be a sequence or map", + )), + } +} + +/// Proxy/ingress configuration. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct ProxyConfig { + #[serde(rename = "type", default)] + pub proxy_type: ProxyType, + + #[serde(default = "default_auto_detect")] + pub auto_detect: bool, + + #[serde(default)] + pub domains: Vec, + + #[serde(default)] + pub config: Option, +} + +fn default_auto_detect() -> bool { + true +} + +/// Per-domain routing and SSL settings. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DomainConfig { + pub domain: String, + + #[serde(default)] + pub ssl: SslMode, + + pub upstream: String, +} + +/// Deployment target configuration. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct DeployConfig { + #[serde(default)] + pub target: DeployTarget, + + #[serde(default)] + pub compose_file: Option, + + #[serde(default)] + pub cloud: Option, + + #[serde(default)] + pub server: Option, +} + +/// Cloud provider settings for cloud deployments. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CloudConfig { + pub provider: CloudProvider, + + #[serde(default)] + pub orchestrator: CloudOrchestrator, + + #[serde(default)] + pub region: Option, + + #[serde(default)] + pub size: Option, + + #[serde(default)] + pub install_image: Option, + + #[serde(default)] + pub remote_payload_file: Option, + + #[serde(default)] + pub ssh_key: Option, + + /// Name of saved cloud credential on the Stacker server. + /// Used with `stacker deploy --key devops` or `deploy.cloud.key: devops` in stacker.yml. + /// When set, the CLI looks up saved credentials by provider instead of requiring env vars. + #[serde(default)] + pub key: Option, + + /// Name of a saved server on the Stacker server. + /// Used with `stacker deploy --server bastion` or `deploy.cloud.server: bastion` in stacker.yml. + /// When set, the CLI passes the server_id to the deploy form so it is reused. + #[serde(default)] + pub server: Option, +} + +/// Remote server settings for server deployments. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServerConfig { + pub host: String, + + #[serde(default = "default_ssh_user")] + pub user: String, + + #[serde(default)] + pub ssh_key: Option, + + #[serde(default = "default_ssh_port")] + pub port: u16, +} + +fn default_ssh_user() -> String { + "root".to_string() +} + +fn default_ssh_port() -> u16 { + 22 +} + +/// Default AI request timeout in seconds. +fn default_ai_timeout() -> u64 { + 300 +} + +/// AI/LLM assistant configuration. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct AiConfig { + #[serde(default)] + pub enabled: bool, + + #[serde(default)] + pub provider: AiProviderType, + + #[serde(default)] + pub model: Option, + + #[serde(default)] + pub api_key: Option, + + #[serde(default)] + pub endpoint: Option, + + /// Request timeout in seconds. Default: 300 (5 minutes). + /// Can be overridden via `STACKER_AI_TIMEOUT` env var. + #[serde(default = "default_ai_timeout")] + pub timeout: u64, + + #[serde(default)] + pub tasks: Vec, +} + +/// Monitoring and health check configuration. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct MonitoringConfig { + #[serde(default)] + pub status_panel: bool, + + #[serde(default)] + pub healthcheck: Option, + + #[serde(default)] + pub metrics: Option, +} + +/// Healthcheck settings. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HealthcheckConfig { + #[serde(default = "default_health_endpoint")] + pub endpoint: String, + + #[serde(default = "default_health_interval")] + pub interval: String, +} + +fn default_health_endpoint() -> String { + "/health".to_string() +} + +fn default_health_interval() -> String { + "30s".to_string() +} + +/// Metrics collection settings. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct MetricsConfig { + #[serde(default)] + pub enabled: bool, + + #[serde(default)] + pub telegraf: bool, +} + +/// Lifecycle hook commands. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct HookConfig { + #[serde(default)] + pub pre_build: Option, + + #[serde(default)] + pub post_deploy: Option, + + #[serde(default)] + pub on_failure: Option, +} + +/// Project identity metadata. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct ProjectConfig { + /// Registered User Service identity used as remote deploy payload `stack_code`. + #[serde(default)] + pub identity: Option, +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// StackerConfig — the root configuration type +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone, Serialize, Deserialize, Default, Validate)] +pub struct StackerConfig { + #[validate(min_length = 1)] + #[validate(max_length = 128)] + pub name: String, + + #[serde(default)] + pub version: Option, + + #[serde(default)] + pub organization: Option, + + #[serde(default)] + pub project: ProjectConfig, + + #[serde(default)] + pub app: AppSource, + + #[serde(default, deserialize_with = "deserialize_services")] + pub services: Vec, + + #[serde(default)] + pub proxy: ProxyConfig, + + #[serde(default)] + pub deploy: DeployConfig, + + #[serde(default)] + pub ai: AiConfig, + + #[serde(default)] + pub monitoring: MonitoringConfig, + + #[serde(default)] + pub hooks: HookConfig, + + #[serde(default)] + pub env_file: Option, + + #[serde(default)] + pub env: HashMap, +} + +impl StackerConfig { + /// Load config from a file path, resolving `${VAR}` environment variable + /// references and validating the result. + pub fn from_file(path: &Path) -> Result { + if !path.exists() { + return Err(CliError::ConfigNotFound { + path: path.to_path_buf(), + }); + } + + let raw_content = std::fs::read_to_string(path)?; + let env_file_vars = load_env_file_vars_from_yaml(path, &raw_content); + let resolved_content = resolve_env_vars_with_fallback(&raw_content, &env_file_vars)?; + let config: StackerConfig = serde_yaml::from_str(&resolved_content)?; + Ok(config) + } + + /// Load config from a YAML string (useful for tests). + pub fn from_str(yaml: &str) -> Result { + let resolved = resolve_env_vars(yaml)?; + let config: StackerConfig = serde_yaml::from_str(&resolved)?; + Ok(config) + } + + /// Validate cross-field semantic constraints beyond serde deserialization. + /// Returns a list of issues (errors, warnings, info). + pub fn validate_semantics(&self) -> Vec { + let mut issues = Vec::new(); + + // Cloud target requires a provider + if self.deploy.target == DeployTarget::Cloud && self.deploy.cloud.is_none() { + issues.push(ValidationIssue { + severity: Severity::Error, + code: "E001".to_string(), + message: "Cloud provider configuration is required for cloud deployment".to_string(), + field: Some("deploy.cloud.provider".to_string()), + }); + } + + // Server target requires a host + if self.deploy.target == DeployTarget::Server { + if self.deploy.server.is_none() { + issues.push(ValidationIssue { + severity: Severity::Error, + code: "E002".to_string(), + message: "Server host is required for server deployment".to_string(), + field: Some("deploy.server.host".to_string()), + }); + } + } + + if self.deploy.target == DeployTarget::Cloud { + if let Some(cloud) = &self.deploy.cloud { + if cloud.orchestrator == CloudOrchestrator::Remote { + let identity_empty = self + .project + .identity + .as_ref() + .map(|v| v.trim().is_empty()) + .unwrap_or(true); + + if identity_empty { + issues.push(ValidationIssue { + severity: Severity::Info, + code: "I001".to_string(), + message: "project.identity is not set; remote deploy will use default stack_code 'custom-stack'".to_string(), + field: Some("project.identity".to_string()), + }); + } + } + } + } + + // Custom app type with no image and no dockerfile + if self.app.app_type == AppType::Custom + && self.app.image.is_none() + && self.app.dockerfile.is_none() + { + issues.push(ValidationIssue { + severity: Severity::Error, + code: "E003".to_string(), + message: "Custom app type requires either 'image' or 'dockerfile'".to_string(), + field: Some("app".to_string()), + }); + } + + // Port conflict detection across services + let mut port_map: HashMap> = HashMap::new(); + for svc in &self.services { + for port_str in &svc.ports { + let host_port = extract_host_port(port_str); + port_map + .entry(host_port.clone()) + .or_default() + .push(svc.name.clone()); + } + } + for (port, services) in &port_map { + if services.len() > 1 { + issues.push(ValidationIssue { + severity: Severity::Warning, + code: "W001".to_string(), + message: format!( + "Port {} is used by multiple services: {}", + port, + services.join(", ") + ), + field: Some("services.ports".to_string()), + }); + } + } + + issues + } +} + +fn load_env_file_vars_from_yaml(path: &Path, raw_content: &str) -> HashMap { + let parsed: serde_yaml::Value = match serde_yaml::from_str(raw_content) { + Ok(v) => v, + Err(_) => return HashMap::new(), + }; + + let env_file_value = parsed + .get("env_file") + .and_then(|v| v.as_str()) + .map(|s| s.trim()) + .filter(|s| !s.is_empty()); + + let env_file = match env_file_value { + Some(v) => v, + None => return HashMap::new(), + }; + + let config_dir = path.parent().unwrap_or_else(|| Path::new(".")); + let env_file_path = Path::new(env_file); + let resolved_path = if env_file_path.is_absolute() { + env_file_path.to_path_buf() + } else { + config_dir.join(env_file_path) + }; + + let content = match std::fs::read_to_string(&resolved_path) { + Ok(c) => c, + Err(_) => return HashMap::new(), + }; + + let mut vars = HashMap::new(); + for line in content.lines() { + let trimmed = line.trim(); + if trimmed.is_empty() || trimmed.starts_with('#') { + continue; + } + + if let Some((key, value)) = trimmed.split_once('=') { + let key = key.trim(); + if key.is_empty() { + continue; + } + + let mut value = value.trim().to_string(); + if (value.starts_with('"') && value.ends_with('"')) + || (value.starts_with('\'') && value.ends_with('\'')) + { + if value.len() >= 2 { + value = value[1..value.len() - 1].to_string(); + } + } + vars.insert(key.to_string(), value); + } + } + + vars +} + +/// Extract the host port from a port mapping string like "8080:80" → "8080". +fn extract_host_port(port_str: &str) -> String { + port_str + .split(':') + .next() + .unwrap_or(port_str) + .to_string() +} + +/// Resolve `${VAR_NAME}` references in a string using process environment. +fn resolve_env_vars(content: &str) -> Result { + resolve_env_vars_with_fallback(content, &HashMap::new()) +} + +fn resolve_env_vars_with_fallback( + content: &str, + fallback_vars: &HashMap, +) -> Result { + let mut result = content.to_string(); + let re = regex::Regex::new(r"\$\{([^}]+)\}").expect("valid regex"); + + // Collect all matches first to avoid borrow issues + let captures: Vec<(String, String)> = re + .captures_iter(content) + .map(|cap| { + let full_match = cap[0].to_string(); + let var_name = cap[1].to_string(); + (full_match, var_name) + }) + .collect(); + + for (full_match, var_name) in captures { + let value = match std::env::var(&var_name) { + Ok(v) => v, + Err(_) => fallback_vars + .get(&var_name) + .cloned() + .ok_or_else(|| CliError::EnvVarNotFound { + var_name: var_name.clone(), + })?, + }; + result = result.replace(&full_match, &value); + } + + Ok(result) +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ConfigBuilder — fluent builder for programmatic construction +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Default)] +pub struct ConfigBuilder { + name: Option, + version: Option, + organization: Option, + project_identity: Option, + app_type: Option, + app_path: Option, + app_image: Option, + app_dockerfile: Option, + build_args: HashMap, + services: Vec, + proxy: Option, + deploy_target: Option, + cloud: Option, + server: Option, + ai: Option, + monitoring: Option, + hooks: Option, + env: HashMap, + env_file: Option, +} + +impl ConfigBuilder { + pub fn new() -> Self { + Self::default() + } + + pub fn name>(mut self, name: S) -> Self { + self.name = Some(name.into()); + self + } + + pub fn version>(mut self, version: S) -> Self { + self.version = Some(version.into()); + self + } + + pub fn organization>(mut self, org: S) -> Self { + self.organization = Some(org.into()); + self + } + + pub fn project_identity>(mut self, identity: S) -> Self { + self.project_identity = Some(identity.into()); + self + } + + pub fn app_type(mut self, app_type: AppType) -> Self { + self.app_type = Some(app_type); + self + } + + pub fn app_path>(mut self, path: P) -> Self { + self.app_path = Some(path.into()); + self + } + + pub fn app_image>(mut self, image: S) -> Self { + self.app_image = Some(image.into()); + self + } + + pub fn app_dockerfile>(mut self, path: P) -> Self { + self.app_dockerfile = Some(path.into()); + self + } + + pub fn build_arg, V: Into>(mut self, key: K, value: V) -> Self { + self.build_args.insert(key.into(), value.into()); + self + } + + pub fn add_service(mut self, service: ServiceDefinition) -> Self { + self.services.push(service); + self + } + + pub fn proxy(mut self, proxy: ProxyConfig) -> Self { + self.proxy = Some(proxy); + self + } + + pub fn deploy_target(mut self, target: DeployTarget) -> Self { + self.deploy_target = Some(target); + self + } + + pub fn cloud(mut self, cloud: CloudConfig) -> Self { + self.cloud = Some(cloud); + self + } + + pub fn server(mut self, server: ServerConfig) -> Self { + self.server = Some(server); + self + } + + pub fn ai(mut self, ai: AiConfig) -> Self { + self.ai = Some(ai); + self + } + + pub fn monitoring(mut self, monitoring: MonitoringConfig) -> Self { + self.monitoring = Some(monitoring); + self + } + + pub fn hooks(mut self, hooks: HookConfig) -> Self { + self.hooks = Some(hooks); + self + } + + pub fn env, V: Into>(mut self, key: K, value: V) -> Self { + self.env.insert(key.into(), value.into()); + self + } + + pub fn env_file>(mut self, path: P) -> Self { + self.env_file = Some(path.into()); + self + } + + /// Consume the builder, validate required fields, and produce StackerConfig. + pub fn build(self) -> Result { + let name = self + .name + .ok_or_else(|| CliError::ConfigValidation("name is required".into()))?; + + let build_config = if self.build_args.is_empty() { + None + } else { + Some(BuildConfig { + context: ".".to_string(), + args: self.build_args, + }) + }; + + Ok(StackerConfig { + name, + version: self.version, + organization: self.organization, + project: ProjectConfig { + identity: self.project_identity, + }, + app: AppSource { + app_type: self.app_type.unwrap_or_default(), + path: self.app_path.unwrap_or_else(|| PathBuf::from(".")), + dockerfile: self.app_dockerfile, + image: self.app_image, + build: build_config, + ports: Vec::new(), + volumes: Vec::new(), + environment: HashMap::new(), + }, + services: self.services, + proxy: self.proxy.unwrap_or_default(), + deploy: DeployConfig { + target: self.deploy_target.unwrap_or_default(), + compose_file: None, + cloud: self.cloud, + server: self.server, + }, + ai: self.ai.unwrap_or_default(), + monitoring: self.monitoring.unwrap_or_default(), + hooks: self.hooks.unwrap_or_default(), + env_file: self.env_file, + env: self.env, + }) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests — Phase 1: Config parser + builder +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use std::env; + use std::fs; + use tempfile::TempDir; + + #[test] + fn test_parse_minimal_config() { + let yaml = r#" +name: my-site +app: + type: static + path: ./public +"#; + let config = StackerConfig::from_str(yaml).unwrap(); + assert_eq!(config.name, "my-site"); + assert_eq!(config.app.app_type, AppType::Static); + assert_eq!(config.app.path, PathBuf::from("./public")); + assert!(config.services.is_empty()); + assert_eq!(config.proxy.proxy_type, ProxyType::None); + assert_eq!(config.deploy.target, DeployTarget::Local); + assert!(!config.ai.enabled); + assert!(!config.monitoring.status_panel); + } + + #[test] + fn test_parse_full_config() { + let yaml = r#" +name: full-app +version: "2.0" +organization: test-org +app: + type: node + path: ./src + build: + context: . + args: + NODE_ENV: production +services: + - name: postgres + image: postgres:16 + ports: ["5432:5432"] + environment: + POSTGRES_DB: testdb + - name: redis + image: redis:7-alpine + ports: ["6379:6379"] +proxy: + type: nginx + domains: + - domain: test.example.com + ssl: auto + upstream: app:3000 +deploy: + target: local +ai: + enabled: true + provider: ollama + model: llama3 + endpoint: http://localhost:11434 + tasks: [dockerfile, troubleshoot] +monitoring: + status_panel: true + healthcheck: + endpoint: /health + interval: 30s +env: + APP_PORT: "3000" + LOG_LEVEL: debug +"#; + let config = StackerConfig::from_str(yaml).unwrap(); + assert_eq!(config.name, "full-app"); + assert_eq!(config.version, Some("2.0".to_string())); + assert_eq!(config.organization, Some("test-org".to_string())); + assert_eq!(config.app.app_type, AppType::Node); + assert_eq!(config.services.len(), 2); + assert_eq!(config.services[0].name, "postgres"); + assert_eq!(config.services[1].name, "redis"); + assert_eq!(config.proxy.proxy_type, ProxyType::Nginx); + assert_eq!(config.proxy.domains.len(), 1); + assert_eq!(config.proxy.domains[0].domain, "test.example.com"); + assert_eq!(config.proxy.domains[0].ssl, SslMode::Auto); + assert!(config.ai.enabled); + assert_eq!(config.ai.provider, AiProviderType::Ollama); + assert!(config.monitoring.status_panel); + assert_eq!(config.env.get("APP_PORT").unwrap(), "3000"); + } + + #[test] + fn test_parse_env_var_interpolation() { + env::set_var("STACKER_TEST_DB_PASS", "secret123"); + let yaml = r#" +name: env-test +app: + type: static +env: + DB_PASSWORD: ${STACKER_TEST_DB_PASS} +"#; + let config = StackerConfig::from_str(yaml).unwrap(); + assert_eq!(config.env.get("DB_PASSWORD").unwrap(), "secret123"); + env::remove_var("STACKER_TEST_DB_PASS"); + } + + #[test] + fn test_parse_env_var_missing_returns_error() { + // Ensure the var definitely doesn't exist + env::remove_var("STACKER_TEST_NONEXISTENT_VAR_12345"); + let yaml = r#" +name: env-test +env: + KEY: ${STACKER_TEST_NONEXISTENT_VAR_12345} +"#; + let result = StackerConfig::from_str(yaml); + assert!(result.is_err()); + let err = result.unwrap_err(); + let msg = format!("{err}"); + assert!( + msg.contains("STACKER_TEST_NONEXISTENT_VAR_12345"), + "Expected var name in error: {msg}" + ); + } + + #[test] + fn test_from_file_resolves_env_from_env_file() { + let dir = TempDir::new().unwrap(); + fs::write(dir.path().join(".env"), "DOCKER_IMAGE=node:14-alpine\n").unwrap(); + + let yaml = r#" +name: env-file-test +env_file: .env +app: + type: custom + path: . + image: ${DOCKER_IMAGE} +deploy: + target: local +"#; + let config_path = dir.path().join("stacker.yml"); + fs::write(&config_path, yaml).unwrap(); + + let config = StackerConfig::from_file(&config_path).unwrap(); + assert_eq!(config.app.image.as_deref(), Some("node:14-alpine")); + } + + #[test] + fn test_parse_invalid_app_type_returns_error() { + let yaml = r#" +name: bad-type +app: + type: cobol +"#; + let result = StackerConfig::from_str(yaml); + assert!(result.is_err()); + } + + #[test] + fn test_parse_missing_name_returns_error() { + let yaml = r#" +app: + type: static +"#; + // name is a required field — serde fails deserialization if missing + let result = StackerConfig::from_str(yaml); + assert!(result.is_err()); + } + + #[test] + fn test_parse_services_array() { + let yaml = r#" +name: svc-test +services: + - name: postgres + image: postgres:16 + ports: ["5432:5432"] + - name: redis + image: redis:7-alpine + - name: minio + image: minio/minio + ports: ["9000:9000", "9001:9001"] +"#; + let config = StackerConfig::from_str(yaml).unwrap(); + assert_eq!(config.services.len(), 3); + assert_eq!(config.services[0].name, "postgres"); + assert_eq!(config.services[0].image, "postgres:16"); + assert_eq!(config.services[0].ports, vec!["5432:5432"]); + assert_eq!(config.services[2].name, "minio"); + assert_eq!(config.services[2].ports.len(), 2); + } + + #[test] + fn test_parse_services_map() { + let yaml = r#" +name: svc-map-test +services: + web: + name: web + image: nginx:alpine + ports: ["8080:80"] + redis: + name: redis + image: redis:7-alpine +"#; + + let config = StackerConfig::from_str(yaml).unwrap(); + assert_eq!(config.services.len(), 2); + assert!(config.services.iter().any(|s| s.name == "web" && s.image == "nginx:alpine")); + assert!(config.services.iter().any(|s| s.name == "redis" && s.image == "redis:7-alpine")); + } + + #[test] + fn test_parse_services_map_infers_name_from_key() { + let yaml = r#" +name: svc-map-key-test +services: + web: + image: nginx:alpine + ports: ["8080:80"] +"#; + + let config = StackerConfig::from_str(yaml).unwrap(); + assert_eq!(config.services.len(), 1); + assert_eq!(config.services[0].name, "web"); + assert_eq!(config.services[0].image, "nginx:alpine"); + } + + #[test] + fn test_parse_proxy_domains() { + let yaml = r#" +name: proxy-test +proxy: + type: nginx + domains: + - domain: app.example.com + ssl: auto + upstream: app:3000 + - domain: api.example.com + ssl: off + upstream: app:8080 +"#; + let config = StackerConfig::from_str(yaml).unwrap(); + assert_eq!(config.proxy.proxy_type, ProxyType::Nginx); + assert_eq!(config.proxy.domains.len(), 2); + assert_eq!(config.proxy.domains[0].ssl, SslMode::Auto); + assert_eq!(config.proxy.domains[0].upstream, "app:3000"); + assert_eq!(config.proxy.domains[1].ssl, SslMode::Off); + } + + #[test] + fn test_parse_ai_section_with_ollama() { + let yaml = r#" +name: ai-test +ai: + enabled: true + provider: ollama + model: llama3 + endpoint: http://localhost:11434 + tasks: [dockerfile, compose] +"#; + let config = StackerConfig::from_str(yaml).unwrap(); + assert!(config.ai.enabled); + assert_eq!(config.ai.provider, AiProviderType::Ollama); + assert_eq!(config.ai.model, Some("llama3".to_string())); + assert_eq!( + config.ai.endpoint, + Some("http://localhost:11434".to_string()) + ); + assert_eq!(config.ai.tasks, vec!["dockerfile", "compose"]); + } + + #[test] + fn test_default_deploy_target_is_local() { + let yaml = "name: minimal\n"; + let config = StackerConfig::from_str(yaml).unwrap(); + assert_eq!(config.deploy.target, DeployTarget::Local); + } + + #[test] + fn test_default_proxy_type_is_none() { + let yaml = "name: minimal\n"; + let config = StackerConfig::from_str(yaml).unwrap(); + assert_eq!(config.proxy.proxy_type, ProxyType::None); + } + + #[test] + fn test_config_file_not_found() { + let result = StackerConfig::from_file(Path::new("/nonexistent/stacker.yml")); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!( + matches!(err, CliError::ConfigNotFound { .. }), + "Expected ConfigNotFound, got: {err:?}" + ); + } + + #[test] + fn test_config_invalid_yaml_syntax() { + let result = StackerConfig::from_str("{{invalid: yaml: :::"); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!( + matches!(err, CliError::ConfigParseFailed { .. }), + "Expected ConfigParseFailed, got: {err:?}" + ); + } + + #[test] + fn test_validate_semantics_cloud_without_provider() { + let config = ConfigBuilder::new() + .name("test") + .deploy_target(DeployTarget::Cloud) + .build() + .unwrap(); + + let issues = config.validate_semantics(); + let errors: Vec<_> = issues + .iter() + .filter(|i| i.severity == Severity::Error) + .collect(); + assert!(!errors.is_empty(), "Expected validation error for missing cloud provider"); + assert!( + errors.iter().any(|e| e.field.as_deref() == Some("deploy.cloud.provider")), + "Expected field reference to deploy.cloud.provider" + ); + } + + #[test] + fn test_validate_semantics_server_without_host() { + let config = ConfigBuilder::new() + .name("test") + .deploy_target(DeployTarget::Server) + .build() + .unwrap(); + + let issues = config.validate_semantics(); + let errors: Vec<_> = issues + .iter() + .filter(|i| i.severity == Severity::Error) + .collect(); + assert!(!errors.is_empty(), "Expected validation error for missing server host"); + assert!( + errors.iter().any(|e| e.message.contains("host")), + "Expected 'host' mentioned in error" + ); + } + + #[test] + fn test_validate_semantics_port_conflict() { + let config = StackerConfig::from_str( + r#" +name: port-conflict +services: + - name: web1 + image: nginx + ports: ["8080:80"] + - name: web2 + image: httpd + ports: ["8080:80"] +"#, + ) + .unwrap(); + + let issues = config.validate_semantics(); + let warnings: Vec<_> = issues + .iter() + .filter(|i| i.severity == Severity::Warning) + .collect(); + assert!( + !warnings.is_empty(), + "Expected warning about port conflict" + ); + assert!( + warnings.iter().any(|w| w.message.contains("8080")), + "Expected port 8080 in warning" + ); + } + + #[test] + fn test_validate_semantics_no_image_no_dockerfile_custom() { + let config = ConfigBuilder::new() + .name("test") + .app_type(AppType::Custom) + .build() + .unwrap(); + + let issues = config.validate_semantics(); + let errors: Vec<_> = issues + .iter() + .filter(|i| i.severity == Severity::Error) + .collect(); + assert!( + !errors.is_empty(), + "Expected error for custom type without image or dockerfile" + ); + } + + #[test] + fn test_validate_semantics_happy_path() { + let config = ConfigBuilder::new() + .name("valid-app") + .app_type(AppType::Static) + .build() + .unwrap(); + + let issues = config.validate_semantics(); + let errors: Vec<_> = issues + .iter() + .filter(|i| i.severity == Severity::Error) + .collect(); + assert!(errors.is_empty(), "Expected no errors, got: {errors:?}"); + } + + #[test] + fn test_validate_semantics_remote_cloud_defaults_stack_code_without_project_identity() { + let config = ConfigBuilder::new() + .name("remote-app") + .deploy_target(DeployTarget::Cloud) + .cloud(CloudConfig { + provider: CloudProvider::Hetzner, + orchestrator: CloudOrchestrator::Remote, + region: Some("nbg1".to_string()), + size: Some("cx11".to_string()), + install_image: None, + remote_payload_file: None, + ssh_key: None, + key: None, + server: None, + }) + .build() + .unwrap(); + + let issues = config.validate_semantics(); + let errors: Vec<_> = issues + .iter() + .filter(|i| i.severity == Severity::Error) + .collect(); + let infos: Vec<_> = issues + .iter() + .filter(|i| i.severity == Severity::Info) + .collect(); + assert!(errors.is_empty(), "Expected no blocking errors, got: {errors:?}"); + assert!( + infos + .iter() + .any(|e| e.field.as_deref() == Some("project.identity")), + "Expected project.identity informational hint" + ); + } + + // ━━━ ConfigBuilder tests ━━━ + + #[test] + fn test_config_builder_minimal() { + let config = ConfigBuilder::new().name("test").build().unwrap(); + assert_eq!(config.name, "test"); + assert_eq!(config.app.app_type, AppType::Static); + assert_eq!(config.app.path, PathBuf::from(".")); + assert_eq!(config.deploy.target, DeployTarget::Local); + assert_eq!(config.project.identity, None); + } + + #[test] + fn test_config_builder_project_identity() { + let config = ConfigBuilder::new() + .name("test") + .project_identity("registered-stack-code") + .build() + .unwrap(); + assert_eq!( + config.project.identity.as_deref(), + Some("registered-stack-code") + ); + } + + #[test] + fn test_config_builder_fluent_chain() { + let config = ConfigBuilder::new() + .name("my-app") + .version("1.0") + .organization("acme") + .app_type(AppType::Node) + .app_path("./src") + .add_service(ServiceDefinition { + name: "postgres".to_string(), + image: "postgres:16".to_string(), + ports: vec!["5432:5432".to_string()], + environment: HashMap::new(), + volumes: vec![], + depends_on: vec![], + }) + .deploy_target(DeployTarget::Cloud) + .cloud(CloudConfig { + provider: CloudProvider::Hetzner, + orchestrator: CloudOrchestrator::Local, + region: Some("fsn1".to_string()), + size: Some("cx21".to_string()), + install_image: None, + remote_payload_file: None, + ssh_key: None, + key: None, + server: None, + }) + .build() + .unwrap(); + + assert_eq!(config.name, "my-app"); + assert_eq!(config.version, Some("1.0".to_string())); + assert_eq!(config.organization, Some("acme".to_string())); + assert_eq!(config.app.app_type, AppType::Node); + assert_eq!(config.app.path, PathBuf::from("./src")); + assert_eq!(config.services.len(), 1); + assert_eq!(config.deploy.target, DeployTarget::Cloud); + assert!(config.deploy.cloud.is_some()); + } + + #[test] + fn test_config_builder_missing_name_returns_error() { + let result = ConfigBuilder::new().app_type(AppType::Static).build(); + assert!(result.is_err()); + let err = result.unwrap_err(); + let msg = format!("{err}"); + assert!(msg.contains("name"), "Expected 'name' in error: {msg}"); + } + + #[test] + fn test_config_builder_default_app_type_is_static() { + let config = ConfigBuilder::new().name("x").build().unwrap(); + assert_eq!(config.app.app_type, AppType::Static); + } + + #[test] + fn test_config_builder_to_yaml_roundtrip() { + let original = ConfigBuilder::new() + .name("roundtrip") + .app_type(AppType::Python) + .app_path("./app") + .env("PORT", "8000") + .build() + .unwrap(); + + let yaml = serde_yaml::to_string(&original).unwrap(); + let parsed = StackerConfig::from_str(&yaml).unwrap(); + + assert_eq!(original.name, parsed.name); + assert_eq!(original.app.app_type, parsed.app.app_type); + assert_eq!(original.app.path, parsed.app.path); + assert_eq!( + original.env.get("PORT"), + parsed.env.get("PORT") + ); + } + + #[test] + fn test_config_builder_multiple_services() { + let config = ConfigBuilder::new() + .name("multi") + .add_service(ServiceDefinition { + name: "pg".to_string(), + image: "postgres:16".to_string(), + ports: vec![], + environment: HashMap::new(), + volumes: vec![], + depends_on: vec![], + }) + .add_service(ServiceDefinition { + name: "redis".to_string(), + image: "redis:7".to_string(), + ports: vec![], + environment: HashMap::new(), + volumes: vec![], + depends_on: vec![], + }) + .add_service(ServiceDefinition { + name: "minio".to_string(), + image: "minio/minio".to_string(), + ports: vec![], + environment: HashMap::new(), + volumes: vec![], + depends_on: vec![], + }) + .build() + .unwrap(); + + assert_eq!(config.services.len(), 3); + } + + // ━━━ Enum tests ━━━ + + #[test] + fn test_app_type_display() { + assert_eq!(format!("{}", AppType::Static), "static"); + assert_eq!(format!("{}", AppType::Node), "node"); + assert_eq!(format!("{}", AppType::Python), "python"); + assert_eq!(format!("{}", AppType::Rust), "rust"); + assert_eq!(format!("{}", AppType::Go), "go"); + assert_eq!(format!("{}", AppType::Php), "php"); + assert_eq!(format!("{}", AppType::Custom), "custom"); + } + + #[test] + fn test_app_type_serde_roundtrip() { + let json = serde_json::to_string(&AppType::Node).unwrap(); + assert_eq!(json, "\"node\""); + let parsed: AppType = serde_json::from_str(&json).unwrap(); + assert_eq!(parsed, AppType::Node); + } + + #[test] + fn test_app_type_default_is_static() { + assert_eq!(AppType::default(), AppType::Static); + } + + #[test] + fn test_deploy_target_display() { + assert_eq!(format!("{}", DeployTarget::Local), "local"); + assert_eq!(format!("{}", DeployTarget::Cloud), "cloud"); + assert_eq!(format!("{}", DeployTarget::Server), "server"); + } + + #[test] + fn test_deploy_target_default_is_local() { + assert_eq!(DeployTarget::default(), DeployTarget::Local); + } + + #[test] + fn test_proxy_type_display() { + assert_eq!(format!("{}", ProxyType::Nginx), "nginx"); + assert_eq!( + format!("{}", ProxyType::NginxProxyManager), + "nginx-proxy-manager" + ); + assert_eq!(format!("{}", ProxyType::Traefik), "traefik"); + assert_eq!(format!("{}", ProxyType::None), "none"); + } + + #[test] + fn test_proxy_type_default_is_none() { + assert_eq!(ProxyType::default(), ProxyType::None); + } +} diff --git a/src/cli/credentials.rs b/src/cli/credentials.rs new file mode 100644 index 00000000..9c2c7a8c --- /dev/null +++ b/src/cli/credentials.rs @@ -0,0 +1,829 @@ +use std::fmt; +use std::path::{Path, PathBuf}; + +use chrono::{DateTime, Duration, Utc}; +use serde::{Deserialize, Serialize}; + +use crate::cli::error::CliError; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// StoredCredentials — what we persist to disk +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Credentials file stored at `~/.config/stacker/credentials.json`. +/// +/// Mirrors the User Service OAuth token response (`/oauth_server/token`): +/// `{ access_token, refresh_token, token_type, scope, expires_in }`. +/// +/// We additionally store the absolute expiry time and user email for +/// convenience (avoids a network call for `stacker whoami`). +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct StoredCredentials { + pub access_token: String, + pub refresh_token: Option, + pub token_type: String, + pub expires_at: DateTime, + pub email: Option, + pub server_url: Option, + pub org: Option, + pub domain: Option, +} + +impl StoredCredentials { + /// True when the access token's expiry has passed. + pub fn is_expired(&self) -> bool { + Utc::now() >= self.expires_at + } + + /// True when the token will expire within the given duration. + pub fn expires_within(&self, margin: Duration) -> bool { + Utc::now() + margin >= self.expires_at + } +} + +/// Convert an OAuth token response (with relative `expires_in`) into +/// `StoredCredentials` with an absolute `expires_at`. +impl From for StoredCredentials { + fn from(resp: TokenResponse) -> Self { + let expires_at = Utc::now() + + Duration::seconds(resp.expires_in.unwrap_or(3600) as i64); + + Self { + access_token: resp.access_token, + refresh_token: resp.refresh_token, + token_type: resp.token_type.unwrap_or_else(|| "Bearer".to_string()), + expires_at, + email: None, + server_url: None, + org: None, + domain: None, + } + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// TokenResponse — raw OAuth /token reply +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Raw JSON returned by `POST /oauth_server/token`. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TokenResponse { + pub access_token: String, + pub refresh_token: Option, + pub token_type: Option, + pub scope: Option, + pub expires_in: Option, +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// CredentialStore trait — abstraction for testability (DIP) +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Pluggable storage back-end. Production writes to disk; tests use +/// an in-memory implementation. +pub trait CredentialStore: Send + Sync { + fn save(&self, creds: &StoredCredentials) -> Result<(), CliError>; + fn load(&self) -> Result, CliError>; + fn delete(&self) -> Result<(), CliError>; +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// FileCredentialStore — XDG-compliant file storage +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Stores credentials in `/stacker/credentials.json`. +/// +/// On macOS: `~/Library/Application Support/stacker/credentials.json` +/// On Linux: `~/.config/stacker/credentials.json` +pub struct FileCredentialStore { + path: PathBuf, +} + +impl FileCredentialStore { + /// Create a store rooted in the platform-specific config directory. + /// Falls back to `~/.config/stacker/` if detection fails. + pub fn default_path() -> PathBuf { + let base = std::env::var("XDG_CONFIG_HOME") + .map(PathBuf::from) + .or_else(|_| { + std::env::var("HOME").map(|h| PathBuf::from(h).join(".config")) + }) + .unwrap_or_else(|_| PathBuf::from(".")); + + base.join("stacker").join("credentials.json") + } + + pub fn new(path: PathBuf) -> Self { + Self { path } + } + + /// Use the platform default path. + pub fn with_default_path() -> Self { + Self::new(Self::default_path()) + } + + pub fn path(&self) -> &Path { + &self.path + } +} + +impl CredentialStore for FileCredentialStore { + fn save(&self, creds: &StoredCredentials) -> Result<(), CliError> { + if let Some(parent) = self.path.parent() { + std::fs::create_dir_all(parent)?; + } + + let json = serde_json::to_string_pretty(creds) + .map_err(|e| CliError::AuthFailed(format!("Failed to serialize credentials: {e}")))?; + + std::fs::write(&self.path, &json)?; + + // Restrict permissions on Unix (owner read/write only) + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let perms = std::fs::Permissions::from_mode(0o600); + std::fs::set_permissions(&self.path, perms)?; + } + + Ok(()) + } + + fn load(&self) -> Result, CliError> { + if !self.path.exists() { + return Ok(None); + } + + let content = std::fs::read_to_string(&self.path)?; + let creds: StoredCredentials = serde_json::from_str(&content) + .map_err(|e| CliError::AuthFailed(format!("Corrupt credentials file: {e}")))?; + + Ok(Some(creds)) + } + + fn delete(&self) -> Result<(), CliError> { + if self.path.exists() { + std::fs::remove_file(&self.path)?; + } + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// CredentialsManager — high-level operations +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Orchestrates login, logout, token loading, and expiry checking. +/// +/// Depends on `CredentialStore` (DIP) so tests can inject in-memory storage. +pub struct CredentialsManager { + store: S, +} + +impl CredentialsManager { + pub fn new(store: S) -> Self { + Self { store } + } + + /// Persist a new credential set (typically after a successful login). + pub fn save(&self, creds: &StoredCredentials) -> Result<(), CliError> { + self.store.save(creds) + } + + /// Load stored credentials, returning `None` if no file exists. + pub fn load(&self) -> Result, CliError> { + self.store.load() + } + + /// Remove stored credentials (logout). + pub fn logout(&self) -> Result<(), CliError> { + self.store.delete() + } + + /// Load credentials and ensure they are present and not expired. + /// Returns `CliError::LoginRequired` when absent, + /// `CliError::TokenExpired` when expired. + pub fn require_valid_token( + &self, + feature: &str, + ) -> Result { + let creds = self.store.load()?.ok_or_else(|| CliError::LoginRequired { + feature: feature.to_string(), + })?; + + if creds.is_expired() { + return Err(CliError::TokenExpired); + } + + Ok(creds) + } + + /// Returns the bearer token header value if credentials are valid. + pub fn bearer_header(&self, feature: &str) -> Result { + let creds = self.require_valid_token(feature)?; + Ok(format!("{} {}", creds.token_type, creds.access_token)) + } +} + +impl CredentialsManager { + /// Convenience: create a manager backed by the default file path. + pub fn with_default_store() -> Self { + Self::new(FileCredentialStore::with_default_path()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// OAuthClient trait — abstraction over HTTP login calls +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Default TryDirect platform API base URL. +pub const DEFAULT_API_URL: &str = "https://api.try.direct"; + +/// OAuth token endpoint path (relative to auth_url). +const TOKEN_ENDPOINT: &str = "/auth/login"; + +fn is_direct_login_endpoint(auth_url: &str) -> bool { + let url = auth_url.trim_end_matches('/').to_lowercase(); + url.ends_with("/auth/login") || url.ends_with("/server/user/auth/login") || url.ends_with("/login") +} + +/// Parameters for a login request. +#[derive(Debug, Clone)] +pub struct LoginRequest { + pub email: String, + pub password: String, + pub auth_url: Option, + pub org: Option, + pub domain: Option, +} + +/// Abstraction over the HTTP call to the OAuth token endpoint. +/// Production uses `HttpOAuthClient`; tests can inject a mock. +pub trait OAuthClient: Send + Sync { + fn request_token(&self, auth_url: &str, email: &str, password: &str) + -> Result; +} + +/// Production OAuth client using `reqwest::blocking`. +pub struct HttpOAuthClient; + +impl OAuthClient for HttpOAuthClient { + fn request_token( + &self, + auth_url: &str, + email: &str, + password: &str, + ) -> Result { + let direct_login = is_direct_login_endpoint(auth_url); + let url = if direct_login { + auth_url.trim_end_matches('/').to_string() + } else { + format!("{}{}", auth_url.trim_end_matches('/'), TOKEN_ENDPOINT) + }; + + // Re-check: the constructed URL may now be a direct login endpoint + let direct_login = direct_login || is_direct_login_endpoint(&url); + + let client = reqwest::blocking::Client::builder() + .timeout(std::time::Duration::from_secs(30)) + .build() + .map_err(|e| CliError::AuthFailed(format!("HTTP client error: {e}")))?; + + let resp = if direct_login { + client + .post(&url) + .form(&[ + ("email", email), + ("password", password), + ]) + .send() + } else { + client + .post(&url) + .form(&[ + ("grant_type", "password"), + ("username", email), + ("password", password), + ]) + .send() + } + .map_err(|e| CliError::AuthFailed(format!("Network error: {e}")))?; + + if !resp.status().is_success() { + let status = resp.status(); + let body = resp.text().unwrap_or_default(); + let body_preview: String = body.chars().take(240).collect(); + let html_404_hint = if status == reqwest::StatusCode::NOT_FOUND + && (body.contains("( + store: &CredentialsManager, + oauth: &O, + request: &LoginRequest, +) -> Result { + let env_auth_url = std::env::var("STACKER_AUTH_URL") + .ok() + .or_else(|| std::env::var("STACKER_API_URL").ok()); + let auth_url = request + .auth_url + .as_deref() + .or(env_auth_url.as_deref()) + .unwrap_or(DEFAULT_API_URL); + + let token_resp = oauth.request_token(auth_url, &request.email, &request.password)?; + let mut creds = StoredCredentials::from(token_resp); + creds.email = Some(request.email.clone()); + creds.server_url = Some(auth_url.to_string()); + creds.org = request.org.clone(); + creds.domain = request.domain.clone(); + + store.save(&creds)?; + Ok(creds) +} + +impl fmt::Display for StoredCredentials { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let email = self.email.as_deref().unwrap_or(""); + let expired = if self.is_expired() { " (expired)" } else { "" }; + write!(f, "Logged in as {email}{expired}") + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::{Arc, Mutex}; + + #[test] + fn test_is_direct_login_endpoint_detection() { + assert!(is_direct_login_endpoint("https://dev.try.direct/server/user/auth/login")); + assert!(is_direct_login_endpoint("https://dev.try.direct/server/user/auth/login/")); + assert!(!is_direct_login_endpoint("https://api.try.direct")); + } + + // ── In-memory mock store ──────────────────────── + + #[derive(Clone, Default)] + struct MockCredentialStore { + inner: Arc>>, + } + + impl CredentialStore for MockCredentialStore { + fn save(&self, creds: &StoredCredentials) -> Result<(), CliError> { + *self.inner.lock().unwrap() = Some(creds.clone()); + Ok(()) + } + + fn load(&self) -> Result, CliError> { + Ok(self.inner.lock().unwrap().clone()) + } + + fn delete(&self) -> Result<(), CliError> { + *self.inner.lock().unwrap() = None; + Ok(()) + } + } + + fn valid_creds() -> StoredCredentials { + StoredCredentials { + access_token: "test-access-token".to_string(), + refresh_token: Some("test-refresh-token".to_string()), + token_type: "Bearer".to_string(), + expires_at: Utc::now() + Duration::hours(1), + email: Some("user@example.com".to_string()), + server_url: Some("https://try.direct".to_string()), + org: None, + domain: None, + } + } + + fn expired_creds() -> StoredCredentials { + StoredCredentials { + access_token: "expired-token".to_string(), + refresh_token: Some("expired-refresh".to_string()), + token_type: "Bearer".to_string(), + expires_at: Utc::now() - Duration::hours(1), + email: Some("old@example.com".to_string()), + server_url: None, + org: None, + domain: None, + } + } + + fn make_manager() -> (CredentialsManager, MockCredentialStore) { + let store = MockCredentialStore::default(); + let manager = CredentialsManager::new(store.clone()); + (manager, store) + } + + // ── StoredCredentials unit tests ──────────────── + + #[test] + fn test_valid_creds_not_expired() { + let creds = valid_creds(); + assert!(!creds.is_expired()); + } + + #[test] + fn test_expired_creds_is_expired() { + let creds = expired_creds(); + assert!(creds.is_expired()); + } + + #[test] + fn test_expires_within_margin() { + let creds = StoredCredentials { + access_token: "tok".into(), + refresh_token: None, + token_type: "Bearer".into(), + expires_at: Utc::now() + Duration::minutes(3), + email: None, + server_url: None, + org: None, + domain: None, + }; + assert!(creds.expires_within(Duration::minutes(5))); + assert!(!creds.expires_within(Duration::minutes(1))); + } + + #[test] + fn test_display_shows_email() { + let creds = valid_creds(); + let display = format!("{}", creds); + assert!(display.contains("user@example.com")); + assert!(!display.contains("expired")); + } + + #[test] + fn test_display_shows_expired() { + let creds = expired_creds(); + let display = format!("{}", creds); + assert!(display.contains("expired")); + } + + // ── TokenResponse → StoredCredentials conversion ─ + + #[test] + fn test_token_response_to_stored_credentials() { + let resp = TokenResponse { + access_token: "new-token".into(), + refresh_token: Some("new-refresh".into()), + token_type: Some("Bearer".into()), + scope: Some("read write".into()), + expires_in: Some(7200), + }; + let creds = StoredCredentials::from(resp); + assert_eq!(creds.access_token, "new-token"); + assert_eq!(creds.refresh_token.as_deref(), Some("new-refresh")); + assert_eq!(creds.token_type, "Bearer"); + // expires_at should be ~2 hours from now + let diff = creds.expires_at - Utc::now(); + assert!(diff.num_seconds() > 7100 && diff.num_seconds() <= 7200); + } + + #[test] + fn test_token_response_defaults() { + let resp = TokenResponse { + access_token: "tok".into(), + refresh_token: None, + token_type: None, + scope: None, + expires_in: None, + }; + let creds = StoredCredentials::from(resp); + assert_eq!(creds.token_type, "Bearer"); + // default expires_in is 3600 + let diff = creds.expires_at - Utc::now(); + assert!(diff.num_seconds() > 3500 && diff.num_seconds() <= 3600); + } + + // ── CredentialsManager tests ──────────────────── + + #[test] + fn test_save_and_load() { + let (manager, _) = make_manager(); + let creds = valid_creds(); + manager.save(&creds).unwrap(); + let loaded = manager.load().unwrap(); + assert!(loaded.is_some()); + assert_eq!(loaded.unwrap().access_token, "test-access-token"); + } + + #[test] + fn test_load_returns_none_when_empty() { + let (manager, _) = make_manager(); + let loaded = manager.load().unwrap(); + assert!(loaded.is_none()); + } + + #[test] + fn test_logout_removes_credentials() { + let (manager, _) = make_manager(); + manager.save(&valid_creds()).unwrap(); + manager.logout().unwrap(); + let loaded = manager.load().unwrap(); + assert!(loaded.is_none()); + } + + #[test] + fn test_require_valid_token_succeeds() { + let (manager, _) = make_manager(); + manager.save(&valid_creds()).unwrap(); + let creds = manager.require_valid_token("cloud deploy").unwrap(); + assert_eq!(creds.access_token, "test-access-token"); + } + + #[test] + fn test_require_valid_token_login_required_when_empty() { + let (manager, _) = make_manager(); + let err = manager.require_valid_token("cloud deploy").unwrap_err(); + let msg = format!("{}", err); + assert!(msg.contains("Login required")); + assert!(msg.contains("cloud deploy")); + } + + #[test] + fn test_require_valid_token_expired() { + let (manager, _) = make_manager(); + manager.save(&expired_creds()).unwrap(); + let err = manager.require_valid_token("cloud deploy").unwrap_err(); + let msg = format!("{}", err); + assert!(msg.contains("expired")); + } + + #[test] + fn test_bearer_header_format() { + let (manager, _) = make_manager(); + manager.save(&valid_creds()).unwrap(); + let header = manager.bearer_header("api call").unwrap(); + assert_eq!(header, "Bearer test-access-token"); + } + + #[test] + fn test_bearer_header_login_required() { + let (manager, _) = make_manager(); + let result = manager.bearer_header("api call"); + assert!(result.is_err()); + } + + // ── FileCredentialStore tests (real filesystem) ── + + #[test] + fn test_file_store_roundtrip() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("creds.json"); + let store = FileCredentialStore::new(path.clone()); + + let creds = valid_creds(); + store.save(&creds).unwrap(); + + let loaded = store.load().unwrap().unwrap(); + assert_eq!(loaded.access_token, creds.access_token); + assert_eq!(loaded.email, creds.email); + } + + #[test] + fn test_file_store_load_nonexistent() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("nonexistent.json"); + let store = FileCredentialStore::new(path); + assert!(store.load().unwrap().is_none()); + } + + #[test] + fn test_file_store_delete() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("creds.json"); + let store = FileCredentialStore::new(path.clone()); + + store.save(&valid_creds()).unwrap(); + assert!(path.exists()); + + store.delete().unwrap(); + assert!(!path.exists()); + } + + #[test] + fn test_file_store_delete_nonexistent_is_ok() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("does-not-exist.json"); + let store = FileCredentialStore::new(path); + assert!(store.delete().is_ok()); + } + + #[test] + fn test_file_store_creates_parent_directories() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("nested").join("dir").join("creds.json"); + let store = FileCredentialStore::new(path.clone()); + + store.save(&valid_creds()).unwrap(); + assert!(path.exists()); + } + + #[cfg(unix)] + #[test] + fn test_file_store_permissions() { + use std::os::unix::fs::PermissionsExt; + + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("creds.json"); + let store = FileCredentialStore::new(path.clone()); + + store.save(&valid_creds()).unwrap(); + + let perms = std::fs::metadata(&path).unwrap().permissions(); + assert_eq!(perms.mode() & 0o777, 0o600); + } + + #[test] + fn test_default_path_contains_stacker() { + let path = FileCredentialStore::default_path(); + let path_str = path.to_string_lossy(); + assert!(path_str.contains("stacker")); + assert!(path_str.contains("credentials.json")); + } + + // ── OAuthClient + login() tests ───────────────── + + /// Mock OAuthClient that returns a configurable result. + struct MockOAuthClient { + response: Option, + error_msg: Option, + } + + impl MockOAuthClient { + fn success() -> Self { + Self { + response: Some(TokenResponse { + access_token: "mock-access-token".into(), + refresh_token: Some("mock-refresh-token".into()), + token_type: Some("Bearer".into()), + scope: Some("read write".into()), + expires_in: Some(3600), + }), + error_msg: None, + } + } + fn failure(msg: &str) -> Self { + Self { + response: None, + error_msg: Some(msg.to_string()), + } + } + } + + impl OAuthClient for MockOAuthClient { + fn request_token( + &self, + _auth_url: &str, + _email: &str, + _password: &str, + ) -> Result { + match &self.response { + Some(resp) => Ok(resp.clone()), + None => Err(CliError::AuthFailed( + self.error_msg.clone().unwrap_or_default(), + )), + } + } + } + + #[test] + fn test_login_saves_credentials() { + let (manager, _store) = make_manager(); + let oauth = MockOAuthClient::success(); + let request = LoginRequest { + email: "user@example.com".into(), + password: "secret".into(), + auth_url: None, + org: None, + domain: None, + }; + + let creds = login(&manager, &oauth, &request).unwrap(); + assert_eq!(creds.access_token, "mock-access-token"); + assert_eq!(creds.email.as_deref(), Some("user@example.com")); + assert_eq!(creds.server_url.as_deref(), Some(DEFAULT_API_URL)); + + // Verify persisted + let loaded = manager.load().unwrap().unwrap(); + assert_eq!(loaded.access_token, "mock-access-token"); + } + + #[test] + fn test_login_with_org_stores_org() { + let (manager, _) = make_manager(); + let oauth = MockOAuthClient::success(); + let request = LoginRequest { + email: "user@example.com".into(), + password: "secret".into(), + auth_url: None, + org: Some("acme".into()), + domain: None, + }; + + let creds = login(&manager, &oauth, &request).unwrap(); + assert_eq!(creds.org.as_deref(), Some("acme")); + } + + #[test] + fn test_login_with_domain_stores_domain() { + let (manager, _) = make_manager(); + let oauth = MockOAuthClient::success(); + let request = LoginRequest { + email: "user@example.com".into(), + password: "secret".into(), + auth_url: None, + org: None, + domain: Some("acme.com".into()), + }; + + let creds = login(&manager, &oauth, &request).unwrap(); + assert_eq!(creds.domain.as_deref(), Some("acme.com")); + } + + #[test] + fn test_login_invalid_credentials_returns_error() { + let (manager, _) = make_manager(); + let oauth = MockOAuthClient::failure("Authentication failed (HTTP 401 Unauthorized): invalid"); + let request = LoginRequest { + email: "bad@example.com".into(), + password: "wrong".into(), + auth_url: None, + org: None, + domain: None, + }; + + let err = login(&manager, &oauth, &request).unwrap_err(); + let msg = format!("{}", err); + assert!(msg.contains("Authentication failed")); + } + + #[test] + fn test_login_auth_url_override() { + let (manager, _) = make_manager(); + let oauth = MockOAuthClient::success(); + let request = LoginRequest { + email: "user@example.com".into(), + password: "secret".into(), + auth_url: Some("https://custom.api".into()), + org: None, + domain: None, + }; + + let creds = login(&manager, &oauth, &request).unwrap(); + assert_eq!(creds.server_url.as_deref(), Some("https://custom.api")); + } + + #[test] + fn test_login_refresh_existing_token() { + let (manager, _) = make_manager(); + // Pre-populate with expired credentials + manager.save(&expired_creds()).unwrap(); + + let oauth = MockOAuthClient::success(); + let request = LoginRequest { + email: "user@example.com".into(), + password: "secret".into(), + auth_url: None, + org: None, + domain: None, + }; + + let creds = login(&manager, &oauth, &request).unwrap(); + assert_eq!(creds.access_token, "mock-access-token"); + assert!(!creds.is_expired()); + + // Only one credential set stored (overwritten, not duplicated) + let loaded = manager.load().unwrap().unwrap(); + assert_eq!(loaded.access_token, "mock-access-token"); + } +} diff --git a/src/cli/detector.rs b/src/cli/detector.rs new file mode 100644 index 00000000..335cbf9d --- /dev/null +++ b/src/cli/detector.rs @@ -0,0 +1,296 @@ +use std::path::Path; + +use crate::cli::config_parser::AppType; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ProjectDetection — result of scanning a project directory +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone)] +pub struct ProjectDetection { + pub app_type: AppType, + pub has_dockerfile: bool, + pub has_compose: bool, + pub has_env_file: bool, + pub detected_files: Vec, +} + +impl Default for ProjectDetection { + fn default() -> Self { + Self { + app_type: AppType::Custom, + has_dockerfile: false, + has_compose: false, + has_env_file: false, + detected_files: Vec::new(), + } + } +} + +/// Convert a detection result into the detected AppType. +impl From<&ProjectDetection> for AppType { + fn from(detection: &ProjectDetection) -> Self { + detection.app_type + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// FileSystem trait — abstraction for testability (DIP) +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +pub trait FileSystem: Send + Sync { + fn exists(&self, path: &Path) -> bool; + fn list_dir(&self, path: &Path) -> Result, std::io::Error>; +} + +/// Production filesystem using std::fs. +pub struct RealFileSystem; + +impl FileSystem for RealFileSystem { + fn exists(&self, path: &Path) -> bool { + path.exists() + } + + fn list_dir(&self, path: &Path) -> Result, std::io::Error> { + let entries = std::fs::read_dir(path)? + .filter_map(|e| e.ok()) + .map(|e| e.file_name().to_string_lossy().to_string()) + .collect(); + Ok(entries) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Detection markers — which files map to which app type +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +struct DetectionMarker { + filename: &'static str, + app_type: AppType, + priority: u8, // higher = stronger signal +} + +/// Ordered list of detection markers. Higher priority takes precedence. +const DETECTION_MARKERS: &[DetectionMarker] = &[ + DetectionMarker { + filename: "Cargo.toml", + app_type: AppType::Rust, + priority: 10, + }, + DetectionMarker { + filename: "go.mod", + app_type: AppType::Go, + priority: 10, + }, + DetectionMarker { + filename: "composer.json", + app_type: AppType::Php, + priority: 10, + }, + DetectionMarker { + filename: "package.json", + app_type: AppType::Node, + priority: 9, + }, + DetectionMarker { + filename: "pyproject.toml", + app_type: AppType::Python, + priority: 9, + }, + DetectionMarker { + filename: "requirements.txt", + app_type: AppType::Python, + priority: 8, + }, + DetectionMarker { + filename: "index.html", + app_type: AppType::Static, + priority: 5, + }, +]; + +/// Infrastructure files to detect alongside app type. +const DOCKERFILE_NAMES: &[&str] = &["Dockerfile", "dockerfile"]; +const COMPOSE_NAMES: &[&str] = &[ + "docker-compose.yml", + "docker-compose.yaml", + "compose.yml", + "compose.yaml", +]; +const ENV_FILE_NAMES: &[&str] = &[".env"]; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// detect_project — scan a directory to identify project type +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Detect the project type and infrastructure files in a directory. +pub fn detect_project( + project_path: &Path, + fs: &dyn FileSystem, +) -> ProjectDetection { + let files = match fs.list_dir(project_path) { + Ok(f) => f, + Err(_) => return ProjectDetection::default(), + }; + + let mut detection = ProjectDetection::default(); + let mut best_priority: u8 = 0; + + for filename in &files { + // Check app type markers + for marker in DETECTION_MARKERS { + if filename == marker.filename && marker.priority > best_priority { + detection.app_type = marker.app_type; + best_priority = marker.priority; + if !detection.detected_files.contains(filename) { + detection.detected_files.push(filename.clone()); + } + } + } + + // Check infrastructure files + if DOCKERFILE_NAMES.iter().any(|n| n == filename) { + detection.has_dockerfile = true; + detection.detected_files.push(filename.clone()); + } + + if COMPOSE_NAMES.iter().any(|n| n == filename) { + detection.has_compose = true; + detection.detected_files.push(filename.clone()); + } + + if ENV_FILE_NAMES.iter().any(|n| n == filename) { + detection.has_env_file = true; + } + } + + detection +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests — Phase 2 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + + /// In-memory mock filesystem for deterministic testing without I/O. + struct MockFileSystem { + files: Vec, + } + + impl MockFileSystem { + fn with_files(files: &[&str]) -> Self { + Self { + files: files.iter().map(|s| s.to_string()).collect(), + } + } + } + + impl FileSystem for MockFileSystem { + fn exists(&self, path: &Path) -> bool { + let name = path + .file_name() + .map(|n| n.to_string_lossy().to_string()) + .unwrap_or_default(); + self.files.contains(&name) + } + + fn list_dir(&self, _path: &Path) -> Result, std::io::Error> { + Ok(self.files.clone()) + } + } + + fn detect_with(files: &[&str]) -> ProjectDetection { + let fs = MockFileSystem::with_files(files); + detect_project(Path::new("/test"), &fs) + } + + #[test] + fn test_detect_static_html() { + let det = detect_with(&["index.html", "style.css"]); + assert_eq!(det.app_type, AppType::Static); + } + + #[test] + fn test_detect_node_project() { + let det = detect_with(&["package.json", "src"]); + assert_eq!(det.app_type, AppType::Node); + } + + #[test] + fn test_detect_python_requirements() { + let det = detect_with(&["requirements.txt", "app.py"]); + assert_eq!(det.app_type, AppType::Python); + } + + #[test] + fn test_detect_python_pyproject() { + let det = detect_with(&["pyproject.toml"]); + assert_eq!(det.app_type, AppType::Python); + } + + #[test] + fn test_detect_rust_project() { + let det = detect_with(&["Cargo.toml", "src"]); + assert_eq!(det.app_type, AppType::Rust); + } + + #[test] + fn test_detect_go_project() { + let det = detect_with(&["go.mod", "main.go"]); + assert_eq!(det.app_type, AppType::Go); + } + + #[test] + fn test_detect_php_composer() { + let det = detect_with(&["composer.json", "public"]); + assert_eq!(det.app_type, AppType::Php); + } + + #[test] + fn test_detect_empty_directory() { + let det = detect_with(&[]); + assert_eq!(det.app_type, AppType::Custom); + } + + #[test] + fn test_detect_priority_node_over_static() { + let det = detect_with(&["package.json", "index.html"]); + assert_eq!( + det.app_type, + AppType::Node, + "package.json (priority 9) should beat index.html (priority 5)" + ); + } + + #[test] + fn test_detect_existing_dockerfile_flag() { + let det = detect_with(&["Dockerfile", "package.json"]); + assert!(det.has_dockerfile); + assert_eq!(det.app_type, AppType::Node); + } + + #[test] + fn test_detect_existing_compose_flag() { + let det = detect_with(&["docker-compose.yml", "index.html"]); + assert!(det.has_compose); + } + + #[test] + fn test_detect_env_file_flag() { + let det = detect_with(&[".env", "index.html"]); + assert!(det.has_env_file); + } + + #[test] + fn test_detection_to_app_type_via_from() { + let detection = ProjectDetection { + app_type: AppType::Node, + ..Default::default() + }; + let app_type = AppType::from(&detection); + assert_eq!(app_type, AppType::Node); + } +} diff --git a/src/cli/error.rs b/src/cli/error.rs new file mode 100644 index 00000000..ea11f5d7 --- /dev/null +++ b/src/cli/error.rs @@ -0,0 +1,353 @@ +use std::fmt; +use std::path::PathBuf; + +use crate::cli::config_parser::DeployTarget; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// CliError — unified error hierarchy for all CLI operations +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug)] +pub enum CliError { + // Config errors + ConfigNotFound { path: PathBuf }, + ConfigParseFailed { source: serde_yaml::Error }, + ConfigValidation(String), + EnvVarNotFound { var_name: String }, + + // Detection errors + DetectionFailed { path: PathBuf, reason: String }, + + // Generator errors + GeneratorError(String), + DockerfileExists { path: PathBuf }, + + // Deployment errors + DeployFailed { target: DeployTarget, reason: String }, + LoginRequired { feature: String }, + CloudProviderMissing, + ServerHostMissing, + + // Runtime errors + ContainerRuntimeUnavailable, + CommandFailed { command: String, exit_code: i32 }, + + // Auth errors + AuthFailed(String), + TokenExpired, + + // AI errors + AiNotConfigured, + AiProviderError { provider: String, message: String }, + + // Proxy errors + ProxyConfigFailed(String), + + // IO errors + Io(std::io::Error), +} + +impl fmt::Display for CliError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ConfigNotFound { path } => { + write!(f, "Configuration file not found: {}", path.display()) + } + Self::ConfigParseFailed { source } => { + write!(f, "Failed to parse stacker.yml: {source}") + } + Self::ConfigValidation(msg) => { + write!(f, "Configuration validation error: {msg}") + } + Self::EnvVarNotFound { var_name } => { + write!(f, "Environment variable not found: ${var_name}") + } + Self::DetectionFailed { path, reason } => { + write!( + f, + "Project detection failed in {}: {reason}", + path.display() + ) + } + Self::GeneratorError(msg) => { + write!(f, "Generator error: {msg}") + } + Self::DockerfileExists { path } => { + write!( + f, + "Dockerfile already exists: {}. Use --force to overwrite.", + path.display() + ) + } + Self::DeployFailed { target, reason } => { + write!(f, "Deployment to {target} failed: {reason}") + } + Self::LoginRequired { feature } => { + write!(f, "Login required for {feature}. Run: stacker login") + } + Self::CloudProviderMissing => { + write!(f, "Cloud provider is required for cloud deployment. Set deploy.cloud.provider in stacker.yml") + } + Self::ServerHostMissing => { + write!(f, "Server host is required for server deployment. Set deploy.server.host in stacker.yml") + } + Self::ContainerRuntimeUnavailable => { + write!( + f, + "Docker is not running. Install Docker or start the Docker daemon." + ) + } + Self::CommandFailed { command, exit_code } => { + write!(f, "Command '{command}' failed with exit code {exit_code}") + } + Self::AuthFailed(msg) => { + write!(f, "Authentication failed: {msg}") + } + Self::TokenExpired => { + write!(f, "Authentication token expired. Run: stacker login") + } + Self::AiNotConfigured => { + write!( + f, + "AI is not configured in stacker.yml.\n\ + Quick fix: run `stacker init --with-ai` (in your project root),\n\ + or add this section:\n\ + ai:\n\ + enabled: true\n\ + provider: ollama # openai | anthropic | ollama | custom\n\ + timeout: 300\n\ + tasks: [\"dockerfile\", \"compose\"]" + ) + } + Self::AiProviderError { provider, message } => { + write!(f, "AI provider '{provider}' error: {message}") + } + Self::ProxyConfigFailed(msg) => { + write!(f, "Proxy configuration failed: {msg}") + } + Self::Io(err) => { + write!(f, "I/O error: {err}") + } + } + } +} + +impl std::error::Error for CliError {} + +impl From for CliError { + fn from(err: std::io::Error) -> Self { + Self::Io(err) + } +} + +impl From for CliError { + fn from(err: serde_yaml::Error) -> Self { + Self::ConfigParseFailed { source: err } + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ValidationIssue — structured validation results +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ValidationIssue { + pub severity: Severity, + pub code: String, + pub message: String, + pub field: Option, +} + +impl fmt::Display for ValidationIssue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.field { + Some(field) => write!(f, "[{}] {}: {} ({})", self.severity, self.code, self.message, field), + None => write!(f, "[{}] {}: {}", self.severity, self.code, self.message), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Severity { + Error, + Warning, + Info, +} + +impl fmt::Display for Severity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Error => write!(f, "error"), + Self::Warning => write!(f, "warning"), + Self::Info => write!(f, "info"), + } + } +} + +impl Default for Severity { + fn default() -> Self { + Self::Info + } +} + +use serde::{Deserialize, Serialize}; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests — Phase 0 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + + #[test] + fn test_cli_error_display_config_not_found() { + let err = CliError::ConfigNotFound { + path: PathBuf::from("/tmp/stacker.yml"), + }; + let msg = format!("{err}"); + assert!( + msg.contains("Configuration file not found"), + "Expected 'Configuration file not found' in: {msg}" + ); + assert!( + msg.contains("/tmp/stacker.yml"), + "Expected path in: {msg}" + ); + } + + #[test] + fn test_cli_error_display_env_var_not_found() { + let err = CliError::EnvVarNotFound { + var_name: "DB_PASSWORD".to_string(), + }; + let msg = format!("{err}"); + assert!( + msg.contains("DB_PASSWORD"), + "Expected var name in: {msg}" + ); + } + + #[test] + fn test_cli_error_display_login_required() { + let err = CliError::LoginRequired { + feature: "cloud deploy".to_string(), + }; + let msg = format!("{err}"); + assert!( + msg.contains("cloud deploy"), + "Expected feature name in: {msg}" + ); + assert!( + msg.contains("stacker login"), + "Expected command hint in: {msg}" + ); + } + + #[test] + fn test_cli_error_display_container_runtime_unavailable() { + let err = CliError::ContainerRuntimeUnavailable; + let msg = format!("{err}"); + assert!( + msg.contains("Docker is not running"), + "Expected docker message in: {msg}" + ); + } + + #[test] + fn test_cli_error_display_generator_error() { + let err = CliError::GeneratorError("base_image is required".to_string()); + let msg = format!("{err}"); + assert!( + msg.contains("base_image is required"), + "Expected reason in: {msg}" + ); + } + + #[test] + fn test_cli_error_from_io_error() { + let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing"); + let cli_err = CliError::from(io_err); + assert!( + matches!(cli_err, CliError::Io(_)), + "Expected CliError::Io variant" + ); + } + + #[test] + fn test_cli_error_from_yaml_error() { + let yaml_result: Result = + serde_yaml::from_str("{{invalid: yaml:"); + let yaml_err = yaml_result.unwrap_err(); + let cli_err = CliError::from(yaml_err); + assert!( + matches!(cli_err, CliError::ConfigParseFailed { .. }), + "Expected CliError::ConfigParseFailed variant" + ); + } + + #[test] + fn test_severity_display() { + assert_eq!(format!("{}", Severity::Error), "error"); + assert_eq!(format!("{}", Severity::Warning), "warning"); + assert_eq!(format!("{}", Severity::Info), "info"); + } + + #[test] + fn test_severity_default_is_info() { + assert_eq!(Severity::default(), Severity::Info); + } + + #[test] + fn test_severity_serde_roundtrip() { + let json = serde_json::to_string(&Severity::Warning).unwrap(); + assert_eq!(json, "\"warning\""); + let parsed: Severity = serde_json::from_str(&json).unwrap(); + assert_eq!(parsed, Severity::Warning); + } + + #[test] + fn test_validation_issue_display_with_field() { + let issue = ValidationIssue { + severity: Severity::Error, + code: "E001".to_string(), + message: "port conflict".to_string(), + field: Some("services[0].ports".to_string()), + }; + let msg = format!("{issue}"); + assert!(msg.contains("[error]"), "Expected severity in: {msg}"); + assert!(msg.contains("E001"), "Expected code in: {msg}"); + assert!(msg.contains("port conflict"), "Expected message in: {msg}"); + assert!(msg.contains("services[0].ports"), "Expected field in: {msg}"); + } + + #[test] + fn test_validation_issue_display_without_field() { + let issue = ValidationIssue { + severity: Severity::Warning, + code: "W001".to_string(), + message: "no healthcheck".to_string(), + field: None, + }; + let msg = format!("{issue}"); + assert!(msg.contains("[warning]"), "Expected severity in: {msg}"); + assert!(!msg.contains("("), "Expected no field parens in: {msg}"); + } + + #[test] + fn test_validation_issue_serialize() { + let issue = ValidationIssue { + severity: Severity::Error, + code: "E001".to_string(), + message: "missing field".to_string(), + field: Some("name".to_string()), + }; + let json = serde_json::to_value(&issue).unwrap(); + assert_eq!(json["severity"], "error"); + assert_eq!(json["code"], "E001"); + assert_eq!(json["message"], "missing field"); + assert_eq!(json["field"], "name"); + } +} diff --git a/src/cli/generator/compose.rs b/src/cli/generator/compose.rs new file mode 100644 index 00000000..6d0600c1 --- /dev/null +++ b/src/cli/generator/compose.rs @@ -0,0 +1,646 @@ +use std::collections::HashMap; +use std::convert::TryFrom; +use std::fmt; +use std::path::Path; + +use crate::cli::config_parser::{ + AppType, ProxyType, ServiceDefinition, StackerConfig, +}; +use crate::cli::error::CliError; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ComposeService — represents one service in docker-compose +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone)] +pub struct ComposeService { + pub name: String, + pub image: Option, + pub build_context: Option, + pub dockerfile: Option, + pub ports: Vec, + pub environment: HashMap, + pub volumes: Vec, + pub depends_on: Vec, + pub restart: String, + pub networks: Vec, +} + +impl Default for ComposeService { + fn default() -> Self { + Self { + name: String::new(), + image: None, + build_context: None, + dockerfile: None, + ports: Vec::new(), + environment: HashMap::new(), + volumes: Vec::new(), + depends_on: Vec::new(), + restart: "unless-stopped".to_string(), + networks: vec!["app-network".to_string()], + } + } +} + +/// Convert a `ServiceDefinition` (from stacker.yml) into a `ComposeService`. +impl From<&ServiceDefinition> for ComposeService { + fn from(svc: &ServiceDefinition) -> Self { + Self { + name: svc.name.clone(), + image: Some(svc.image.clone()), + ports: svc.ports.clone(), + environment: svc.environment.clone(), + volumes: svc.volumes.clone(), + depends_on: svc.depends_on.clone(), + ..Default::default() + } + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ComposeDefinition — full docker-compose document +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[derive(Debug, Clone)] +pub struct ComposeDefinition { + pub services: Vec, + pub networks: Vec, + pub volumes: Vec, +} + +impl Default for ComposeDefinition { + fn default() -> Self { + Self { + services: Vec::new(), + networks: vec!["app-network".to_string()], + volumes: Vec::new(), + } + } +} + +/// Build a complete `ComposeDefinition` from a `StackerConfig`. +/// +/// This converts the config's app + services into docker-compose services, +/// sets up networking, and optionally adds a proxy service. +impl TryFrom<&StackerConfig> for ComposeDefinition { + type Error = CliError; + + fn try_from(config: &StackerConfig) -> Result { + let mut compose = ComposeDefinition::default(); + let mut named_volumes: Vec = Vec::new(); + + // --- Main app service --- + let app_service = build_app_service(config); + compose.services.push(app_service); + + // --- Additional services (databases, caches, etc.) --- + for svc_def in &config.services { + let svc = ComposeService::from(svc_def); + + // Collect named volumes + for vol in &svc.volumes { + if let Some(named) = extract_named_volume(vol) { + if !named_volumes.contains(&named) { + named_volumes.push(named); + } + } + } + + compose.services.push(svc); + } + + // --- Proxy service --- + if let Some(proxy_svc) = build_proxy_service(config) { + compose.services.push(proxy_svc); + } + + // --- Set top-level volumes --- + compose.volumes = named_volumes; + + Ok(compose) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Internal construction helpers (SRP: each builds one aspect) +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +fn build_app_service(config: &StackerConfig) -> ComposeService { + let mut svc = ComposeService { + name: "app".to_string(), + ..Default::default() + }; + + // If user specifies an image directly, use it. + if let Some(ref img) = config.app.image { + svc.image = Some(img.clone()); + } else { + // Build from context + svc.build_context = Some(config.app.path.to_string_lossy().to_string()); + if let Some(ref df) = config.app.dockerfile { + svc.dockerfile = Some(df.to_string_lossy().to_string()); + } + } + + // Ports: use explicit ports if provided, otherwise default from app type + if config.app.ports.is_empty() { + let default_port = default_port_for_app_type(config.app.app_type); + svc.ports.push(format!("{}:{}", default_port, default_port)); + } else { + svc.ports.extend(config.app.ports.clone()); + } + + // Volumes from app section + svc.volumes.extend(config.app.volumes.clone()); + + // Merge environment: top-level env first, then app-level (app wins) + for (k, v) in &config.env { + svc.environment.insert(k.clone(), v.clone()); + } + for (k, v) in &config.app.environment { + svc.environment.insert(k.clone(), v.clone()); + } + + svc +} + +fn default_port_for_app_type(app_type: AppType) -> u16 { + match app_type { + AppType::Static => 80, + AppType::Node => 3000, + AppType::Python => 8000, + AppType::Rust => 8080, + AppType::Go => 8080, + AppType::Php => 9000, + AppType::Custom => 8080, + } +} + +fn build_proxy_service(config: &StackerConfig) -> Option { + match config.proxy.proxy_type { + ProxyType::Nginx => { + let mut svc = ComposeService { + name: "nginx".to_string(), + image: Some("nginx:alpine".to_string()), + ports: vec!["80:80".to_string(), "443:443".to_string()], + depends_on: vec!["app".to_string()], + ..Default::default() + }; + svc.volumes + .push("./nginx/conf.d:/etc/nginx/conf.d:ro".to_string()); + Some(svc) + } + ProxyType::NginxProxyManager => { + let svc = ComposeService { + name: "proxy-manager".to_string(), + image: Some("jc21/nginx-proxy-manager:latest".to_string()), + ports: vec![ + "80:80".to_string(), + "443:443".to_string(), + "81:81".to_string(), + ], + depends_on: vec!["app".to_string()], + ..Default::default() + }; + Some(svc) + } + ProxyType::Traefik => { + let mut svc = ComposeService { + name: "traefik".to_string(), + image: Some("traefik:v2.10".to_string()), + ports: vec!["80:80".to_string(), "443:443".to_string()], + depends_on: vec!["app".to_string()], + ..Default::default() + }; + svc.volumes + .push("/var/run/docker.sock:/var/run/docker.sock:ro".to_string()); + Some(svc) + } + ProxyType::None => None, + } +} + +/// Extract a named volume from a volume string like "my-data:/var/lib/data". +/// Returns `None` for bind mounts (starting with `.` or `/`). +fn extract_named_volume(vol_str: &str) -> Option { + let parts: Vec<&str> = vol_str.split(':').collect(); + if parts.len() >= 2 { + let source = parts[0]; + if !source.starts_with('.') && !source.starts_with('/') { + return Some(source.to_string()); + } + } + None +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Rendering — produce docker-compose YAML string +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +impl ComposeDefinition { + /// Render as a docker-compose YAML string (hand-built for readability). + pub fn render(&self) -> String { + let mut out = String::new(); + + out.push_str("services:\n"); + + for svc in &self.services { + out.push_str(&format!(" {}:\n", svc.name)); + + if let Some(ref img) = svc.image { + out.push_str(&format!(" image: {}\n", img)); + } + + if let Some(ref ctx) = svc.build_context { + out.push_str(" build:\n"); + out.push_str(&format!(" context: {}\n", ctx)); + if let Some(ref df) = svc.dockerfile { + out.push_str(&format!(" dockerfile: {}\n", df)); + } + } + + if !svc.ports.is_empty() { + out.push_str(" ports:\n"); + for p in &svc.ports { + out.push_str(&format!(" - \"{}\"\n", p)); + } + } + + if !svc.environment.is_empty() { + out.push_str(" environment:\n"); + let mut keys: Vec<&String> = svc.environment.keys().collect(); + keys.sort(); + for k in keys { + out.push_str(&format!(" {}: \"{}\"\n", k, svc.environment[k])); + } + } + + if !svc.volumes.is_empty() { + out.push_str(" volumes:\n"); + for v in &svc.volumes { + out.push_str(&format!(" - \"{}\"\n", v)); + } + } + + if !svc.depends_on.is_empty() { + out.push_str(" depends_on:\n"); + for d in &svc.depends_on { + out.push_str(&format!(" - {}\n", d)); + } + } + + out.push_str(&format!(" restart: {}\n", svc.restart)); + + if !svc.networks.is_empty() { + out.push_str(" networks:\n"); + for n in &svc.networks { + out.push_str(&format!(" - {}\n", n)); + } + } + + out.push('\n'); + } + + // Top-level networks + if !self.networks.is_empty() { + out.push_str("networks:\n"); + for n in &self.networks { + out.push_str(&format!(" {}:\n driver: bridge\n", n)); + } + out.push('\n'); + } + + // Top-level volumes + if !self.volumes.is_empty() { + out.push_str("volumes:\n"); + for v in &self.volumes { + out.push_str(&format!(" {}:\n", v)); + } + out.push('\n'); + } + + out + } + + /// Write docker-compose YAML to a file path. + pub fn write_to(&self, path: &Path, overwrite: bool) -> Result<(), CliError> { + if !overwrite && path.exists() { + return Err(CliError::GeneratorError(format!( + "Compose file already exists: {}", + path.display() + ))); + } + let content = self.render(); + std::fs::write(path, content)?; + Ok(()) + } +} + +impl fmt::Display for ComposeDefinition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.render()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::config_parser::{ + AppSource, ConfigBuilder, DeployConfig, ProxyConfig, SslMode, + }; + use std::collections::HashMap; + + fn minimal_config(app_type: AppType) -> StackerConfig { + ConfigBuilder::new() + .name("test-app") + .app_type(app_type) + .build() + .unwrap() + } + + #[test] + fn test_compose_from_minimal_static_config() { + let config = minimal_config(AppType::Static); + let compose = ComposeDefinition::try_from(&config).unwrap(); + assert_eq!(compose.services.len(), 1); + assert_eq!(compose.services[0].name, "app"); + assert!(compose.services[0].ports.contains(&"80:80".to_string())); + } + + #[test] + fn test_compose_from_node_config() { + let config = minimal_config(AppType::Node); + let compose = ComposeDefinition::try_from(&config).unwrap(); + assert!(compose.services[0].ports.contains(&"3000:3000".to_string())); + } + + #[test] + fn test_compose_from_python_config_port() { + let config = minimal_config(AppType::Python); + let compose = ComposeDefinition::try_from(&config).unwrap(); + assert!(compose.services[0].ports.contains(&"8000:8000".to_string())); + } + + #[test] + fn test_compose_app_service_uses_build_context() { + let config = minimal_config(AppType::Static); + let compose = ComposeDefinition::try_from(&config).unwrap(); + let app = &compose.services[0]; + assert!(app.build_context.is_some()); + assert!(app.image.is_none()); + } + + #[test] + fn test_compose_app_service_with_explicit_image() { + let config = ConfigBuilder::new() + .name("img-app") + .app_type(AppType::Custom) + .app_image("myregistry/myapp:latest") + .build() + .unwrap(); + let compose = ComposeDefinition::try_from(&config).unwrap(); + let app = &compose.services[0]; + assert_eq!(app.image.as_deref(), Some("myregistry/myapp:latest")); + assert!(app.build_context.is_none()); + } + + #[test] + fn test_compose_includes_additional_services() { + let svc = ServiceDefinition { + name: "postgres".into(), + image: "postgres:16".into(), + ports: vec!["5432:5432".into()], + environment: HashMap::from([("POSTGRES_PASSWORD".into(), "secret".into())]), + volumes: vec!["pg-data:/var/lib/postgresql/data".into()], + depends_on: Vec::new(), + }; + let config = ConfigBuilder::new() + .name("with-db") + .app_type(AppType::Node) + .add_service(svc) + .build() + .unwrap(); + + let compose = ComposeDefinition::try_from(&config).unwrap(); + assert_eq!(compose.services.len(), 2); + assert_eq!(compose.services[1].name, "postgres"); + assert!(compose.volumes.contains(&"pg-data".to_string())); + } + + #[test] + fn test_compose_nginx_proxy_added() { + let config = ConfigBuilder::new() + .name("proxied-app") + .app_type(AppType::Node) + .proxy(ProxyConfig { + proxy_type: ProxyType::Nginx, + auto_detect: true, + domains: Vec::new(), + config: None, + }) + .build() + .unwrap(); + + let compose = ComposeDefinition::try_from(&config).unwrap(); + let proxy_svc = compose.services.iter().find(|s| s.name == "nginx"); + assert!(proxy_svc.is_some()); + let proxy = proxy_svc.unwrap(); + assert_eq!(proxy.image.as_deref(), Some("nginx:alpine")); + assert!(proxy.ports.contains(&"80:80".to_string())); + assert!(proxy.ports.contains(&"443:443".to_string())); + assert!(proxy.depends_on.contains(&"app".to_string())); + } + + #[test] + fn test_compose_no_proxy_when_none() { + let config = minimal_config(AppType::Static); + let compose = ComposeDefinition::try_from(&config).unwrap(); + // Only app service, no proxy + assert_eq!(compose.services.len(), 1); + } + + #[test] + fn test_compose_traefik_proxy() { + let config = ConfigBuilder::new() + .name("traefik-app") + .app_type(AppType::Python) + .proxy(ProxyConfig { + proxy_type: ProxyType::Traefik, + auto_detect: true, + domains: Vec::new(), + config: None, + }) + .build() + .unwrap(); + + let compose = ComposeDefinition::try_from(&config).unwrap(); + let traefik = compose.services.iter().find(|s| s.name == "traefik"); + assert!(traefik.is_some()); + assert_eq!( + traefik.unwrap().image.as_deref(), + Some("traefik:v2.10") + ); + } + + #[test] + fn test_compose_render_omits_obsolete_version() { + let config = minimal_config(AppType::Static); + let compose = ComposeDefinition::try_from(&config).unwrap(); + let yaml = compose.render(); + assert!(!yaml.contains("version:")); + } + + #[test] + fn test_compose_render_contains_services_block() { + let config = minimal_config(AppType::Node); + let compose = ComposeDefinition::try_from(&config).unwrap(); + let yaml = compose.render(); + assert!(yaml.contains("services:")); + assert!(yaml.contains(" app:")); + } + + #[test] + fn test_compose_render_contains_networks() { + let config = minimal_config(AppType::Static); + let compose = ComposeDefinition::try_from(&config).unwrap(); + let yaml = compose.render(); + assert!(yaml.contains("networks:")); + assert!(yaml.contains("app-network")); + } + + #[test] + fn test_compose_render_contains_volumes_section() { + let svc = ServiceDefinition { + name: "redis".into(), + image: "redis:7".into(), + ports: Vec::new(), + environment: HashMap::new(), + volumes: vec!["redis-data:/data".into()], + depends_on: Vec::new(), + }; + let config = ConfigBuilder::new() + .name("with-vol") + .app_type(AppType::Static) + .add_service(svc) + .build() + .unwrap(); + + let compose = ComposeDefinition::try_from(&config).unwrap(); + let yaml = compose.render(); + assert!(yaml.contains("volumes:")); + assert!(yaml.contains(" redis-data:")); + } + + #[test] + fn test_compose_env_vars_propagated_to_app() { + let config = ConfigBuilder::new() + .name("env-app") + .app_type(AppType::Node) + .env("NODE_ENV", "production") + .env("LOG_LEVEL", "debug") + .build() + .unwrap(); + + let compose = ComposeDefinition::try_from(&config).unwrap(); + let app = &compose.services[0]; + assert_eq!(app.environment.get("NODE_ENV").map(|s| s.as_str()), Some("production")); + assert_eq!(app.environment.get("LOG_LEVEL").map(|s| s.as_str()), Some("debug")); + } + + #[test] + fn test_compose_display_matches_render() { + let config = minimal_config(AppType::Static); + let compose = ComposeDefinition::try_from(&config).unwrap(); + assert_eq!(format!("{}", compose), compose.render()); + } + + #[test] + fn test_compose_write_refuses_overwrite() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("docker-compose.yml"); + std::fs::write(&path, "existing").unwrap(); + + let config = minimal_config(AppType::Static); + let compose = ComposeDefinition::try_from(&config).unwrap(); + let result = compose.write_to(&path, false); + assert!(result.is_err()); + } + + #[test] + fn test_compose_write_creates_file() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("docker-compose.yml"); + + let config = minimal_config(AppType::Node); + let compose = ComposeDefinition::try_from(&config).unwrap(); + compose.write_to(&path, false).unwrap(); + + let written = std::fs::read_to_string(&path).unwrap(); + assert!(written.contains("app:")); + assert!(written.contains("3000:3000")); + } + + #[test] + fn test_service_definition_to_compose_service() { + let svc_def = ServiceDefinition { + name: "mysql".into(), + image: "mysql:8".into(), + ports: vec!["3306:3306".into()], + environment: HashMap::from([("MYSQL_ROOT_PASSWORD".into(), "pass".into())]), + volumes: vec!["mysql-data:/var/lib/mysql".into()], + depends_on: Vec::new(), + }; + + let compose_svc = ComposeService::from(&svc_def); + assert_eq!(compose_svc.name, "mysql"); + assert_eq!(compose_svc.image.as_deref(), Some("mysql:8")); + assert!(compose_svc.ports.contains(&"3306:3306".to_string())); + assert_eq!( + compose_svc.environment.get("MYSQL_ROOT_PASSWORD").map(|s| s.as_str()), + Some("pass") + ); + } + + #[test] + fn test_extract_named_volume_returns_name() { + assert_eq!( + extract_named_volume("pg-data:/var/lib/postgresql/data"), + Some("pg-data".to_string()) + ); + } + + #[test] + fn test_extract_named_volume_ignores_bind_mount() { + assert_eq!(extract_named_volume("./data:/app/data"), None); + assert_eq!(extract_named_volume("/host/path:/container"), None); + } + + #[test] + fn test_compose_nginx_proxy_manager() { + let config = ConfigBuilder::new() + .name("npm-app") + .app_type(AppType::Static) + .proxy(ProxyConfig { + proxy_type: ProxyType::NginxProxyManager, + auto_detect: true, + domains: Vec::new(), + config: None, + }) + .build() + .unwrap(); + + let compose = ComposeDefinition::try_from(&config).unwrap(); + let npm = compose + .services + .iter() + .find(|s| s.name == "proxy-manager"); + assert!(npm.is_some()); + let npm = npm.unwrap(); + assert!(npm.ports.contains(&"81:81".to_string())); // NPM admin port + } +} diff --git a/src/cli/generator/dockerfile.rs b/src/cli/generator/dockerfile.rs new file mode 100644 index 00000000..cb7d9f6f --- /dev/null +++ b/src/cli/generator/dockerfile.rs @@ -0,0 +1,437 @@ +use std::fmt; + +use crate::cli::config_parser::AppType; +use crate::cli::error::CliError; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// DockerfileBuilder — generates Dockerfiles from AppType +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// A fluent builder for producing multi-stage Dockerfile contents. +#[derive(Debug, Clone)] +pub struct DockerfileBuilder { + base_image: String, + work_dir: String, + copy_sources: Vec<(String, String)>, + run_commands: Vec, + expose_ports: Vec, + cmd: Vec, + entrypoint: Option>, + env_vars: Vec<(String, String)>, + labels: Vec<(String, String)>, + stages: Vec, +} + +/// A named build stage for multi-stage builds. +#[derive(Debug, Clone)] +struct Stage { + name: String, + base_image: String, + commands: Vec, +} + +impl Default for DockerfileBuilder { + fn default() -> Self { + Self { + base_image: "alpine:3.18".to_string(), + work_dir: "/app".to_string(), + copy_sources: Vec::new(), + run_commands: Vec::new(), + expose_ports: Vec::new(), + cmd: Vec::new(), + entrypoint: None, + env_vars: Vec::new(), + labels: Vec::new(), + stages: Vec::new(), + } + } +} + +/// Create a `DockerfileBuilder` pre-configured with sensible defaults for a given +/// `AppType`. This is the primary entry point for generating Dockerfiles. +impl From for DockerfileBuilder { + fn from(app_type: AppType) -> Self { + match app_type { + AppType::Static => Self::default() + .base_image("nginx:alpine") + .copy(".", "/usr/share/nginx/html") + .expose(80), + + AppType::Node => Self::default() + .base_image("node:20-alpine") + .work_dir("/app") + .copy("package*.json", "./") + .run("npm ci --production") + .copy(".", ".") + .expose(3000) + .cmd_str("node server.js"), + + AppType::Python => Self::default() + .base_image("python:3.12-slim") + .work_dir("/app") + .copy("requirements.txt", "./") + .run("pip install --no-cache-dir -r requirements.txt") + .copy(".", ".") + .expose(8000) + .cmd_str("python -m uvicorn main:app --host 0.0.0.0 --port 8000"), + + AppType::Rust => Self::default() + .base_image("rust:1.77-alpine") + .work_dir("/app") + .run("apk add --no-cache musl-dev") + .copy(".", ".") + .run("cargo build --release") + .expose(8080) + .cmd_str("./target/release/app"), + + AppType::Go => Self::default() + .base_image("golang:1.22-alpine") + .work_dir("/app") + .copy("go.mod", "./") + .copy("go.sum", "./") + .run("go mod download") + .copy(".", ".") + .run("go build -o /app/server .") + .expose(8080) + .cmd_str("/app/server"), + + AppType::Php => Self::default() + .base_image("php:8.3-fpm-alpine") + .work_dir("/var/www/html") + .run("docker-php-ext-install pdo pdo_mysql") + .copy(".", ".") + .expose(9000), + + AppType::Custom => Self::default(), + } + } +} + +impl DockerfileBuilder { + pub fn new() -> Self { + Self::default() + } + + pub fn base_image>(mut self, image: S) -> Self { + self.base_image = image.into(); + self + } + + pub fn work_dir>(mut self, dir: S) -> Self { + self.work_dir = dir.into(); + self + } + + pub fn copy>(mut self, src: S, dest: S) -> Self { + self.copy_sources.push((src.into(), dest.into())); + self + } + + pub fn run>(mut self, cmd: S) -> Self { + self.run_commands.push(cmd.into()); + self + } + + pub fn expose(mut self, port: u16) -> Self { + self.expose_ports.push(port); + self + } + + pub fn cmd(mut self, parts: Vec) -> Self { + self.cmd = parts; + self + } + + /// Convenience: sets CMD from a simple string, splitting by whitespace. + pub fn cmd_str>(mut self, cmd: S) -> Self { + self.cmd = cmd + .into() + .split_whitespace() + .map(|s| s.to_string()) + .collect(); + self + } + + pub fn entrypoint(mut self, parts: Vec) -> Self { + self.entrypoint = Some(parts); + self + } + + pub fn env, V: Into>(mut self, key: K, value: V) -> Self { + self.env_vars.push((key.into(), value.into())); + self + } + + pub fn label, V: Into>(mut self, key: K, value: V) -> Self { + self.labels.push((key.into(), value.into())); + self + } + + /// Render the Dockerfile contents as a `String`. + pub fn build(&self) -> String { + let mut lines: Vec = Vec::new(); + + // FROM + lines.push(format!("FROM {}", self.base_image)); + lines.push(String::new()); + + // LABELS + for (k, v) in &self.labels { + lines.push(format!("LABEL {}=\"{}\"", k, v)); + } + if !self.labels.is_empty() { + lines.push(String::new()); + } + + // WORKDIR + if self.work_dir != "/" { + lines.push(format!("WORKDIR {}", self.work_dir)); + lines.push(String::new()); + } + + // ENV + for (k, v) in &self.env_vars { + lines.push(format!("ENV {}={}", k, v)); + } + if !self.env_vars.is_empty() { + lines.push(String::new()); + } + + // Interleaved COPY and RUN in order + // + // We track the order: first emit copy_sources and run_commands by + // recording the insertion order. For simplicity in the builder we + // output all COPYs first, then all RUNs, followed by EXPOSE/CMD. + for (src, dest) in &self.copy_sources { + lines.push(format!("COPY {} {}", src, dest)); + } + if !self.copy_sources.is_empty() { + lines.push(String::new()); + } + + for cmd in &self.run_commands { + lines.push(format!("RUN {}", cmd)); + } + if !self.run_commands.is_empty() { + lines.push(String::new()); + } + + // EXPOSE + for port in &self.expose_ports { + lines.push(format!("EXPOSE {}", port)); + } + if !self.expose_ports.is_empty() { + lines.push(String::new()); + } + + // ENTRYPOINT + if let Some(ep) = &self.entrypoint { + let quoted: Vec = ep.iter().map(|p| format!("\"{}\"", p)).collect(); + lines.push(format!("ENTRYPOINT [{}]", quoted.join(", "))); + } + + // CMD + if !self.cmd.is_empty() { + let quoted: Vec = self.cmd.iter().map(|p| format!("\"{}\"", p)).collect(); + lines.push(format!("CMD [{}]", quoted.join(", "))); + } + + // Trim trailing blank lines + while lines.last().map_or(false, |l| l.is_empty()) { + lines.pop(); + } + + lines.push(String::new()); // final newline + lines.join("\n") + } + + /// Write Dockerfile to a path. Returns error if file already exists. + pub fn write_to( + &self, + path: &std::path::Path, + overwrite: bool, + ) -> Result<(), CliError> { + if !overwrite && path.exists() { + return Err(CliError::DockerfileExists { + path: path.to_path_buf(), + }); + } + let content = self.build(); + std::fs::write(path, content)?; + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Display — pretty-print Dockerfile to stdout +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +impl fmt::Display for DockerfileBuilder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.build()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_static_dockerfile_from_app_type() { + let builder = DockerfileBuilder::from(AppType::Static); + let content = builder.build(); + assert!(content.contains("FROM nginx:alpine")); + assert!(content.contains("COPY . /usr/share/nginx/html")); + assert!(content.contains("EXPOSE 80")); + } + + #[test] + fn test_node_dockerfile_from_app_type() { + let builder = DockerfileBuilder::from(AppType::Node); + let content = builder.build(); + assert!(content.contains("FROM node:20-alpine")); + assert!(content.contains("COPY package*.json ./")); + assert!(content.contains("RUN npm ci --production")); + assert!(content.contains("EXPOSE 3000")); + assert!(content.contains("CMD [\"node\", \"server.js\"]")); + } + + #[test] + fn test_python_dockerfile_from_app_type() { + let builder = DockerfileBuilder::from(AppType::Python); + let content = builder.build(); + assert!(content.contains("FROM python:3.12-slim")); + assert!(content.contains("RUN pip install")); + assert!(content.contains("EXPOSE 8000")); + } + + #[test] + fn test_rust_dockerfile_from_app_type() { + let builder = DockerfileBuilder::from(AppType::Rust); + let content = builder.build(); + assert!(content.contains("FROM rust:1.77-alpine")); + assert!(content.contains("RUN cargo build --release")); + assert!(content.contains("EXPOSE 8080")); + } + + #[test] + fn test_go_dockerfile_from_app_type() { + let builder = DockerfileBuilder::from(AppType::Go); + let content = builder.build(); + assert!(content.contains("FROM golang:1.22-alpine")); + assert!(content.contains("RUN go build")); + assert!(content.contains("EXPOSE 8080")); + } + + #[test] + fn test_php_dockerfile_from_app_type() { + let builder = DockerfileBuilder::from(AppType::Php); + let content = builder.build(); + assert!(content.contains("FROM php:8.3-fpm-alpine")); + assert!(content.contains("EXPOSE 9000")); + } + + #[test] + fn test_custom_dockerfile_is_bare() { + let builder = DockerfileBuilder::from(AppType::Custom); + let content = builder.build(); + assert!(content.contains("FROM alpine:3.18")); + // Custom has no COPY/RUN/CMD by default + assert!(!content.contains("COPY")); + assert!(!content.contains("RUN")); + assert!(!content.contains("CMD")); + } + + #[test] + fn test_builder_fluent_chaining() { + let content = DockerfileBuilder::new() + .base_image("ubuntu:22.04") + .work_dir("/opt/app") + .env("APP_ENV", "production") + .copy("src", "/opt/app/src") + .run("apt-get update && apt-get install -y curl") + .expose(8443) + .cmd_str("./start.sh") + .build(); + + assert!(content.contains("FROM ubuntu:22.04")); + assert!(content.contains("WORKDIR /opt/app")); + assert!(content.contains("ENV APP_ENV=production")); + assert!(content.contains("COPY src /opt/app/src")); + assert!(content.contains("RUN apt-get update")); + assert!(content.contains("EXPOSE 8443")); + assert!(content.contains("CMD [\"./start.sh\"]")); + } + + #[test] + fn test_builder_label() { + let content = DockerfileBuilder::new() + .label("maintainer", "team@example.com") + .build(); + assert!(content.contains("LABEL maintainer=\"team@example.com\"")); + } + + #[test] + fn test_builder_entrypoint() { + let content = DockerfileBuilder::new() + .entrypoint(vec!["./run.sh".into(), "--flag".into()]) + .build(); + assert!(content.contains("ENTRYPOINT [\"./run.sh\", \"--flag\"]")); + } + + #[test] + fn test_display_trait_matches_build() { + let builder = DockerfileBuilder::from(AppType::Static); + assert_eq!(format!("{}", builder), builder.build()); + } + + #[test] + fn test_write_to_refuses_overwrite_by_default() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("Dockerfile"); + std::fs::write(&path, "existing").unwrap(); + + let builder = DockerfileBuilder::from(AppType::Static); + let result = builder.write_to(&path, false); + assert!(result.is_err()); + } + + #[test] + fn test_write_to_allows_overwrite_when_flag_set() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("Dockerfile"); + std::fs::write(&path, "old").unwrap(); + + let builder = DockerfileBuilder::from(AppType::Static); + builder.write_to(&path, true).unwrap(); + + let written = std::fs::read_to_string(&path).unwrap(); + assert!(written.contains("FROM nginx:alpine")); + } + + #[test] + fn test_write_to_creates_new_file() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("Dockerfile"); + + let builder = DockerfileBuilder::from(AppType::Node); + builder.write_to(&path, false).unwrap(); + + let written = std::fs::read_to_string(&path).unwrap(); + assert!(written.contains("FROM node:20-alpine")); + } + + #[test] + fn test_multiple_expose_ports() { + let content = DockerfileBuilder::new() + .expose(80) + .expose(443) + .build(); + assert!(content.contains("EXPOSE 80")); + assert!(content.contains("EXPOSE 443")); + } +} diff --git a/src/cli/generator/mod.rs b/src/cli/generator/mod.rs new file mode 100644 index 00000000..5a6c10c5 --- /dev/null +++ b/src/cli/generator/mod.rs @@ -0,0 +1,2 @@ +pub mod compose; +pub mod dockerfile; diff --git a/src/cli/install_runner.rs b/src/cli/install_runner.rs new file mode 100644 index 00000000..4a81f37c --- /dev/null +++ b/src/cli/install_runner.rs @@ -0,0 +1,1734 @@ +use std::path::{Path, PathBuf}; + +use crate::cli::config_parser::{CloudOrchestrator, DeployTarget, StackerConfig}; +use crate::cli::credentials::CredentialsManager; +use crate::cli::error::CliError; +use crate::cli::stacker_client::{self, StackerClient}; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Constants +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Default Docker image for the install container (Terraform + Ansible). +pub const DEFAULT_INSTALL_IMAGE: &str = "trydirect/install-service:latest"; + +/// Mount point for stacker.yml inside the install container. +pub const CONTAINER_CONFIG_PATH: &str = "/app/stacker.yml"; + +/// Mount point for the compose file inside the install container. +pub const CONTAINER_COMPOSE_PATH: &str = "/app/docker-compose.yml"; + +/// Mount point for SSH keys inside the install container. +pub const CONTAINER_SSH_KEY_PATH: &str = "/root/.ssh/id_rsa"; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// CommandExecutor — abstraction for running shell commands (DIP) +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Result of executing a command. +#[derive(Debug, Clone)] +pub struct CommandOutput { + pub exit_code: i32, + pub stdout: String, + pub stderr: String, +} + +impl CommandOutput { + pub fn success(&self) -> bool { + self.exit_code == 0 + } +} + +/// Abstraction over shell command execution. +/// +/// Production: `ShellExecutor` runs commands via `std::process::Command`. +/// Tests: `MockExecutor` records commands for assertion without side effects. +pub trait CommandExecutor: Send + Sync { + fn execute(&self, program: &str, args: &[&str]) -> Result; +} + +/// Production executor — actually runs docker commands. +pub struct ShellExecutor; + +impl CommandExecutor for ShellExecutor { + fn execute(&self, program: &str, args: &[&str]) -> Result { + let output = std::process::Command::new(program) + .args(args) + .output() + .map_err(|e| CliError::CommandFailed { + command: format!("{} {} — {}", program, args.join(" "), e), + exit_code: -1, + })?; + + Ok(CommandOutput { + exit_code: output.status.code().unwrap_or(-1), + stdout: String::from_utf8_lossy(&output.stdout).to_string(), + stderr: String::from_utf8_lossy(&output.stderr).to_string(), + }) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// DeployContext — everything needed for a deployment +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Aggregated deployment context passed to strategies. +#[derive(Debug, Clone)] +pub struct DeployContext { + /// Path to the stacker.yml config file. + pub config_path: PathBuf, + + /// Path to the generated docker-compose.yml. + pub compose_path: PathBuf, + + /// Working directory of the project. + pub project_dir: PathBuf, + + /// Whether this is a dry-run (plan) or real deployment (apply). + pub dry_run: bool, + + /// Install container image override. + pub image: Option, + + /// Remote deploy overrides from CLI flags. + pub project_name_override: Option, + pub key_name_override: Option, + pub server_name_override: Option, +} + +impl DeployContext { + pub fn install_image(&self) -> &str { + self.image.as_deref().unwrap_or(DEFAULT_INSTALL_IMAGE) + } +} + +/// Outcome of a successful deployment. +#[derive(Debug, Clone)] +pub struct DeployResult { + pub target: DeployTarget, + pub message: String, + pub server_ip: Option, +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// DeployStrategy — strategy pattern for deployment targets (OCP + DIP) +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Each deployment target implements this trait. +/// New targets can be added without modifying existing code (OCP). +pub trait DeployStrategy { + fn validate(&self, config: &StackerConfig) -> Result<(), CliError>; + fn deploy( + &self, + config: &StackerConfig, + context: &DeployContext, + executor: &dyn CommandExecutor, + ) -> Result; + fn destroy( + &self, + config: &StackerConfig, + context: &DeployContext, + executor: &dyn CommandExecutor, + ) -> Result<(), CliError>; +} + +/// Factory: map `DeployTarget` to its strategy implementation. +pub fn strategy_for(target: &DeployTarget) -> Box { + match target { + DeployTarget::Local => Box::new(LocalDeploy), + DeployTarget::Cloud => Box::new(CloudDeploy), + DeployTarget::Server => Box::new(ServerDeploy), + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// LocalDeploy — docker compose up/down +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +pub struct LocalDeploy; + +impl DeployStrategy for LocalDeploy { + fn validate(&self, _config: &StackerConfig) -> Result<(), CliError> { + // Local deploy only requires Docker to be available; + // that check happens at command level before calling deploy(). + Ok(()) + } + + fn deploy( + &self, + config: &StackerConfig, + context: &DeployContext, + executor: &dyn CommandExecutor, + ) -> Result { + let compose_path = context.compose_path.to_string_lossy().to_string(); + + let mut args: Vec = vec!["compose".into()]; + if let Some(ref env_file) = config.env_file { + let env_file_path = if env_file.is_absolute() { + env_file.clone() + } else { + context.project_dir.join(env_file) + }; + args.push("--env-file".into()); + args.push(env_file_path.to_string_lossy().to_string()); + } + args.push("-f".into()); + args.push(compose_path.clone()); + + if context.dry_run { + args.push("config".into()); + } else { + args.push("up".into()); + args.push("-d".into()); + args.push("--build".into()); + } + + let args_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + let output = executor.execute("docker", &args_refs)?; + + if !output.stdout.trim().is_empty() { + println!("{}", output.stdout); + } + if !output.stderr.trim().is_empty() { + eprintln!("{}", output.stderr); + } + + if !output.success() { + return Err(CliError::DeployFailed { + target: DeployTarget::Local, + reason: format!("docker compose failed: {}", output.stderr.trim()), + }); + } + + let action = if context.dry_run { "validated" } else { "started" }; + Ok(DeployResult { + target: DeployTarget::Local, + message: format!("Local deployment {} successfully", action), + server_ip: None, + }) + } + + fn destroy( + &self, + config: &StackerConfig, + context: &DeployContext, + executor: &dyn CommandExecutor, + ) -> Result<(), CliError> { + let compose_path = context.compose_path.to_string_lossy().to_string(); + let mut args: Vec = vec!["compose".into()]; + if let Some(ref env_file) = config.env_file { + let env_file_path = if env_file.is_absolute() { + env_file.clone() + } else { + context.project_dir.join(env_file) + }; + args.push("--env-file".into()); + args.push(env_file_path.to_string_lossy().to_string()); + } + args.push("-f".into()); + args.push(compose_path); + args.push("down".into()); + args.push("--volumes".into()); + let args_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + let output = executor.execute("docker", &args_refs)?; + + if !output.success() { + return Err(CliError::DeployFailed { + target: DeployTarget::Local, + reason: format!("docker compose down failed: {}", output.stderr.trim()), + }); + } + + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// InstallContainerCommand — builds `docker run` for the install container +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Builder for `docker run` commands that launch the install container +/// with Terraform + Ansible for cloud/server deployments. +/// +/// Modeled after the existing install service docker-compose mounts +/// and the `ConfigureProxyCommandRequest` pattern in `forms/status_panel.rs`. +#[derive(Debug, Clone)] +pub struct InstallContainerCommand { + image: String, + volume_mounts: Vec<(String, String)>, + env_vars: Vec<(String, String)>, + action: InstallAction, + remove_after: bool, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum InstallAction { + Plan, + Apply, + Destroy, +} + +impl InstallAction { + pub fn as_str(&self) -> &'static str { + match self { + Self::Plan => "plan", + Self::Apply => "apply", + Self::Destroy => "destroy", + } + } +} + +impl InstallContainerCommand { + /// Create a new builder with the given image (or default). + pub fn new(image: Option<&str>) -> Self { + Self { + image: image.unwrap_or(DEFAULT_INSTALL_IMAGE).to_string(), + volume_mounts: Vec::new(), + env_vars: Vec::new(), + action: InstallAction::Apply, + remove_after: true, + } + } + + /// Mount a host path into the container. + pub fn mount(mut self, host_path: impl AsRef, container_path: &str) -> Self { + self.volume_mounts.push(( + host_path.as_ref().to_string_lossy().to_string(), + container_path.to_string(), + )); + self + } + + /// Add an environment variable. + pub fn env(mut self, key: &str, value: &str) -> Self { + self.env_vars.push((key.to_string(), value.to_string())); + self + } + + /// Set the action to perform (plan, apply, destroy). + pub fn action(mut self, action: InstallAction) -> Self { + self.action = action; + self + } + + /// Whether to remove the container after exit (--rm). Default: true. + pub fn remove_after(mut self, remove: bool) -> Self { + self.remove_after = remove; + self + } + + /// Build the argument list for `docker run`. + pub fn build_args(&self) -> Vec { + let mut args = vec!["run".to_string()]; + + if self.remove_after { + args.push("--rm".to_string()); + } + + for (host, container) in &self.volume_mounts { + args.push("-v".to_string()); + args.push(format!("{}:{}", host, container)); + } + + for (key, value) in &self.env_vars { + args.push("-e".to_string()); + args.push(format!("{}={}", key, value)); + } + + args.push(self.image.clone()); + args.push(self.action.as_str().to_string()); + + args + } + + /// Build from a `StackerConfig` and `DeployContext`, setting up + /// standard mounts and environment variables. + pub fn from_config( + config: &StackerConfig, + context: &DeployContext, + action: InstallAction, + ) -> Self { + let mut cmd = Self::new(Some(context.install_image())).action(action); + + // Mount stacker.yml + cmd = cmd.mount(&context.config_path, CONTAINER_CONFIG_PATH); + + // Mount compose file + cmd = cmd.mount(&context.compose_path, CONTAINER_COMPOSE_PATH); + + // Set project name + cmd = cmd.env("PROJECT_NAME", &config.name); + + // Cloud-specific configuration + if let Some(ref cloud) = config.deploy.cloud { + cmd = cmd.env("CLOUD_PROVIDER", &cloud.provider.to_string()); + + if let Some(ref region) = cloud.region { + cmd = cmd.env("CLOUD_REGION", region); + } + + if let Some(ref size) = cloud.size { + cmd = cmd.env("CLOUD_SIZE", size); + } + + // Mount SSH key if specified + if let Some(ref ssh_key) = cloud.ssh_key { + cmd = cmd.mount(ssh_key, CONTAINER_SSH_KEY_PATH); + } + } + + // Server-specific configuration + if let Some(ref server) = config.deploy.server { + cmd = cmd.env("SERVER_HOST", &server.host); + cmd = cmd.env("SERVER_USER", &server.user); + cmd = cmd.env("SERVER_PORT", &server.port.to_string()); + + if let Some(ref ssh_key) = server.ssh_key { + cmd = cmd.mount(ssh_key, CONTAINER_SSH_KEY_PATH); + } + } + + cmd + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// CloudDeploy — install container with Terraform/Ansible +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +pub struct CloudDeploy; + +impl DeployStrategy for CloudDeploy { + fn validate(&self, config: &StackerConfig) -> Result<(), CliError> { + if config.deploy.cloud.is_none() { + return Err(CliError::CloudProviderMissing); + } + + Ok(()) + } + + fn deploy( + &self, + config: &StackerConfig, + context: &DeployContext, + executor: &dyn CommandExecutor, + ) -> Result { + if let Some(cloud_cfg) = &config.deploy.cloud { + if cloud_cfg.orchestrator == CloudOrchestrator::Remote { + let cred_manager = CredentialsManager::with_default_store(); + let creds = cred_manager.require_valid_token("remote cloud orchestrator deployment")?; + + if context.dry_run { + return Ok(DeployResult { + target: DeployTarget::Cloud, + message: "Remote cloud deploy dry-run validated payload and credentials".to_string(), + server_ip: None, + }); + } + + // Resolve project name: CLI flag > config project.identity > config name + let project_name = context + .project_name_override + .clone() + .or_else(|| config.project.identity.clone()) + .unwrap_or_else(|| config.name.clone()); + + // Resolve cloud key name: CLI flag > config deploy.cloud.key + let key_name = context + .key_name_override + .clone() + .or_else(|| cloud_cfg.key.clone()); + + // Resolve server name: CLI flag > config deploy.cloud.server + let server_name = context + .server_name_override + .clone() + .or_else(|| cloud_cfg.server.clone()); + + let base_url = normalize_stacker_server_url( + stacker_client::DEFAULT_STACKER_URL, + ); + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .map_err(|e| CliError::DeployFailed { + target: DeployTarget::Cloud, + reason: format!("Failed to initialize async runtime: {}", e), + })?; + + let response = rt.block_on(async { + let client = StackerClient::new(&base_url, &creds.access_token); + + // Step 1: Resolve or auto-create project + eprintln!(" Resolving project '{}'...", project_name); + let project_body = stacker_client::build_project_body(config); + let project = match client.find_project_by_name(&project_name).await? { + Some(p) => { + eprintln!(" Found project '{}' (id={}), syncing metadata...", p.name, p.id); + let updated = client + .update_project(p.id, project_body) + .await?; + eprintln!(" Updated project '{}' (id={})", updated.name, updated.id); + updated + } + None => { + eprintln!(" Project '{}' not found, creating...", project_name); + let p = client + .create_project(&project_name, project_body) + .await?; + eprintln!(" Created project '{}' (id={})", p.name, p.id); + p + } + }; + + // Step 2: Resolve cloud credentials + let cloud_id = if let Some(key_ref) = &key_name { + // Look up saved cloud by provider name + eprintln!(" Looking up saved cloud key '{}'...", key_ref); + match client.find_cloud_by_provider(key_ref).await? { + Some(c) => { + eprintln!( + " Found cloud credentials (id={}, provider={})", + c.id, c.provider + ); + Some(c.id) + } + None => { + // Try saving current env-var creds under this provider + let provider_str = cloud_cfg.provider.to_string(); + let provider_code = provider_code_for_remote( + &provider_str, + ); + let env_creds = + resolve_remote_cloud_credentials(provider_code); + let cloud_token = env_creds + .get("cloud_token") + .and_then(|v| v.as_str()); + let cloud_key = env_creds + .get("cloud_key") + .and_then(|v| v.as_str()); + let cloud_secret = env_creds + .get("cloud_secret") + .and_then(|v| v.as_str()); + + if cloud_token.is_some() + || cloud_key.is_some() + || cloud_secret.is_some() + { + eprintln!( + " No saved cloud '{}', saving from env vars...", + key_ref + ); + let saved = client + .save_cloud( + provider_code, + cloud_token, + cloud_key, + cloud_secret, + ) + .await?; + eprintln!( + " Saved cloud credentials (id={})", + saved.id + ); + Some(saved.id) + } else { + return Err(CliError::DeployFailed { + target: DeployTarget::Cloud, + reason: format!( + "Cloud key '{}' not found on server and no cloud credentials in env vars (STACKER_CLOUD_TOKEN, HCLOUD_TOKEN, etc.)", + key_ref + ), + }); + } + } + } + } else { + // No key specified: try to find existing cloud creds for this provider, + // or pass creds directly in deploy form from env vars + let provider_str = cloud_cfg.provider.to_string(); + let provider_code = + provider_code_for_remote(&provider_str); + match client.find_cloud_by_provider(provider_code).await? { + Some(c) => { + eprintln!( + " Using saved cloud credentials (id={}, provider={})", + c.id, c.provider + ); + Some(c.id) + } + None => None, + } + }; + + // Step 3: Resolve server by name + let server_id = if let Some(srv_name) = &server_name { + eprintln!(" Looking up server '{}'...", srv_name); + match client.find_server_by_name(srv_name).await? { + Some(s) => { + eprintln!( + " Found server '{}' (id={})", + s.name.as_deref().unwrap_or("unnamed"), + s.id + ); + Some(s.id) + } + None => { + return Err(CliError::DeployFailed { + target: DeployTarget::Cloud, + reason: format!( + "Server '{}' not found. Create it on the Stacker server first or remove --server flag.", + srv_name + ), + }); + } + } + } else { + None + }; + + // Step 4: Build deploy form + let mut deploy_form = stacker_client::build_deploy_form(config); + if let Some(sid) = server_id { + if let Some(server_obj) = deploy_form.get_mut("server") { + if let Some(obj) = server_obj.as_object_mut() { + obj.insert( + "server_id".to_string(), + serde_json::Value::Number(sid.into()), + ); + } + } + } + + // Include env-var cloud creds in form if no saved cloud + if cloud_id.is_none() { + let provider_str = cloud_cfg.provider.to_string(); + let provider_code = + provider_code_for_remote(&provider_str); + let env_creds = resolve_remote_cloud_credentials(provider_code); + if let Some(cloud_obj) = deploy_form.get_mut("cloud") { + if let Some(obj) = cloud_obj.as_object_mut() { + for (k, v) in &env_creds { + obj.insert(k.clone(), v.clone()); + } + obj.insert( + "save_token".to_string(), + serde_json::Value::Bool(true), + ); + } + } + } + + // Step 5: Deploy + eprintln!(" Deploying project '{}' (id={})...", project_name, project.id); + let resp = client.deploy(project.id, cloud_id, deploy_form).await?; + + Ok(resp) + }).map_err(|e: CliError| e)?; + + let deploy_id = response + .meta + .as_ref() + .and_then(|m| m.get("deployment_id")) + .and_then(|v| v.as_i64()); + + let project_id = response.id; + + let mut message = format!( + "Cloud deployment requested via Stacker server (project='{}'", + project_name + ); + + if let Some(pid) = project_id { + message.push_str(&format!(", project_id={}", pid)); + } + if let Some(did) = deploy_id { + message.push_str(&format!(", deployment_id={}", did)); + } + message.push(')'); + + if let Some(srv) = &server_name { + message.push_str(&format!("; server='{}'", srv)); + } + if let Some(key) = &key_name { + message.push_str(&format!("; cloud_key='{}'", key)); + } + + return Ok(DeployResult { + target: DeployTarget::Cloud, + message, + server_ip: None, + }); + } + } + + let action = if context.dry_run { + InstallAction::Plan + } else { + InstallAction::Apply + }; + + let cmd = InstallContainerCommand::from_config(config, context, action); + let args = cmd.build_args(); + let args_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + + let output = executor.execute("docker", &args_refs)?; + + if !output.success() { + return Err(CliError::DeployFailed { + target: DeployTarget::Cloud, + reason: format!("Install container failed: {}", output.stderr.trim()), + }); + } + + let action_str = if context.dry_run { "plan completed" } else { "deployed" }; + Ok(DeployResult { + target: DeployTarget::Cloud, + message: format!("Cloud deployment {}", action_str), + server_ip: extract_server_ip(&output.stdout), + }) + } + + fn destroy( + &self, + config: &StackerConfig, + context: &DeployContext, + executor: &dyn CommandExecutor, + ) -> Result<(), CliError> { + if let Some(cloud_cfg) = &config.deploy.cloud { + if cloud_cfg.orchestrator == CloudOrchestrator::Remote { + return Err(CliError::DeployFailed { + target: DeployTarget::Cloud, + reason: "Remote cloud orchestrator destroy is not implemented yet. Use platform API/UI or switch deploy.cloud.orchestrator=local.".to_string(), + }); + } + } + + let cmd = InstallContainerCommand::from_config(config, context, InstallAction::Destroy); + let args = cmd.build_args(); + let args_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + + let output = executor.execute("docker", &args_refs)?; + + if !output.success() { + return Err(CliError::DeployFailed { + target: DeployTarget::Cloud, + reason: format!("Cloud destroy failed: {}", output.stderr.trim()), + }); + } + + Ok(()) + } +} + +pub fn provider_code_for_remote(config_provider: &str) -> &str { + match config_provider { + "hetzner" => "htz", + "digitalocean" => "do", + "aws" => "aws", + "linode" => "lo", + "vultr" => "vu", + _ => config_provider, + } +} + +#[allow(dead_code)] +fn normalize_user_service_base_url(raw: &str) -> String { + let mut url = raw.trim_end_matches('/').to_string(); + if url.ends_with("/server/user/auth/login") { + let len = url.len() - "/auth/login".len(); + return url[..len].to_string(); + } + + for suffix in ["/oauth_server/token", "/auth/login", "/login"] { + if url.ends_with(suffix) { + let len = url.len() - suffix.len(); + url = url[..len].to_string(); + break; + } + } + url +} + +/// Normalize the Stacker server URL from stored credentials. +/// Strips trailing slashes and known auth path suffixes to get the base API URL. +fn normalize_stacker_server_url(raw: &str) -> String { + let mut url = raw.trim_end_matches('/').to_string(); + // Strip known auth endpoints that might be stored as server_url + for suffix in ["/oauth_server/token", "/auth/login", "/server/user/auth/login", "/login", "/api"] { + if url.ends_with(suffix) { + let len = url.len() - suffix.len(); + url = url[..len].to_string(); + break; + } + } + url +} + +#[allow(dead_code)] +fn sanitize_stack_code(name: &str) -> String { + let mut out = String::new(); + let mut prev_dash = false; + for ch in name.chars() { + let c = ch.to_ascii_lowercase(); + if c.is_ascii_alphanumeric() { + out.push(c); + prev_dash = false; + } else if !prev_dash { + out.push('-'); + prev_dash = true; + } + } + let out = out.trim_matches('-').to_string(); + if out.is_empty() { + "app-stack".to_string() + } else { + out + } +} + +#[allow(dead_code)] +fn default_common_domain(project_name: &str) -> String { + format!("{}.example.com", sanitize_stack_code(project_name)) +} + +fn first_non_empty_env(keys: &[&str]) -> Option { + keys.iter().find_map(|key| { + std::env::var(key) + .ok() + .map(|v| v.trim().to_string()) + .filter(|v| !v.is_empty()) + }) +} + +fn resolve_remote_cloud_credentials(provider: &str) -> serde_json::Map { + let mut creds = serde_json::Map::new(); + + match provider { + "htz" => { + if let Some(token) = first_non_empty_env(&[ + "STACKER_CLOUD_TOKEN", + "STACKER_HETZNER_TOKEN", + "HETZNER_TOKEN", + "HCLOUD_TOKEN", + ]) { + creds.insert("cloud_token".to_string(), serde_json::Value::String(token)); + } + } + "do" => { + if let Some(token) = first_non_empty_env(&[ + "STACKER_CLOUD_TOKEN", + "STACKER_DIGITALOCEAN_TOKEN", + "DIGITALOCEAN_TOKEN", + "DO_API_TOKEN", + ]) { + creds.insert("cloud_token".to_string(), serde_json::Value::String(token)); + } + } + "lo" => { + if let Some(token) = first_non_empty_env(&[ + "STACKER_CLOUD_TOKEN", + "STACKER_LINODE_TOKEN", + "LINODE_TOKEN", + ]) { + creds.insert("cloud_token".to_string(), serde_json::Value::String(token)); + } + } + "vu" => { + if let Some(token) = first_non_empty_env(&[ + "STACKER_CLOUD_TOKEN", + "STACKER_VULTR_TOKEN", + "VULTR_TOKEN", + "VULTR_API_KEY", + ]) { + creds.insert("cloud_token".to_string(), serde_json::Value::String(token)); + } + } + "aws" => { + if let Some(key) = first_non_empty_env(&["STACKER_CLOUD_KEY", "AWS_ACCESS_KEY_ID"]) { + creds.insert("cloud_key".to_string(), serde_json::Value::String(key)); + } + if let Some(secret) = + first_non_empty_env(&["STACKER_CLOUD_SECRET", "AWS_SECRET_ACCESS_KEY"]) + { + creds.insert("cloud_secret".to_string(), serde_json::Value::String(secret)); + } + } + _ => {} + } + + creds +} + +#[allow(dead_code)] +fn build_remote_deploy_payload(config: &StackerConfig) -> serde_json::Value { + let cloud = config.deploy.cloud.as_ref(); + let provider = cloud + .map(|c| provider_code_for_remote(&c.provider.to_string()).to_string()) + .unwrap_or_else(|| "htz".to_string()); + let region = cloud.and_then(|c| c.region.clone()).unwrap_or_else(|| "nbg1".to_string()); + let server = cloud.and_then(|c| c.size.clone()).unwrap_or_else(|| "cx11".to_string()); + let stack_code = config + .project + .identity + .clone() + .map(|v| v.trim().to_string()) + .filter(|v| !v.is_empty()) + .unwrap_or_else(|| "custom-stack".to_string()); + let os = match provider.as_str() { + "do" => "docker-20-04", + _ => "ubuntu-22.04", + }; + + let mut payload = serde_json::json!({ + "provider": provider, + "region": region, + "server": server, + "os": os, + "ssl": "letsencrypt", + "commonDomain": default_common_domain(&config.name), + "domainList": {}, + "stack_code": stack_code, + "project_name": config.name, + "selected_plan": "free", + "payment_type": "subscription", + "subscriptions": [], + "vars": [], + "integrated_features": [], + "extended_features": [], + "save_token": true, + "custom": { + "project_name": config.name, + "custom_stack_code": sanitize_stack_code(&config.name), + "project_overview": format!("Generated by stacker-cli for {}", config.name) + } + }); + + if let Some(obj) = payload.as_object_mut() { + for (key, value) in resolve_remote_cloud_credentials(&provider) { + obj.insert(key, value); + } + } + + payload +} + +#[allow(dead_code)] +fn validate_remote_deploy_payload(payload: &serde_json::Value) -> Result<(), CliError> { + let required = [ + "provider", + "region", + "server", + "os", + "commonDomain", + "stack_code", + "selected_plan", + "payment_type", + "subscriptions", + ]; + + let mut missing = Vec::new(); + + for key in required { + match payload.get(key) { + Some(v) if !v.is_null() => { + if key == "subscriptions" && !v.is_array() { + missing.push("subscriptions(array)"); + } + if key == "stack_code" + && v + .as_str() + .map(|s| s.trim().is_empty()) + .unwrap_or(true) + { + missing.push("stack_code(non-empty)"); + } + } + _ => missing.push(key), + } + } + + if !missing.is_empty() { + let identity_hint = if missing + .iter() + .any(|item| item.contains("stack_code")) + { + " stack_code defaults to 'custom-stack'. Optionally set project.identity in stacker.yml to a registered catalog stack code." + } else { + "" + }; + Err(CliError::DeployFailed { + target: DeployTarget::Cloud, + reason: format!( + "Remote deploy payload is missing required fields: {}. Preferred flow: remove `deploy.cloud.remote_payload_file` and run `stacker deploy --target cloud` so payload is generated automatically. For advanced/debug use `stacker-cli config setup remote-payload`.{}", + missing.join(", "), + identity_hint + ), + }) + } else { + let mut invalid = Vec::new(); + + if let Some(domain) = payload.get("commonDomain").and_then(|v| v.as_str()) { + let normalized = domain.trim().to_ascii_lowercase(); + if normalized == "localhost" || !normalized.contains('.') { + invalid.push("commonDomain(valid domain required)"); + } + } + + let provider = payload + .get("provider") + .and_then(|v| v.as_str()) + .unwrap_or_default(); + + match provider { + "htz" | "lo" | "vu" => { + let has_token = payload + .get("cloud_token") + .and_then(|v| v.as_str()) + .map(|v| !v.trim().is_empty()) + .unwrap_or(false); + if !has_token { + invalid.push("cloud_token"); + } + } + "aws" => { + let has_key = payload + .get("cloud_key") + .and_then(|v| v.as_str()) + .map(|v| !v.trim().is_empty()) + .unwrap_or(false); + let has_secret = payload + .get("cloud_secret") + .and_then(|v| v.as_str()) + .map(|v| !v.trim().is_empty()) + .unwrap_or(false); + + if !has_key { + invalid.push("cloud_key"); + } + if !has_secret { + invalid.push("cloud_secret"); + } + } + _ => {} + } + + if invalid.is_empty() { + Ok(()) + } else { + Err(CliError::DeployFailed { + target: DeployTarget::Cloud, + reason: format!( + "Remote deploy payload has invalid/missing provider credentials: {}. Set env vars before deploy (e.g. STACKER_CLOUD_TOKEN or provider-specific token vars; for AWS use AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY).", + invalid.join(", ") + ), + }) + } + } +} + +#[allow(dead_code)] +fn persist_remote_payload_snapshot(project_dir: &Path, payload: &serde_json::Value) -> Option { + let stacker_dir = project_dir.join(".stacker"); + let snapshot_path = stacker_dir.join("remote-payload.last.json"); + + if let Err(err) = std::fs::create_dir_all(&stacker_dir) { + eprintln!( + "Warning: failed to create payload snapshot directory {}: {}", + stacker_dir.display(), + err + ); + return None; + } + + let payload_str = match serde_json::to_string_pretty(payload) { + Ok(s) => s, + Err(err) => { + eprintln!("Warning: failed to serialize remote payload snapshot: {}", err); + return None; + } + }; + + if let Err(err) = std::fs::write(&snapshot_path, payload_str) { + eprintln!( + "Warning: failed to write payload snapshot {}: {}", + snapshot_path.display(), + err + ); + return None; + } + + Some(snapshot_path) +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ServerDeploy — SSH + install container +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +pub struct ServerDeploy; + +impl DeployStrategy for ServerDeploy { + fn validate(&self, config: &StackerConfig) -> Result<(), CliError> { + if config.deploy.server.is_none() { + return Err(CliError::ServerHostMissing); + } + + Ok(()) + } + + fn deploy( + &self, + config: &StackerConfig, + context: &DeployContext, + executor: &dyn CommandExecutor, + ) -> Result { + let action = if context.dry_run { + InstallAction::Plan + } else { + InstallAction::Apply + }; + + let cmd = InstallContainerCommand::from_config(config, context, action); + let args = cmd.build_args(); + let args_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + + let output = executor.execute("docker", &args_refs)?; + + if !output.success() { + return Err(CliError::DeployFailed { + target: DeployTarget::Server, + reason: format!("Server deployment failed: {}", output.stderr.trim()), + }); + } + + let server_host = config + .deploy + .server + .as_ref() + .map(|s| s.host.clone()); + + let action_str = if context.dry_run { "plan completed" } else { "deployed" }; + Ok(DeployResult { + target: DeployTarget::Server, + message: format!("Server deployment {}", action_str), + server_ip: server_host, + }) + } + + fn destroy( + &self, + config: &StackerConfig, + context: &DeployContext, + executor: &dyn CommandExecutor, + ) -> Result<(), CliError> { + let cmd = InstallContainerCommand::from_config(config, context, InstallAction::Destroy); + let args = cmd.build_args(); + let args_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + + let output = executor.execute("docker", &args_refs)?; + + if !output.success() { + return Err(CliError::DeployFailed { + target: DeployTarget::Server, + reason: format!("Server destroy failed: {}", output.stderr.trim()), + }); + } + + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Helpers +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Try to extract a server IP from install container stdout. +/// Looks for lines like `server_ip = 1.2.3.4` (Terraform output format). +fn extract_server_ip(stdout: &str) -> Option { + for line in stdout.lines() { + let trimmed = line.trim(); + if trimmed.starts_with("server_ip") || trimmed.starts_with("public_ip") { + if let Some(value) = trimmed.split('=').nth(1) { + let ip = value.trim().trim_matches('"'); + if !ip.is_empty() { + return Some(ip.to_string()); + } + } + } + } + None +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Mutex; + use crate::cli::config_parser::{CloudConfig, CloudOrchestrator, CloudProvider, ConfigBuilder, ServerConfig}; + + // ── Mock executor ─────────────────────────────── + + struct MockExecutor { + recorded_calls: Mutex)>>, + exit_code: i32, + stdout: String, + stderr: String, + } + + impl MockExecutor { + fn success() -> Self { + Self { + recorded_calls: Mutex::new(Vec::new()), + exit_code: 0, + stdout: String::new(), + stderr: String::new(), + } + } + + #[allow(dead_code)] + fn success_with_stdout(stdout: &str) -> Self { + Self { + recorded_calls: Mutex::new(Vec::new()), + exit_code: 0, + stdout: stdout.to_string(), + stderr: String::new(), + } + } + + fn failure(stderr: &str) -> Self { + Self { + recorded_calls: Mutex::new(Vec::new()), + exit_code: 1, + stdout: String::new(), + stderr: stderr.to_string(), + } + } + + fn last_call(&self) -> (String, Vec) { + self.recorded_calls.lock().unwrap().last().cloned().unwrap() + } + + fn last_args(&self) -> Vec { + self.last_call().1 + } + } + + impl CommandExecutor for MockExecutor { + fn execute(&self, program: &str, args: &[&str]) -> Result { + self.recorded_calls.lock().unwrap().push(( + program.to_string(), + args.iter().map(|s| s.to_string()).collect(), + )); + + Ok(CommandOutput { + exit_code: self.exit_code, + stdout: self.stdout.clone(), + stderr: self.stderr.clone(), + }) + } + } + + // Helper to join args as a single string for easier assertion. + fn args_as_string(args: &[String]) -> String { + args.join(" ") + } + + fn sample_cloud_config() -> StackerConfig { + ConfigBuilder::new().name("test-cloud-app") + .deploy_target(DeployTarget::Cloud) + .cloud(CloudConfig { + provider: CloudProvider::Hetzner, + orchestrator: CloudOrchestrator::Local, + region: Some("fsn1".to_string()), + size: Some("cx21".to_string()), + install_image: None, + remote_payload_file: None, + ssh_key: Some(PathBuf::from("/home/user/.ssh/id_ed25519")), + key: None, + server: None, + }) + .build() + .unwrap() + } + + #[test] + fn test_normalize_user_service_base_url_from_token_endpoint() { + let url = normalize_user_service_base_url("https://api.try.direct/oauth_server/token"); + assert_eq!(url, "https://api.try.direct"); + } + + #[test] + fn test_normalize_user_service_base_url_from_direct_login_endpoint() { + let url = normalize_user_service_base_url("https://dev.try.direct/server/user/auth/login"); + assert_eq!(url, "https://dev.try.direct/server/user"); + } + + #[test] + fn test_provider_code_for_remote_hetzner() { + assert_eq!(provider_code_for_remote("hetzner"), "htz"); + assert_eq!(provider_code_for_remote("aws"), "aws"); + assert_eq!(provider_code_for_remote("linode"), "lo"); + assert_eq!(provider_code_for_remote("vultr"), "vu"); + } + + #[test] + fn test_build_remote_deploy_payload_contains_required_fields() { + let cfg = sample_cloud_config(); + let payload = build_remote_deploy_payload(&cfg); + assert!(payload.get("provider").is_some()); + assert!(payload.get("region").is_some()); + assert!(payload.get("server").is_some()); + assert!(payload.get("os").is_some()); + assert!(payload.get("commonDomain").is_some()); + assert!(payload.get("selected_plan").is_some()); + assert!(payload.get("payment_type").is_some()); + assert!(payload.get("subscriptions").is_some()); + assert!(payload.get("stack_code").is_some()); + assert_eq!( + payload.get("stack_code").and_then(|v| v.as_str()), + Some("custom-stack") + ); + } + + #[test] + fn test_build_remote_deploy_payload_uses_project_identity_when_set() { + let cfg = ConfigBuilder::new() + .name("test-cloud-app") + .project_identity("registered-stack-code") + .deploy_target(DeployTarget::Cloud) + .cloud(CloudConfig { + provider: CloudProvider::Hetzner, + orchestrator: CloudOrchestrator::Local, + region: Some("fsn1".to_string()), + size: Some("cx21".to_string()), + install_image: None, + remote_payload_file: None, + ssh_key: Some(PathBuf::from("/home/user/.ssh/id_ed25519")), + key: None, + server: None, + }) + .build() + .unwrap(); + + let payload = build_remote_deploy_payload(&cfg); + assert_eq!( + payload.get("stack_code").and_then(|v| v.as_str()), + Some("registered-stack-code") + ); + } + + #[test] + fn test_validate_remote_deploy_payload_accepts_generated_payload() { + std::env::set_var("STACKER_CLOUD_TOKEN", "test-token-value"); + let cfg = sample_cloud_config(); + let payload = build_remote_deploy_payload(&cfg); + let result = validate_remote_deploy_payload(&payload); + std::env::remove_var("STACKER_CLOUD_TOKEN"); + assert!(result.is_ok()); + } + + #[test] + fn test_validate_remote_deploy_payload_rejects_missing_common_domain() { + let payload = serde_json::json!({ + "provider": "htz", + "region": "nbg1", + "server": "cx11", + "os": "ubuntu-22.04", + "stack_code": "demo", + "selected_plan": "free", + "payment_type": "subscription", + "subscriptions": [] + }); + + let err = validate_remote_deploy_payload(&payload).unwrap_err(); + let msg = err.to_string(); + assert!(msg.contains("commonDomain")); + } + + #[test] + fn test_validate_remote_deploy_payload_rejects_empty_stack_code() { + let payload = serde_json::json!({ + "provider": "htz", + "region": "nbg1", + "server": "cx11", + "os": "ubuntu-22.04", + "commonDomain": "example.com", + "stack_code": "", + "selected_plan": "free", + "payment_type": "subscription", + "subscriptions": [], + "cloud_token": "token" + }); + + let err = validate_remote_deploy_payload(&payload).unwrap_err(); + let msg = err.to_string(); + assert!(msg.contains("stack_code")); + } + + #[test] + fn test_persist_remote_payload_snapshot_writes_file() { + let dir = tempfile::TempDir::new().unwrap(); + let payload = serde_json::json!({ + "provider": "htz", + "region": "nbg1", + "server": "cx11", + "os": "ubuntu-22.04", + "commonDomain": "localhost", + "stack_code": "demo-stack", + "selected_plan": "free", + "payment_type": "subscription", + "subscriptions": [] + }); + + let path = persist_remote_payload_snapshot(dir.path(), &payload).unwrap(); + assert!(path.exists()); + + let raw = std::fs::read_to_string(path).unwrap(); + let parsed: serde_json::Value = serde_json::from_str(&raw).unwrap(); + assert_eq!(parsed.get("provider").and_then(|v| v.as_str()), Some("htz")); + } + + fn sample_server_config() -> StackerConfig { + ConfigBuilder::new().name("test-server-app") + .deploy_target(DeployTarget::Server) + .server(ServerConfig { + host: "192.168.1.100".to_string(), + user: "deploy".to_string(), + ssh_key: Some(PathBuf::from("/home/user/.ssh/id_rsa")), + port: 22, + }) + .build() + .unwrap() + } + + fn sample_context(dry_run: bool) -> DeployContext { + DeployContext { + config_path: PathBuf::from("/project/stacker.yml"), + compose_path: PathBuf::from("/project/docker-compose.yml"), + project_dir: PathBuf::from("/project"), + dry_run, + image: None, + project_name_override: None, + key_name_override: None, + server_name_override: None, + } + } + + // ── Phase 6 tests ─────────────────────────────── + + #[test] + fn test_build_run_command_with_cloud_config() { + let config = sample_cloud_config(); + let context = sample_context(false); + let cmd = InstallContainerCommand::from_config(&config, &context, InstallAction::Apply); + let args = args_as_string(&cmd.build_args()); + + assert!(args.contains("-v /project/stacker.yml:/app/stacker.yml")); + assert!(args.contains("-v /project/docker-compose.yml:/app/docker-compose.yml")); + assert!(args.contains("-e CLOUD_PROVIDER=hetzner")); + assert!(args.contains("-e CLOUD_REGION=fsn1")); + assert!(args.contains("-e PROJECT_NAME=test-cloud-app")); + } + + #[test] + fn test_run_command_mounts_stacker_yml() { + let config = sample_cloud_config(); + let context = sample_context(false); + let cmd = InstallContainerCommand::from_config(&config, &context, InstallAction::Apply); + let args = cmd.build_args(); + + let mount_idx = args.iter().position(|a| a == "-v").unwrap(); + let mount_val = &args[mount_idx + 1]; + assert!(mount_val.contains("stacker.yml:/app/stacker.yml")); + } + + #[test] + fn test_run_command_mounts_ssh_key() { + let config = sample_cloud_config(); + let context = sample_context(false); + let cmd = InstallContainerCommand::from_config(&config, &context, InstallAction::Apply); + let args = args_as_string(&cmd.build_args()); + + assert!(args.contains("-v /home/user/.ssh/id_ed25519:/root/.ssh/id_rsa")); + } + + #[test] + fn test_run_command_plan_mode() { + let config = sample_cloud_config(); + let context = sample_context(true); + let cmd = InstallContainerCommand::from_config(&config, &context, InstallAction::Plan); + let args = cmd.build_args(); + + let last = args.last().unwrap(); + assert_eq!(last, "plan"); + assert!(!args.contains(&"apply".to_string())); + } + + #[test] + fn test_run_command_apply_mode() { + let config = sample_cloud_config(); + let context = sample_context(false); + let cmd = InstallContainerCommand::from_config(&config, &context, InstallAction::Apply); + let args = cmd.build_args(); + + let last = args.last().unwrap(); + assert_eq!(last, "apply"); + assert!(!args.contains(&"plan".to_string())); + } + + #[test] + fn test_install_container_image_tag() { + let cmd = InstallContainerCommand::new(None); + let args = cmd.build_args(); + + assert!(args.contains(&DEFAULT_INSTALL_IMAGE.to_string())); + } + + // ── Additional tests ──────────────────────────── + + #[test] + fn test_install_container_custom_image() { + let cmd = InstallContainerCommand::new(Some("custom/installer:v2")); + let args = cmd.build_args(); + + assert!(args.contains(&"custom/installer:v2".to_string())); + assert!(!args.contains(&DEFAULT_INSTALL_IMAGE.to_string())); + } + + #[test] + fn test_deploy_context_default_image() { + let ctx = sample_context(false); + assert_eq!(ctx.install_image(), DEFAULT_INSTALL_IMAGE); + } + + #[test] + fn test_deploy_context_custom_image() { + let ctx = DeployContext { + config_path: PathBuf::from("/p/stacker.yml"), + compose_path: PathBuf::from("/p/docker-compose.yml"), + project_dir: PathBuf::from("/p"), + dry_run: false, + image: Some("mycompany/install:v3".to_string()), + project_name_override: None, + key_name_override: None, + server_name_override: None, + }; + assert_eq!(ctx.install_image(), "mycompany/install:v3"); + } + + #[test] + fn test_local_deploy_dry_run() { + let config = ConfigBuilder::new().name("local-app").build().unwrap(); + let context = sample_context(true); + let executor = MockExecutor::success(); + let strategy = LocalDeploy; + + let result = strategy.deploy(&config, &context, &executor).unwrap(); + assert_eq!(result.target, DeployTarget::Local); + assert!(result.message.contains("validated")); + + let args = executor.last_args(); + assert!(args.contains(&"config".to_string())); + assert!(!args.contains(&"up".to_string())); + } + + #[test] + fn test_local_deploy_apply() { + let config = ConfigBuilder::new().name("local-app").build().unwrap(); + let context = sample_context(false); + let executor = MockExecutor::success(); + let strategy = LocalDeploy; + + let result = strategy.deploy(&config, &context, &executor).unwrap(); + assert_eq!(result.target, DeployTarget::Local); + assert!(result.message.contains("started")); + + let args = executor.last_args(); + assert!(args.contains(&"up".to_string())); + assert!(args.contains(&"-d".to_string())); + assert!(args.contains(&"--build".to_string())); + } + + #[test] + fn test_local_deploy_failure() { + let config = ConfigBuilder::new().name("local-app").build().unwrap(); + let context = sample_context(false); + let executor = MockExecutor::failure("service failed to start"); + let strategy = LocalDeploy; + + let result = strategy.deploy(&config, &context, &executor); + assert!(result.is_err()); + } + + #[test] + fn test_local_destroy() { + let config = ConfigBuilder::new().name("local-app").build().unwrap(); + let context = sample_context(false); + let executor = MockExecutor::success(); + let strategy = LocalDeploy; + + strategy.destroy(&config, &context, &executor).unwrap(); + + let args = executor.last_args(); + assert!(args.contains(&"down".to_string())); + assert!(args.contains(&"--volumes".to_string())); + } + + #[test] + fn test_local_deploy_uses_env_file_when_configured() { + let config = ConfigBuilder::new() + .name("local-app") + .env_file(".env") + .build() + .unwrap(); + let context = sample_context(true); + let executor = MockExecutor::success(); + let strategy = LocalDeploy; + + strategy.deploy(&config, &context, &executor).unwrap(); + + let args = executor.last_args(); + assert!(args.contains(&"--env-file".to_string())); + assert!(args.contains(&"/project/.env".to_string())); + } + + #[test] + fn test_cloud_deploy_validates_provider() { + let config = ConfigBuilder::new().name("no-cloud").build().unwrap(); + let strategy = CloudDeploy; + let result = strategy.validate(&config); + assert!(result.is_err()); + } + + #[test] + fn test_cloud_deploy_has_provider_passes() { + let config = sample_cloud_config(); + let strategy = CloudDeploy; + assert!(strategy.validate(&config).is_ok()); + } + + #[test] + fn test_cloud_deploy_runs_install_container() { + let config = sample_cloud_config(); + let context = sample_context(false); + let executor = MockExecutor::success(); + let strategy = CloudDeploy; + + let result = strategy.deploy(&config, &context, &executor).unwrap(); + assert_eq!(result.target, DeployTarget::Cloud); + + let (program, args) = executor.last_call(); + assert_eq!(program, "docker"); + assert!(args.contains(&"run".to_string())); + assert!(args.contains(&DEFAULT_INSTALL_IMAGE.to_string())); + assert!(args.contains(&"apply".to_string())); + } + + #[test] + fn test_cloud_deploy_dry_run_uses_plan() { + let config = sample_cloud_config(); + let context = sample_context(true); + let executor = MockExecutor::success(); + let strategy = CloudDeploy; + + strategy.deploy(&config, &context, &executor).unwrap(); + + let args = executor.last_args(); + assert!(args.contains(&"plan".to_string())); + assert!(!args.contains(&"apply".to_string())); + } + + #[test] + fn test_server_deploy_validates_host() { + let config = ConfigBuilder::new().name("no-server").build().unwrap(); + let strategy = ServerDeploy; + let result = strategy.validate(&config); + assert!(result.is_err()); + } + + #[test] + fn test_server_deploy_has_host_passes() { + let config = sample_server_config(); + let strategy = ServerDeploy; + assert!(strategy.validate(&config).is_ok()); + } + + #[test] + fn test_server_deploy_sets_env_vars() { + let config = sample_server_config(); + let context = sample_context(false); + let cmd = InstallContainerCommand::from_config(&config, &context, InstallAction::Apply); + let args = args_as_string(&cmd.build_args()); + + assert!(args.contains("-e SERVER_HOST=192.168.1.100")); + assert!(args.contains("-e SERVER_USER=deploy")); + assert!(args.contains("-e SERVER_PORT=22")); + } + + #[test] + fn test_extract_server_ip_from_terraform_output() { + let stdout = "Apply complete!\n\nOutputs:\n\nserver_ip = \"203.0.113.42\"\n"; + assert_eq!(extract_server_ip(stdout), Some("203.0.113.42".to_string())); + } + + #[test] + fn test_extract_server_ip_public_ip() { + let stdout = "public_ip = 10.0.0.5\n"; + assert_eq!(extract_server_ip(stdout), Some("10.0.0.5".to_string())); + } + + #[test] + fn test_extract_server_ip_none() { + assert_eq!(extract_server_ip("no ip here"), None); + } + + #[test] + fn test_strategy_for_factory() { + // Verify the factory returns something for each variant (no panic). + let _ = strategy_for(&DeployTarget::Local); + let _ = strategy_for(&DeployTarget::Cloud); + let _ = strategy_for(&DeployTarget::Server); + } + + #[test] + fn test_install_action_as_str() { + assert_eq!(InstallAction::Plan.as_str(), "plan"); + assert_eq!(InstallAction::Apply.as_str(), "apply"); + assert_eq!(InstallAction::Destroy.as_str(), "destroy"); + } + + #[test] + fn test_command_output_success() { + let output = CommandOutput { + exit_code: 0, + stdout: "ok".to_string(), + stderr: String::new(), + }; + assert!(output.success()); + + let output = CommandOutput { + exit_code: 1, + stdout: String::new(), + stderr: "fail".to_string(), + }; + assert!(!output.success()); + } + + #[test] + fn test_install_command_remove_after_default() { + let cmd = InstallContainerCommand::new(None); + let args = cmd.build_args(); + assert!(args.contains(&"--rm".to_string())); + } + + #[test] + fn test_install_command_no_remove() { + let cmd = InstallContainerCommand::new(None).remove_after(false); + let args = cmd.build_args(); + assert!(!args.contains(&"--rm".to_string())); + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 00000000..5167f51e --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,10 @@ +pub mod ai_client; +pub mod ai_scanner; +pub mod config_parser; +pub mod credentials; +pub mod detector; +pub mod error; +pub mod generator; +pub mod install_runner; +pub mod proxy_manager; +pub mod stacker_client; diff --git a/src/cli/proxy_manager.rs b/src/cli/proxy_manager.rs new file mode 100644 index 00000000..06e6d5b4 --- /dev/null +++ b/src/cli/proxy_manager.rs @@ -0,0 +1,523 @@ +use std::collections::HashMap; + +use crate::cli::config_parser::{DomainConfig, ProxyType, SslMode}; +use crate::cli::error::CliError; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ContainerInfo — minimal container metadata +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Lightweight representation of a running Docker container. +/// Populated by `ContainerRuntime::list_containers()`. +#[derive(Debug, Clone)] +pub struct ContainerInfo { + pub id: String, + pub name: String, + pub image: String, + pub ports: Vec, + pub status: String, +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ContainerRuntime trait — abstraction over Docker CLI (DIP) +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Abstraction for interacting with the local container runtime. +/// +/// Production: `DockerCliRuntime` shells out to `docker` / `docker compose`. +/// Tests: `MockContainerRuntime` returns canned data. +/// +/// This is the **first** direct Docker CLI interaction in stacker — +/// the server-side code uses agent-mediated command queuing instead. +pub trait ContainerRuntime: Send + Sync { + fn is_available(&self) -> bool; + fn list_containers(&self) -> Result, CliError>; +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// DockerCliRuntime — production implementation +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +pub struct DockerCliRuntime; + +impl ContainerRuntime for DockerCliRuntime { + fn is_available(&self) -> bool { + std::process::Command::new("docker") + .arg("info") + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .status() + .map(|s| s.success()) + .unwrap_or(false) + } + + fn list_containers(&self) -> Result, CliError> { + let output = std::process::Command::new("docker") + .args(["ps", "--format", "{{.ID}}|{{.Names}}|{{.Image}}|{{.Ports}}|{{.Status}}"]) + .output() + .map_err(|_| CliError::ContainerRuntimeUnavailable)?; + + if !output.status.success() { + return Err(CliError::ContainerRuntimeUnavailable); + } + + let stdout = String::from_utf8_lossy(&output.stdout); + let containers = stdout + .lines() + .filter(|line| !line.is_empty()) + .map(parse_docker_ps_line) + .collect(); + + Ok(containers) + } +} + +/// Parse a single line from `docker ps --format "{{.ID}}|{{.Names}}|{{.Image}}|{{.Ports}}|{{.Status}}"`. +fn parse_docker_ps_line(line: &str) -> ContainerInfo { + let parts: Vec<&str> = line.splitn(5, '|').collect(); + + let id = parts.first().unwrap_or(&"").to_string(); + let name = parts.get(1).unwrap_or(&"").to_string(); + let image = parts.get(2).unwrap_or(&"").to_string(); + let ports_str = parts.get(3).unwrap_or(&""); + let status = parts.get(4).unwrap_or(&"").to_string(); + + let ports = extract_host_ports(ports_str); + + ContainerInfo { + id, + name, + image, + ports, + status, + } +} + +/// Extract host-side port numbers from Docker port mapping strings like +/// `0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp`. +fn extract_host_ports(ports_str: &str) -> Vec { + let mut ports = Vec::new(); + for part in ports_str.split(',') { + let part = part.trim(); + // Format: "0.0.0.0:HOST_PORT->CONTAINER_PORT/proto" or "HOST_PORT->CONTAINER_PORT/proto" + if let Some(arrow_idx) = part.find("->") { + let before_arrow = &part[..arrow_idx]; + // Get the port number after the last ':' + if let Some(port_str) = before_arrow.rsplit(':').next() { + if let Ok(port) = port_str.parse::() { + if !ports.contains(&port) { + ports.push(port); + } + } + } + } + } + ports +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ProxyDetection — result of scanning running containers +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Result of scanning local containers for a running reverse proxy. +#[derive(Debug, Clone, PartialEq)] +pub struct ProxyDetection { + pub proxy_type: ProxyType, + pub container_name: Option, + pub ports: Vec, +} + +impl Default for ProxyDetection { + fn default() -> Self { + Self { + proxy_type: ProxyType::None, + container_name: None, + ports: Vec::new(), + } + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// detect_proxy — scan running containers for a reverse proxy +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Known proxy image prefixes and their corresponding type. +const PROXY_SIGNATURES: &[(&str, ProxyType)] = &[ + ("jc21/nginx-proxy-manager", ProxyType::NginxProxyManager), + ("nginx-proxy-manager", ProxyType::NginxProxyManager), + ("traefik", ProxyType::Traefik), + ("nginx", ProxyType::Nginx), +]; + +/// Scan running containers to find a reverse proxy. +/// +/// Uses `ContainerRuntime` (DIP) so tests inject `MockContainerRuntime`. +/// NPM detection takes priority over plain nginx because NPM containers +/// also contain "nginx" in their image name. +pub fn detect_proxy(runtime: &dyn ContainerRuntime) -> Result { + if !runtime.is_available() { + return Err(CliError::ContainerRuntimeUnavailable); + } + + let containers = runtime.list_containers()?; + + for (signature, proxy_type) in PROXY_SIGNATURES { + for container in &containers { + if container.image.contains(signature) || container.name.contains(signature) { + return Ok(ProxyDetection { + proxy_type: *proxy_type, + container_name: Some(container.name.clone()), + ports: container.ports.clone(), + }); + } + } + } + + Ok(ProxyDetection::default()) +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// generate_nginx_server_block — produce nginx config snippet +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Generate an nginx `server { }` block for a single domain configuration. +/// +/// Produces a config suitable for inclusion in `/etc/nginx/conf.d/`. +/// SSL directives are included when `ssl` is `Auto` or `Manual`. +pub fn generate_nginx_server_block(domain: &DomainConfig) -> String { + let mut block = String::new(); + + block.push_str("server {\n"); + + match domain.ssl { + SslMode::Auto | SslMode::Manual => { + block.push_str(" listen 80;\n"); + block.push_str(&format!(" server_name {};\n", domain.domain)); + block.push_str("\n"); + block.push_str(" location / {\n"); + block.push_str(&format!( + " return 301 https://{}$request_uri;\n", + domain.domain + )); + block.push_str(" }\n"); + block.push_str("}\n\n"); + + block.push_str("server {\n"); + block.push_str(" listen 443 ssl http2;\n"); + block.push_str(&format!(" server_name {};\n", domain.domain)); + block.push_str("\n"); + + if domain.ssl == SslMode::Auto { + block.push_str(&format!( + " ssl_certificate /etc/letsencrypt/live/{}/fullchain.pem;\n", + domain.domain + )); + block.push_str(&format!( + " ssl_certificate_key /etc/letsencrypt/live/{}/privkey.pem;\n", + domain.domain + )); + } else { + block.push_str(" ssl_certificate /etc/nginx/ssl/cert.pem;\n"); + block.push_str(" ssl_certificate_key /etc/nginx/ssl/key.pem;\n"); + } + + block.push_str("\n"); + block.push_str(" location / {\n"); + block.push_str(&format!(" proxy_pass http://{};\n", domain.upstream)); + block.push_str(" proxy_set_header Host $host;\n"); + block.push_str(" proxy_set_header X-Real-IP $remote_addr;\n"); + block.push_str(" proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n"); + block.push_str(" proxy_set_header X-Forwarded-Proto $scheme;\n"); + block.push_str(" }\n"); + block.push_str("}\n"); + } + SslMode::Off => { + block.push_str(" listen 80;\n"); + block.push_str(&format!(" server_name {};\n", domain.domain)); + block.push_str("\n"); + block.push_str(" location / {\n"); + block.push_str(&format!(" proxy_pass http://{};\n", domain.upstream)); + block.push_str(" proxy_set_header Host $host;\n"); + block.push_str(" proxy_set_header X-Real-IP $remote_addr;\n"); + block.push_str(" proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n"); + block.push_str(" proxy_set_header X-Forwarded-Proto $scheme;\n"); + block.push_str(" }\n"); + block.push_str("}\n"); + } + } + + block +} + +/// Generate nginx configs for all domains in a proxy config. +/// Returns a map of `filename → config content` for writing to `./nginx/conf.d/`. +pub fn generate_nginx_configs( + domains: &[DomainConfig], +) -> HashMap { + let mut configs = HashMap::new(); + + for domain in domains { + let filename = format!("{}.conf", domain.domain.replace('.', "_")); + let content = generate_nginx_server_block(domain); + configs.insert(filename, content); + } + + configs +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + + // ── Mock container runtime ────────────────────── + + struct MockContainerRuntime { + available: bool, + containers: Vec, + } + + impl MockContainerRuntime { + fn available_with(containers: Vec) -> Self { + Self { + available: true, + containers, + } + } + + fn unavailable() -> Self { + Self { + available: false, + containers: Vec::new(), + } + } + } + + impl ContainerRuntime for MockContainerRuntime { + fn is_available(&self) -> bool { + self.available + } + + fn list_containers(&self) -> Result, CliError> { + Ok(self.containers.clone()) + } + } + + fn nginx_container() -> ContainerInfo { + ContainerInfo { + id: "abc123".to_string(), + name: "nginx-proxy".to_string(), + image: "nginx:alpine".to_string(), + ports: vec![80, 443], + status: "Up 2 hours".to_string(), + } + } + + fn npm_container() -> ContainerInfo { + ContainerInfo { + id: "def456".to_string(), + name: "npm".to_string(), + image: "jc21/nginx-proxy-manager:latest".to_string(), + ports: vec![80, 443, 81], + status: "Up 5 hours".to_string(), + } + } + + fn traefik_container() -> ContainerInfo { + ContainerInfo { + id: "ghi789".to_string(), + name: "traefik".to_string(), + image: "traefik:v2.10".to_string(), + ports: vec![80, 443, 8080], + status: "Up 1 hour".to_string(), + } + } + + fn app_container() -> ContainerInfo { + ContainerInfo { + id: "xyz999".to_string(), + name: "my-app".to_string(), + image: "myapp:latest".to_string(), + ports: vec![3000], + status: "Up 30 minutes".to_string(), + } + } + + // ── Proxy detection tests ─────────────────────── + + #[test] + fn test_detect_proxy_nginx_from_containers() { + let runtime = MockContainerRuntime::available_with(vec![ + app_container(), + nginx_container(), + ]); + let detection = detect_proxy(&runtime).unwrap(); + assert_eq!(detection.proxy_type, ProxyType::Nginx); + assert_eq!(detection.container_name.as_deref(), Some("nginx-proxy")); + assert!(detection.ports.contains(&80)); + assert!(detection.ports.contains(&443)); + } + + #[test] + fn test_detect_proxy_npm_from_containers() { + let runtime = MockContainerRuntime::available_with(vec![ + app_container(), + npm_container(), + ]); + let detection = detect_proxy(&runtime).unwrap(); + assert_eq!(detection.proxy_type, ProxyType::NginxProxyManager); + assert!(detection.ports.contains(&81)); + } + + #[test] + fn test_detect_proxy_traefik_from_containers() { + let runtime = MockContainerRuntime::available_with(vec![ + traefik_container(), + ]); + let detection = detect_proxy(&runtime).unwrap(); + assert_eq!(detection.proxy_type, ProxyType::Traefik); + assert_eq!(detection.container_name.as_deref(), Some("traefik")); + } + + #[test] + fn test_detect_no_proxy() { + let runtime = MockContainerRuntime::available_with(vec![app_container()]); + let detection = detect_proxy(&runtime).unwrap(); + assert_eq!(detection.proxy_type, ProxyType::None); + assert!(detection.container_name.is_none()); + } + + #[test] + fn test_detect_npm_takes_priority_over_nginx() { + // NPM containers contain "nginx" in their image. NPM must be detected + // first because its signature is checked before plain "nginx". + let runtime = MockContainerRuntime::available_with(vec![ + npm_container(), + nginx_container(), + ]); + let detection = detect_proxy(&runtime).unwrap(); + assert_eq!(detection.proxy_type, ProxyType::NginxProxyManager); + } + + #[test] + fn test_detect_proxy_docker_unavailable() { + let runtime = MockContainerRuntime::unavailable(); + let result = detect_proxy(&runtime); + assert!(result.is_err()); + } + + // ── nginx config generation tests ─────────────── + + #[test] + fn test_generate_nginx_server_block_ssl_auto() { + let domain = DomainConfig { + domain: "app.example.com".to_string(), + ssl: SslMode::Auto, + upstream: "app:3000".to_string(), + }; + let block = generate_nginx_server_block(&domain); + assert!(block.contains("server_name app.example.com;")); + assert!(block.contains("listen 443 ssl http2;")); + assert!(block.contains("proxy_pass http://app:3000;")); + assert!(block.contains("letsencrypt")); + assert!(block.contains("return 301 https://")); + } + + #[test] + fn test_generate_nginx_server_block_ssl_manual() { + let domain = DomainConfig { + domain: "app.example.com".to_string(), + ssl: SslMode::Manual, + upstream: "app:3000".to_string(), + }; + let block = generate_nginx_server_block(&domain); + assert!(block.contains("listen 443 ssl http2;")); + assert!(block.contains("/etc/nginx/ssl/cert.pem")); + assert!(!block.contains("letsencrypt")); + } + + #[test] + fn test_generate_nginx_server_block_no_ssl() { + let domain = DomainConfig { + domain: "app.local".to_string(), + ssl: SslMode::Off, + upstream: "app:8080".to_string(), + }; + let block = generate_nginx_server_block(&domain); + assert!(block.contains("listen 80;")); + assert!(block.contains("server_name app.local;")); + assert!(block.contains("proxy_pass http://app:8080;")); + assert!(!block.contains("ssl")); + assert!(!block.contains("443")); + } + + #[test] + fn test_generate_nginx_configs_multiple_domains() { + let domains = vec![ + DomainConfig { + domain: "api.example.com".to_string(), + ssl: SslMode::Auto, + upstream: "api:4000".to_string(), + }, + DomainConfig { + domain: "web.example.com".to_string(), + ssl: SslMode::Off, + upstream: "web:3000".to_string(), + }, + ]; + let configs = generate_nginx_configs(&domains); + assert_eq!(configs.len(), 2); + assert!(configs.contains_key("api_example_com.conf")); + assert!(configs.contains_key("web_example_com.conf")); + } + + // ── Port parsing tests ────────────────────────── + + #[test] + fn test_extract_host_ports_standard() { + let ports = extract_host_ports("0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp"); + assert_eq!(ports, vec![80, 443]); + } + + #[test] + fn test_extract_host_ports_different_host_container() { + let ports = extract_host_ports("0.0.0.0:8080->80/tcp"); + assert_eq!(ports, vec![8080]); + } + + #[test] + fn test_extract_host_ports_empty() { + let ports = extract_host_ports(""); + assert!(ports.is_empty()); + } + + #[test] + fn test_extract_host_ports_no_arrow() { + let ports = extract_host_ports("80/tcp"); + assert!(ports.is_empty()); + } + + // ── Docker ps line parsing tests ──────────────── + + #[test] + fn test_parse_docker_ps_line() { + let line = "abc123|my-nginx|nginx:alpine|0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp|Up 2 hours"; + let info = parse_docker_ps_line(line); + assert_eq!(info.id, "abc123"); + assert_eq!(info.name, "my-nginx"); + assert_eq!(info.image, "nginx:alpine"); + assert_eq!(info.ports, vec![80, 443]); + assert_eq!(info.status, "Up 2 hours"); + } + + #[test] + fn test_parse_docker_ps_line_no_ports() { + let line = "def456|app||Running|Up 5 min"; + let info = parse_docker_ps_line(line); + assert_eq!(info.name, "app"); + assert!(info.ports.is_empty()); + } +} diff --git a/src/cli/stacker_client.rs b/src/cli/stacker_client.rs new file mode 100644 index 00000000..45e81fe0 --- /dev/null +++ b/src/cli/stacker_client.rs @@ -0,0 +1,1023 @@ +//! Stacker Server API Client for CLI +//! +//! Communicates with the Stacker server (not User Service directly) for: +//! - Project CRUD (list, create, lookup by name) +//! - Cloud credential management (list, lookup by provider) +//! - Server management (list, lookup by name) +//! - Deployment (POST /project/{id}/deploy or /project/{id}/deploy/{cloud_id}) +//! +//! All endpoints require `Authorization: Bearer ` from `stacker login`. + +use crate::cli::error::CliError; +use serde::{Deserialize, Serialize}; + +/// Default Stacker server base URL (distinct from the User Service auth URL). +pub const DEFAULT_STACKER_URL: &str = "https://stacker.try.direct"; + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Response types (matching Stacker server JSON envelope) +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +/// Stacker server wraps responses in `{ "item": ..., "list": [...], "msg": "...", "_status": "OK" }` +#[derive(Debug, Deserialize)] +struct ApiResponse { + #[serde(rename = "_status")] + pub status: Option, + pub msg: Option, + pub item: Option, + pub list: Option>, + pub id: Option, + pub meta: Option, +} + +/// Project as returned by `/project` endpoints +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ProjectInfo { + pub id: i32, + pub name: String, + pub user_id: String, + pub metadata: serde_json::Value, + pub created_at: String, + pub updated_at: String, +} + +/// Cloud credentials as returned by `/cloud` endpoints +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CloudInfo { + pub id: i32, + pub user_id: String, + pub provider: String, + pub cloud_token: Option, + pub cloud_key: Option, + pub cloud_secret: Option, + pub save_token: Option, +} + +/// Server as returned by `/server` endpoints +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServerInfo { + pub id: i32, + pub user_id: String, + pub project_id: i32, + pub cloud_id: Option, + #[serde(default)] + pub cloud: Option, + pub region: Option, + pub zone: Option, + pub server: Option, + pub os: Option, + pub disk_type: Option, + pub srv_ip: Option, + pub ssh_port: Option, + pub ssh_user: Option, + pub name: Option, + #[serde(default = "default_connection_mode")] + pub connection_mode: String, + #[serde(default = "default_key_status")] + pub key_status: String, +} + +fn default_connection_mode() -> String { + "ssh".to_string() +} +fn default_key_status() -> String { + "none".to_string() +} + +/// Deploy response from `/project/{id}/deploy` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DeployResponse { + pub id: Option, + #[serde(rename = "_status")] + pub status: Option, + pub msg: Option, + pub meta: Option, +} + +/// Deployment status info from `/api/v1/deployments/{id}` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DeploymentStatusInfo { + pub id: i32, + pub project_id: i32, + pub deployment_hash: String, + pub status: String, + /// Human-readable status/error message from the deployment pipeline. + pub status_message: Option, + pub created_at: String, + pub updated_at: String, +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// StackerClient — HTTP client for the Stacker server +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +pub struct StackerClient { + base_url: String, + token: String, + http: reqwest::Client, +} + +impl StackerClient { + pub fn new(base_url: &str, token: &str) -> Self { + let http = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(30)) + .build() + .expect("Failed to create HTTP client"); + Self { + base_url: base_url.trim_end_matches('/').to_string(), + token: token.to_string(), + http, + } + } + + // ── Projects ───────────────────────────────────── + + /// List all projects for the authenticated user. + pub async fn list_projects(&self) -> Result, CliError> { + let url = format!("{}/project", self.base_url); + let resp = self + .http + .get(&url) + .bearer_auth(&self.token) + .send() + .await + .map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server unreachable: {}", e), + })?; + + if !resp.status().is_success() { + let status = resp.status().as_u16(); + let body = resp.text().await.unwrap_or_default(); + return Err(CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server GET /project failed ({}): {}", status, body), + }); + } + + let api: ApiResponse = resp.json().await.map_err(|e| { + CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Invalid response from Stacker server: {}", e), + } + })?; + + Ok(api.list.unwrap_or_default()) + } + + /// Find a project by name (case-insensitive). + pub async fn find_project_by_name(&self, name: &str) -> Result, CliError> { + let projects = self.list_projects().await?; + let lower = name.to_lowercase(); + Ok(projects + .into_iter() + .find(|p| p.name.to_lowercase() == lower)) + } + + /// Create a project on the Stacker server. + pub async fn create_project( + &self, + name: &str, + metadata: serde_json::Value, + ) -> Result { + let url = format!("{}/project", self.base_url); + + // If metadata already has "custom" key (e.g. from build_project_body), + // use it directly. Otherwise, wrap in a default structure. + let body = if metadata.get("custom").is_some() { + // Ensure custom_stack_code is set to the project name + let mut body = metadata; + if let Some(custom) = body.get_mut("custom").and_then(|c| c.as_object_mut()) { + custom + .entry("custom_stack_code") + .or_insert_with(|| serde_json::json!(name)); + } + body + } else { + let payload = serde_json::json!({ + "custom": { + "custom_stack_code": name, + "web": [], + "feature": [], + "service": [], + } + }); + + // Merge metadata if provided + if metadata.is_object() { + let mut base = payload; + if let Some(obj) = base.as_object_mut() { + if let Some(meta_obj) = metadata.as_object() { + for (k, v) in meta_obj { + obj.insert(k.clone(), v.clone()); + } + } + } + base + } else { + payload + } + }; + + let resp = self + .http + .post(&url) + .bearer_auth(&self.token) + .json(&body) + .send() + .await + .map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server unreachable: {}", e), + })?; + + if !resp.status().is_success() { + let status = resp.status().as_u16(); + let body = resp.text().await.unwrap_or_default(); + return Err(CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!( + "Stacker server POST /project failed ({}): {}", + status, body + ), + }); + } + + let api: ApiResponse = resp.json().await.map_err(|e| { + CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Invalid response from Stacker server: {}", e), + } + })?; + + api.item.ok_or_else(|| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: "Stacker server created project but returned no item".to_string(), + }) + } + + /// Update an existing project's metadata on the Stacker server. + pub async fn update_project( + &self, + project_id: i32, + body: serde_json::Value, + ) -> Result { + let url = format!("{}/project/{}", self.base_url, project_id); + + let resp = self + .http + .put(&url) + .bearer_auth(&self.token) + .json(&body) + .send() + .await + .map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server unreachable: {}", e), + })?; + + if !resp.status().is_success() { + let status = resp.status().as_u16(); + let body = resp.text().await.unwrap_or_default(); + return Err(CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!( + "Stacker server PUT /project/{} failed ({}): {}", + project_id, status, body + ), + }); + } + + let api: ApiResponse = resp.json().await.map_err(|e| { + CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Invalid response from Stacker server: {}", e), + } + })?; + + api.item.ok_or_else(|| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: "Stacker server updated project but returned no item".to_string(), + }) + } + + // ── Cloud credentials ──────────────────────────── + + /// List all saved cloud credentials for the authenticated user. + pub async fn list_clouds(&self) -> Result, CliError> { + let url = format!("{}/cloud", self.base_url); + let resp = self + .http + .get(&url) + .bearer_auth(&self.token) + .send() + .await + .map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server unreachable: {}", e), + })?; + + if !resp.status().is_success() { + let status = resp.status().as_u16(); + let body = resp.text().await.unwrap_or_default(); + return Err(CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server GET /cloud failed ({}): {}", status, body), + }); + } + + let api: ApiResponse = resp.json().await.map_err(|e| { + CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Invalid response from Stacker server: {}", e), + } + })?; + + Ok(api.list.unwrap_or_default()) + } + + /// Find saved cloud credentials by provider name (e.g. "hetzner", "digital_ocean"). + pub async fn find_cloud_by_provider( + &self, + provider: &str, + ) -> Result, CliError> { + let clouds = self.list_clouds().await?; + let lower = provider.to_lowercase(); + Ok(clouds.into_iter().find(|c| c.provider.to_lowercase() == lower)) + } + + /// Find saved cloud credentials by ID. + pub async fn get_cloud(&self, cloud_id: i32) -> Result, CliError> { + let url = format!("{}/cloud/{}", self.base_url, cloud_id); + let resp = self + .http + .get(&url) + .bearer_auth(&self.token) + .send() + .await + .map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server unreachable: {}", e), + })?; + + if resp.status().as_u16() == 404 { + return Ok(None); + } + + if !resp.status().is_success() { + let status = resp.status().as_u16(); + let body = resp.text().await.unwrap_or_default(); + return Err(CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!( + "Stacker server GET /cloud/{} failed ({}): {}", + cloud_id, status, body + ), + }); + } + + let api: ApiResponse = resp.json().await.map_err(|e| { + CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Invalid response from Stacker server: {}", e), + } + })?; + + Ok(api.item) + } + + /// Save cloud credentials to the Stacker server. + pub async fn save_cloud( + &self, + provider: &str, + cloud_token: Option<&str>, + cloud_key: Option<&str>, + cloud_secret: Option<&str>, + ) -> Result { + let url = format!("{}/cloud", self.base_url); + + let mut payload = serde_json::json!({ + "provider": provider, + "save_token": true, + }); + + if let Some(obj) = payload.as_object_mut() { + if let Some(t) = cloud_token { + obj.insert( + "cloud_token".to_string(), + serde_json::Value::String(t.to_string()), + ); + } + if let Some(k) = cloud_key { + obj.insert( + "cloud_key".to_string(), + serde_json::Value::String(k.to_string()), + ); + } + if let Some(s) = cloud_secret { + obj.insert( + "cloud_secret".to_string(), + serde_json::Value::String(s.to_string()), + ); + } + } + + let resp = self + .http + .post(&url) + .bearer_auth(&self.token) + .json(&payload) + .send() + .await + .map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server unreachable: {}", e), + })?; + + if !resp.status().is_success() { + let status = resp.status().as_u16(); + let body = resp.text().await.unwrap_or_default(); + return Err(CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!( + "Stacker server POST /cloud failed ({}): {}", + status, body + ), + }); + } + + let api: ApiResponse = resp.json().await.map_err(|e| { + CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Invalid response from Stacker server: {}", e), + } + })?; + + api.item.ok_or_else(|| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: "Stacker server saved cloud but returned no item".to_string(), + }) + } + + // ── Servers ────────────────────────────────────── + + /// List all servers for the authenticated user. + pub async fn list_servers(&self) -> Result, CliError> { + let url = format!("{}/server", self.base_url); + let resp = self + .http + .get(&url) + .bearer_auth(&self.token) + .send() + .await + .map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server unreachable: {}", e), + })?; + + if !resp.status().is_success() { + let status = resp.status().as_u16(); + let body = resp.text().await.unwrap_or_default(); + return Err(CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server GET /server failed ({}): {}", status, body), + }); + } + + let api: ApiResponse = resp.json().await.map_err(|e| { + CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Invalid response from Stacker server: {}", e), + } + })?; + + Ok(api.list.unwrap_or_default()) + } + + /// Find a server by name (case-insensitive). + pub async fn find_server_by_name(&self, name: &str) -> Result, CliError> { + let servers = self.list_servers().await?; + let lower = name.to_lowercase(); + Ok(servers.into_iter().find(|s| { + s.name + .as_deref() + .map(|n| n.to_lowercase() == lower) + .unwrap_or(false) + })) + } + + // ── Deploy ─────────────────────────────────────── + + /// Deploy a project. If `cloud_id` is provided, uses saved cloud credentials. + pub async fn deploy( + &self, + project_id: i32, + cloud_id: Option, + deploy_form: serde_json::Value, + ) -> Result { + let url = match cloud_id { + Some(cid) => format!("{}/project/{}/deploy/{}", self.base_url, project_id, cid), + None => format!("{}/project/{}/deploy", self.base_url, project_id), + }; + + let resp = self + .http + .post(&url) + .bearer_auth(&self.token) + .json(&deploy_form) + .send() + .await + .map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server unreachable: {}", e), + })?; + + if !resp.status().is_success() { + let status = resp.status().as_u16(); + let body = resp.text().await.unwrap_or_default(); + return Err(CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!( + "Stacker server deploy failed ({}): {}", + status, body + ), + }); + } + + resp.json::().await.map_err(|e| { + CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Invalid deploy response from Stacker server: {}", e), + } + }) + } + + // ── Deployment status ──────────────────────────── + + /// Fetch deployment status by deployment ID. + /// Returns `GET /api/v1/deployments/{id}`. + pub async fn get_deployment_status( + &self, + deployment_id: i32, + ) -> Result, CliError> { + let url = format!("{}/api/v1/deployments/{}", self.base_url, deployment_id); + let resp = self + .http + .get(&url) + .bearer_auth(&self.token) + .send() + .await + .map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server unreachable: {}", e), + })?; + + if resp.status().as_u16() == 404 { + return Ok(None); + } + + if !resp.status().is_success() { + let status = resp.status().as_u16(); + let body = resp.text().await.unwrap_or_default(); + return Err(CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!( + "Stacker server GET /api/v1/deployments/{} failed ({}): {}", + deployment_id, status, body + ), + }); + } + + let api: ApiResponse = + resp.json().await.map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Invalid response from Stacker server: {}", e), + })?; + + Ok(api.item) + } + + /// Fetch the latest deployment status for a project. + /// Returns `GET /api/v1/deployments/project/{project_id}`. + pub async fn get_deployment_status_by_project( + &self, + project_id: i32, + ) -> Result, CliError> { + let url = format!( + "{}/api/v1/deployments/project/{}", + self.base_url, project_id + ); + let resp = self + .http + .get(&url) + .bearer_auth(&self.token) + .send() + .await + .map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Stacker server unreachable: {}", e), + })?; + + if resp.status().as_u16() == 404 { + return Ok(None); + } + + if !resp.status().is_success() { + let status = resp.status().as_u16(); + let body = resp.text().await.unwrap_or_default(); + return Err(CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!( + "Stacker server GET /api/v1/deployments/project/{} failed ({}): {}", + project_id, status, body + ), + }); + } + + let api: ApiResponse = + resp.json().await.map_err(|e| CliError::DeployFailed { + target: crate::cli::config_parser::DeployTarget::Cloud, + reason: format!("Invalid response from Stacker server: {}", e), + })?; + + Ok(api.item) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Helper: build deploy form from stacker.yml config +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +use crate::cli::config_parser::{ServiceDefinition, StackerConfig}; + +/// Generate a short unique ID for app entries (similar to Stacker UI IDs). +fn generate_app_id() -> String { + use std::time::{SystemTime, UNIX_EPOCH}; + let ts = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis(); + format!("cli_{:x}", ts) +} + +/// Parse a Docker image string like `user/repo:tag`, `repo:tag`, or `repo` +/// into (dockerhub_user, dockerhub_name, dockerhub_tag) tuple. +/// +/// The tag is separated from the name so the server / Python side doesn't +/// accidentally append `:latest` again. +fn parse_docker_image(image: &str) -> (Option, String, Option) { + // Split off tag first ("repo:tag" → "repo", Some("tag")) + let (image_no_tag, tag) = if let Some(pos) = image.rfind(':') { + // Avoid splitting on registry port like "registry.io:5000/repo" + let after_colon = &image[pos + 1..]; + if after_colon.contains('/') { + // The colon is part of a registry address, not a tag + (image, None) + } else { + (&image[..pos], Some(after_colon.to_string())) + } + } else { + (image, None) + }; + + // Now split user/name + if let Some((user_part, repo_part)) = image_no_tag.split_once('/') { + if user_part.contains('.') { + // Registry address (e.g. "ghcr.io/owner/repo") — keep as-is + (None, image_no_tag.to_string(), tag) + } else { + (Some(user_part.to_string()), repo_part.to_string(), tag) + } + } else { + (None, image_no_tag.to_string(), tag) + } +} + +/// Parse a port mapping string like "8080:80", "8080:80/tcp", or "3000" +/// into (host_port, container_port) tuple. +fn parse_port_mapping(port_str: &str) -> (String, String) { + // Remove protocol suffix like "/tcp", "/udp" + let port_no_proto = port_str.split('/').next().unwrap_or(port_str); + if let Some((host, container)) = port_no_proto.split_once(':') { + (host.to_string(), container.to_string()) + } else { + (port_no_proto.to_string(), port_no_proto.to_string()) + } +} + +/// Parse a volume mapping string like "./dist:/usr/share/nginx/html" or "data:/var/lib/db" +/// into (host_path, container_path) tuple. +fn parse_volume_mapping(vol_str: &str) -> (String, String) { + if let Some((host, container)) = vol_str.split_once(':') { + (host.to_string(), container.to_string()) + } else { + (vol_str.to_string(), vol_str.to_string()) + } +} + +/// Convert a `ServiceDefinition` from stacker.yml into the Stacker server's +/// app JSON format (matching `forms::project::App` / `forms::project::Web`). +fn service_to_app_json(svc: &ServiceDefinition, network_ids: &[String]) -> serde_json::Value { + let (dockerhub_user, dockerhub_name, dockerhub_tag) = parse_docker_image(&svc.image); + let id = generate_app_id(); + + let shared_ports: Vec = svc + .ports + .iter() + .map(|p| { + let (host, container) = parse_port_mapping(p); + serde_json::json!({ + "host_port": host, + "container_port": container, + }) + }) + .collect(); + + let volumes: Vec = svc + .volumes + .iter() + .map(|v| { + let (host, container) = parse_volume_mapping(v); + serde_json::json!({ + "host_path": host, + "container_path": container, + }) + }) + .collect(); + + let environment: Vec = svc + .environment + .iter() + .map(|(k, v)| { + serde_json::json!({ + "key": k, + "value": v, + }) + }) + .collect(); + + let mut app = serde_json::json!({ + "_id": id, + "name": svc.name.clone(), + "code": svc.name.to_lowercase(), + "type": "web", + "dockerhub_name": dockerhub_name, + "restart": "always", + "custom": true, + "shared_ports": shared_ports, + "volumes": volumes, + "environment": environment, + "network": network_ids, + }); + + let obj = app.as_object_mut().unwrap(); + if let Some(user) = dockerhub_user { + obj.insert("dockerhub_user".to_string(), serde_json::json!(user)); + } + if let Some(tag) = dockerhub_tag { + obj.insert("dockerhub_tag".to_string(), serde_json::json!(tag)); + } + + app +} + +/// Convert the `app` section of stacker.yml into the Stacker server's app JSON +/// format. Returns `None` if the app has no image (build-only local apps). +fn app_source_to_app_json( + config: &StackerConfig, + network_ids: &[String], +) -> Option { + let image = config.app.image.as_deref()?; + let (dockerhub_user, dockerhub_name, dockerhub_tag) = parse_docker_image(image); + let id = generate_app_id(); + + let app_name = config + .project + .identity + .clone() + .unwrap_or_else(|| config.name.clone()); + + // Ports: use explicit ports if provided, otherwise default from app type + let shared_ports: Vec = if config.app.ports.is_empty() { + let default_port = default_port_for_app_type(config.app.app_type); + vec![serde_json::json!({ + "host_port": default_port.to_string(), + "container_port": default_port.to_string(), + })] + } else { + config + .app + .ports + .iter() + .map(|p| { + let (host, container) = parse_port_mapping(p); + serde_json::json!({ + "host_port": host, + "container_port": container, + }) + }) + .collect() + }; + + // Volumes + let volumes: Vec = config + .app + .volumes + .iter() + .map(|v| { + let (host, container) = parse_volume_mapping(v); + serde_json::json!({ + "host_path": host, + "container_path": container, + }) + }) + .collect(); + + // Environment: merge top-level env + app-level (app wins) + let mut merged_env: std::collections::HashMap = config.env.clone(); + for (k, v) in &config.app.environment { + merged_env.insert(k.clone(), v.clone()); + } + let environment: Vec = merged_env + .iter() + .map(|(k, v)| serde_json::json!({ "key": k, "value": v })) + .collect(); + + let mut app = serde_json::json!({ + "_id": id, + "name": app_name, + "code": app_name.to_lowercase(), + "type": "web", + "dockerhub_name": dockerhub_name, + "restart": "always", + "custom": true, + "shared_ports": shared_ports, + "volumes": volumes, + "environment": environment, + "network": network_ids, + }); + + let obj = app.as_object_mut().unwrap(); + if let Some(user) = dockerhub_user { + obj.insert("dockerhub_user".to_string(), serde_json::json!(user)); + } + if let Some(tag) = dockerhub_tag { + obj.insert("dockerhub_tag".to_string(), serde_json::json!(tag)); + } + + Some(app) +} + +/// Map CLI AppType to default port (same as compose generator). +fn default_port_for_app_type(app_type: crate::cli::config_parser::AppType) -> u16 { + use crate::cli::config_parser::AppType; + match app_type { + AppType::Static => 80, + AppType::Node => 3000, + AppType::Python => 8000, + AppType::Rust => 8080, + AppType::Go => 8080, + AppType::Php => 9000, + AppType::Custom => 8080, + } +} + +/// Build the project creation body (matching `forms::project::ProjectForm`) +/// from the CLI's `StackerConfig`, including services from stacker.yml. +pub fn build_project_body(config: &StackerConfig) -> serde_json::Value { + let stack_code = config + .project + .identity + .clone() + .unwrap_or_else(|| config.name.clone()); + + // Create a default network + let network_id = format!("cli_net_{:x}", { + use std::time::{SystemTime, UNIX_EPOCH}; + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() + }); + + let network_ids = vec![network_id.clone()]; + + // Convert the main app + services from stacker.yml to Stacker server + // app format. The main `app` section is the primary web application; + // additional `services` are supporting containers. + let mut web_apps: Vec = Vec::new(); + + // Include the main app (if it has an image) + if let Some(main_app) = app_source_to_app_json(config, &network_ids) { + web_apps.push(main_app); + } + + // Include additional services + for svc in &config.services { + web_apps.push(service_to_app_json(svc, &network_ids)); + } + + serde_json::json!({ + "custom": { + "custom_stack_code": stack_code, + "project_name": config.name.clone(), + "web": web_apps, + "feature": [], + "service": [], + "networks": [{ + "id": network_id, + "name": "default_network", + "driver": "bridge", + }], + } + }) +} + +/// Build the deploy form payload that matches the Stacker server's +/// `forms::project::Deploy` structure. +pub fn build_deploy_form(config: &StackerConfig) -> serde_json::Value { + let cloud = config.deploy.cloud.as_ref(); + let provider = cloud + .map(|c| super::install_runner::provider_code_for_remote(&c.provider.to_string()).to_string()) + .unwrap_or_else(|| "htz".to_string()); + let region = cloud.and_then(|c| c.region.clone()).unwrap_or_else(|| "nbg1".to_string()); + let server_size = cloud.and_then(|c| c.size.clone()).unwrap_or_else(|| "cx11".to_string()); + let os = match provider.as_str() { + "do" => "docker-20-04", + _ => "ubuntu-22.04", + }; + + serde_json::json!({ + "cloud": { + "provider": provider, + "save_token": true, + }, + "server": { + "region": region, + "server": server_size, + "os": os, + }, + "stack": { + "stack_code": config.project.identity.clone().unwrap_or_else(|| config.name.clone()), + "vars": [], + "integrated_features": [], + "extended_features": [], + "subscriptions": [], + } + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_build_deploy_form_defaults() { + let config = crate::cli::config_parser::ConfigBuilder::new() + .name("myproject") + .deploy_target(crate::cli::config_parser::DeployTarget::Cloud) + .cloud(crate::cli::config_parser::CloudConfig { + provider: crate::cli::config_parser::CloudProvider::Hetzner, + orchestrator: crate::cli::config_parser::CloudOrchestrator::Remote, + region: Some("fsn1".to_string()), + size: Some("cx11".to_string()), + install_image: None, + remote_payload_file: None, + ssh_key: None, + key: None, + server: None, + }) + .build() + .unwrap(); + + let form = build_deploy_form(&config); + assert_eq!(form["cloud"]["provider"], "htz"); + assert_eq!(form["server"]["region"], "fsn1"); + assert_eq!(form["server"]["server"], "cx11"); + assert_eq!(form["stack"]["stack_code"], "myproject"); + } + + #[test] + fn test_build_deploy_form_with_identity() { + let config = crate::cli::config_parser::ConfigBuilder::new() + .name("myproject") + .deploy_target(crate::cli::config_parser::DeployTarget::Cloud) + .cloud(crate::cli::config_parser::CloudConfig { + provider: crate::cli::config_parser::CloudProvider::Hetzner, + orchestrator: crate::cli::config_parser::CloudOrchestrator::Remote, + region: None, + size: None, + install_image: None, + remote_payload_file: None, + ssh_key: None, + key: None, + server: None, + }) + .project_identity("optimumcode") + .build() + .unwrap(); + + let form = build_deploy_form(&config); + assert_eq!(form["stack"]["stack_code"], "optimumcode"); + } +} diff --git a/src/connectors/user_service/install.rs b/src/connectors/user_service/install.rs index 4b9edebe..588ba80b 100644 --- a/src/connectors/user_service/install.rs +++ b/src/connectors/user_service/install.rs @@ -113,4 +113,105 @@ impl UserServiceClient { .await .map_err(|e| ConnectorError::InvalidResponse(e.to_string())) } + + /// Initiate a deployment via User Service native flow + pub async fn initiate_deployment( + &self, + bearer_token: &str, + payload: serde_json::Value, + ) -> Result { + let url = format!("{}/install/init/", self.base_url); + + let response = self + .http_client + .post(&url) + .header("Authorization", format!("Bearer {}", bearer_token)) + .json(&payload) + .send() + .await + .map_err(ConnectorError::from)?; + + if !response.status().is_success() { + let status = response.status().as_u16(); + let body = response.text().await.unwrap_or_default(); + return Err(ConnectorError::HttpError(format!( + "User Service error ({}): {}", + status, body + ))); + } + + response + .json::() + .await + .map_err(|e| ConnectorError::InvalidResponse(e.to_string())) + } + + /// Trigger redeploy for an installation + pub async fn trigger_redeploy( + &self, + bearer_token: &str, + installation_id: i64, + ) -> Result { + let url = format!("{}/install/{}/redeploy", self.base_url, installation_id); + + let response = self + .http_client + .post(&url) + .header("Authorization", format!("Bearer {}", bearer_token)) + .send() + .await + .map_err(ConnectorError::from)?; + + if !response.status().is_success() { + let status = response.status().as_u16(); + let body = response.text().await.unwrap_or_default(); + return Err(ConnectorError::HttpError(format!( + "User Service error ({}): {}", + status, body + ))); + } + + response + .json::() + .await + .map_err(|e| ConnectorError::InvalidResponse(e.to_string())) + } + + /// Add app to an existing installation + pub async fn add_app_to_installation( + &self, + bearer_token: &str, + installation_id: i64, + app_code: &str, + app_config: Option, + ) -> Result { + let url = format!("{}/install/{}/add-app", self.base_url, installation_id); + let payload = serde_json::json!({ + "app_code": app_code, + "app_config": app_config.unwrap_or_else(|| serde_json::json!({})) + }); + + let response = self + .http_client + .post(&url) + .header("Authorization", format!("Bearer {}", bearer_token)) + .json(&payload) + .send() + .await + .map_err(ConnectorError::from)?; + + if !response.status().is_success() { + let status = response.status().as_u16(); + let body = response.text().await.unwrap_or_default(); + return Err(ConnectorError::HttpError(format!( + "User Service error ({}): {}", + status, body + ))); + } + + response + .json::() + .await + .map_err(|e| ConnectorError::InvalidResponse(e.to_string())) + } } diff --git a/src/connectors/user_service/marketplace_search.rs b/src/connectors/user_service/marketplace_search.rs new file mode 100644 index 00000000..545f5ac4 --- /dev/null +++ b/src/connectors/user_service/marketplace_search.rs @@ -0,0 +1,129 @@ +use crate::connectors::errors::ConnectorError; + +use super::UserServiceClient; + +impl UserServiceClient { + pub async fn search_marketplace_templates( + &self, + bearer_token: &str, + query: Option<&str>, + category: Option<&str>, + is_marketplace: Option, + page: Option, + max_results: Option, + ) -> Result, ConnectorError> { + let mut url = format!("{}/applications/", self.base_url); + let mut query_parts: Vec = Vec::new(); + + if let Some(page) = page { + query_parts.push(format!("page={}", page)); + } + + if let Some(max_results) = max_results { + query_parts.push(format!("max_results={}", max_results)); + } + + if !query_parts.is_empty() { + url.push('?'); + url.push_str(&query_parts.join("&")); + } + + let response = self + .http_client + .get(&url) + .header("Authorization", format!("Bearer {}", bearer_token)) + .send() + .await + .map_err(ConnectorError::from)?; + + if !response.status().is_success() { + let status = response.status().as_u16(); + let body = response.text().await.unwrap_or_default(); + return Err(ConnectorError::HttpError(format!( + "User Service error ({}): {}", + status, body + ))); + } + + let payload = response + .json::() + .await + .map_err(|e| ConnectorError::InvalidResponse(e.to_string()))?; + + let raw_items = if let Some(items) = payload.get("_items").and_then(|v| v.as_array()) { + items.clone() + } else if let Some(items) = payload.as_array() { + items.clone() + } else { + Vec::new() + }; + + let query_lc = query.map(|q| q.to_lowercase()); + let category_lc = category.map(|c| c.to_lowercase()); + + let items = raw_items + .into_iter() + .filter(|item| { + if let Some(expected) = is_marketplace { + let actual = item + .get("is_from_marketplace") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + if actual != expected { + return false; + } + } + + if let Some(ref q) = query_lc { + let name = item + .get("name") + .and_then(|v| v.as_str()) + .unwrap_or_default() + .to_lowercase(); + let code = item + .get("code") + .and_then(|v| v.as_str()) + .unwrap_or_default() + .to_lowercase(); + let description = item + .get("description") + .and_then(|v| v.as_str()) + .unwrap_or_default() + .to_lowercase(); + + if !(name.contains(q) || code.contains(q) || description.contains(q)) { + return false; + } + } + + if let Some(ref expected_category) = category_lc { + let category_match = item + .get("category") + .and_then(|v| v.as_str()) + .map(|v| v.to_lowercase() == *expected_category) + .unwrap_or(false) + || item + .get("categories") + .and_then(|v| v.as_array()) + .map(|arr| { + arr.iter().any(|entry| { + entry + .as_str() + .map(|v| v.to_lowercase() == *expected_category) + .unwrap_or(false) + }) + }) + .unwrap_or(false); + + if !category_match { + return false; + } + } + + true + }) + .collect::>(); + + Ok(items) + } +} diff --git a/src/connectors/user_service/mod.rs b/src/connectors/user_service/mod.rs index c7bc2731..f86f0a2e 100644 --- a/src/connectors/user_service/mod.rs +++ b/src/connectors/user_service/mod.rs @@ -7,7 +7,9 @@ pub mod deployment_validator; pub mod init; pub mod install; pub mod marketplace_webhook; +pub mod marketplace_search; pub mod mock; +pub mod notifications; pub mod plan; pub mod profile; pub mod stack; diff --git a/src/connectors/user_service/notifications.rs b/src/connectors/user_service/notifications.rs new file mode 100644 index 00000000..4ea904c8 --- /dev/null +++ b/src/connectors/user_service/notifications.rs @@ -0,0 +1,142 @@ +use serde::{Deserialize, Serialize}; + +use crate::connectors::errors::ConnectorError; + +use super::UserServiceClient; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NotificationItem { + #[serde(rename = "_id")] + pub id: Option, + #[serde(default)] + pub title: Option, + #[serde(default)] + pub message: Option, + #[serde(default)] + pub r#type: Option, + #[serde(default)] + pub is_read: Option, + #[serde(rename = "_created")] + #[serde(default)] + pub created_at: Option, + #[serde(rename = "_updated")] + #[serde(default)] + pub updated_at: Option, + #[serde(flatten)] + pub extra: serde_json::Value, +} + +#[derive(Debug, Deserialize)] +struct NotificationsResponse { + _items: Vec, +} + +impl UserServiceClient { + pub async fn list_notifications( + &self, + bearer_token: &str, + page: Option, + max_results: Option, + ) -> Result, ConnectorError> { + let mut url = format!("{}/notifications/", self.base_url); + let mut query = Vec::new(); + + if let Some(page) = page { + query.push(format!("page={}", page)); + } + + if let Some(max_results) = max_results { + query.push(format!("max_results={}", max_results)); + } + + if !query.is_empty() { + url.push('?'); + url.push_str(&query.join("&")); + } + + let response = self + .http_client + .get(&url) + .header("Authorization", format!("Bearer {}", bearer_token)) + .send() + .await + .map_err(ConnectorError::from)?; + + if !response.status().is_success() { + let status = response.status().as_u16(); + let body = response.text().await.unwrap_or_default(); + return Err(ConnectorError::HttpError(format!( + "User Service error ({}): {}", + status, body + ))); + } + + let wrapper: NotificationsResponse = response + .json() + .await + .map_err(|e| ConnectorError::InvalidResponse(e.to_string()))?; + + Ok(wrapper._items) + } + + pub async fn mark_notification_read( + &self, + bearer_token: &str, + notification_id: i64, + is_read: bool, + ) -> Result { + let url = format!("{}/notifications/{}", self.base_url, notification_id); + + let response = self + .http_client + .patch(&url) + .header("Authorization", format!("Bearer {}", bearer_token)) + .json(&serde_json::json!({ "is_read": is_read })) + .send() + .await + .map_err(ConnectorError::from)?; + + if !response.status().is_success() { + let status = response.status().as_u16(); + let body = response.text().await.unwrap_or_default(); + return Err(ConnectorError::HttpError(format!( + "User Service error ({}): {}", + status, body + ))); + } + + response + .json::() + .await + .map_err(|e| ConnectorError::InvalidResponse(e.to_string())) + } + + pub async fn mark_all_notifications_read( + &self, + bearer_token: &str, + ) -> Result { + let url = format!("{}/notifications/mark-all-read", self.base_url); + + let response = self + .http_client + .post(&url) + .header("Authorization", format!("Bearer {}", bearer_token)) + .send() + .await + .map_err(ConnectorError::from)?; + + if !response.status().is_success() { + let status = response.status().as_u16(); + let body = response.text().await.unwrap_or_default(); + return Err(ConnectorError::HttpError(format!( + "User Service error ({}): {}", + status, body + ))); + } + + response + .json::() + .await + .map_err(|e| ConnectorError::InvalidResponse(e.to_string())) + } +} diff --git a/src/console/commands/cli/ai.rs b/src/console/commands/cli/ai.rs new file mode 100644 index 00000000..3da37dc7 --- /dev/null +++ b/src/console/commands/cli/ai.rs @@ -0,0 +1,269 @@ +use std::path::{Path, PathBuf}; +use std::io::{self, Write}; + +use crate::cli::ai_client::{AiProvider, create_provider}; +use crate::cli::config_parser::{AiConfig, AiProviderType, StackerConfig}; +use crate::cli::error::CliError; +use crate::console::commands::CallableTrait; + +const DEFAULT_CONFIG_FILE: &str = "stacker.yml"; + +/// Load AI config from stacker.yml. +fn load_ai_config(config_path: &str) -> Result { + let path = Path::new(config_path); + if !path.exists() { + return Err(CliError::ConfigNotFound { + path: PathBuf::from(config_path), + }); + } + let config = StackerConfig::from_file(path)?; + if !config.ai.enabled { + return Err(CliError::AiNotConfigured); + } + Ok(config.ai) +} + +fn parse_ai_provider(s: &str) -> Result { + let json = format!("\"{}\"", s.trim().to_lowercase()); + serde_json::from_str::(&json).map_err(|_| { + CliError::ConfigValidation( + "Unknown AI provider. Use: openai, anthropic, ollama, custom".to_string(), + ) + }) +} + +fn prompt_line(prompt: &str) -> Result { + print!("{}", prompt); + io::stdout().flush()?; + let mut input = String::new(); + io::stdin().read_line(&mut input)?; + Ok(input.trim().to_string()) +} + +fn prompt_with_default(prompt: &str, default: &str) -> Result { + let line = prompt_line(&format!("{} [{}]: ", prompt, default))?; + if line.is_empty() { + Ok(default.to_string()) + } else { + Ok(line) + } +} + +fn configure_ai_interactive(config_path: &str) -> Result { + let path = Path::new(config_path); + if !path.exists() { + return Err(CliError::ConfigNotFound { + path: PathBuf::from(config_path), + }); + } + + let mut config = StackerConfig::from_file(path)?; + let current = config.ai.clone(); + + eprintln!("AI interactive setup for {}", config_path); + + let provider_default = current.provider.to_string(); + let provider_input = prompt_with_default( + "AI provider (openai|anthropic|ollama|custom)", + &provider_default, + )?; + let provider = parse_ai_provider(&provider_input)?; + + let model_default = current.model.as_deref().unwrap_or(""); + let model_input = prompt_with_default("Model (empty = provider default)", model_default)?; + let model = if model_input.trim().is_empty() { + None + } else { + Some(model_input) + }; + + let api_key_default = current.api_key.as_deref().unwrap_or(""); + let api_key_input = prompt_with_default("API key (empty = keep/none)", api_key_default)?; + let api_key = if api_key_input.trim().is_empty() { + current.api_key.clone() + } else { + Some(api_key_input) + }; + + let endpoint_default = current.endpoint.as_deref().unwrap_or("http://localhost:11434"); + let endpoint_input = prompt_with_default("Endpoint", endpoint_default)?; + let endpoint = if endpoint_input.trim().is_empty() { + None + } else { + Some(endpoint_input) + }; + + let timeout_default = current.timeout.to_string(); + let timeout_input = prompt_with_default("Timeout seconds", &timeout_default)?; + let timeout = timeout_input.parse::().unwrap_or(current.timeout); + + let tasks = if current.tasks.is_empty() { + vec!["dockerfile".to_string(), "compose".to_string()] + } else { + current.tasks.clone() + }; + + config.ai = AiConfig { + enabled: true, + provider, + model, + api_key, + endpoint, + timeout, + tasks, + }; + + let backup_path = format!("{}.bak", config_path); + std::fs::copy(config_path, &backup_path)?; + let yaml = serde_yaml::to_string(&config) + .map_err(|e| CliError::ConfigValidation(format!("Failed to serialize config: {}", e)))?; + std::fs::write(config_path, yaml)?; + + eprintln!("✓ AI configuration saved to {}", config_path); + eprintln!(" Backup written to {}", backup_path); + Ok(config.ai) +} + +/// Build a prompt from the question and optional context file content. +pub fn build_ai_prompt(question: &str, context_content: Option<&str>) -> String { + match context_content { + Some(ctx) => format!( + "Given the following context:\n\n```\n{}\n```\n\nQuestion: {}", + ctx, question + ), + None => question.to_string(), + } +} + +/// Core AI ask logic, extracted for testability. +pub fn run_ai_ask( + question: &str, + context: Option<&str>, + provider: &dyn AiProvider, +) -> Result { + let context_content = match context { + Some(path) => { + let p = Path::new(path); + if !p.exists() { + return Err(CliError::ConfigNotFound { + path: PathBuf::from(path), + }); + } + Some(std::fs::read_to_string(p)?) + } + None => None, + }; + + let prompt = build_ai_prompt(question, context_content.as_deref()); + provider.complete(&prompt, "") +} + +/// `stacker ai ask "" [--context ]` +/// +/// Sends a question to the configured AI provider for assistance +/// with Dockerfile, docker-compose, or deployment troubleshooting. +pub struct AiAskCommand { + pub question: String, + pub context: Option, + pub configure: bool, +} + +impl AiAskCommand { + pub fn new(question: String, context: Option) -> Self { + Self { + question, + context, + configure: false, + } + } + + pub fn with_configure(mut self, configure: bool) -> Self { + self.configure = configure; + self + } +} + +impl CallableTrait for AiAskCommand { + fn call(&self) -> Result<(), Box> { + let ai_config = if self.configure { + configure_ai_interactive(DEFAULT_CONFIG_FILE)? + } else { + load_ai_config(DEFAULT_CONFIG_FILE)? + }; + let provider = create_provider(&ai_config)?; + let response = run_ai_ask( + &self.question, + self.context.as_deref(), + provider.as_ref(), + )?; + println!("{}", response); + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + + struct MockProvider { + response: String, + } + + impl MockProvider { + fn new(response: &str) -> Self { + Self { response: response.to_string() } + } + } + + impl AiProvider for MockProvider { + fn name(&self) -> &str { "mock" } + fn complete(&self, _prompt: &str, _context: &str) -> Result { + Ok(self.response.clone()) + } + } + + #[test] + fn test_build_prompt_without_context() { + let prompt = build_ai_prompt("How do I optimize my Dockerfile?", None); + assert_eq!(prompt, "How do I optimize my Dockerfile?"); + } + + #[test] + fn test_build_prompt_with_context() { + let prompt = build_ai_prompt("Explain this", Some("FROM node:18\nRUN npm install")); + assert!(prompt.contains("context")); + assert!(prompt.contains("FROM node:18")); + assert!(prompt.contains("Explain this")); + } + + #[test] + fn test_run_ai_ask_returns_response() { + let provider = MockProvider::new("Use multi-stage builds for smaller images."); + let result = run_ai_ask("How to optimize?", None, &provider).unwrap(); + assert_eq!(result, "Use multi-stage builds for smaller images."); + } + + #[test] + fn test_run_ai_ask_with_context_file() { + let dir = tempfile::TempDir::new().unwrap(); + let ctx_path = dir.path().join("Dockerfile"); + std::fs::write(&ctx_path, "FROM rust:1.75\nCOPY . .").unwrap(); + + let provider = MockProvider::new("Looks good!"); + let result = run_ai_ask( + "Review this", + Some(ctx_path.to_str().unwrap()), + &provider, + ).unwrap(); + assert_eq!(result, "Looks good!"); + } + + #[test] + fn test_run_ai_ask_missing_context_file_errors() { + let provider = MockProvider::new("unreachable"); + let result = run_ai_ask("question", Some("/does/not/exist.txt"), &provider); + assert!(result.is_err()); + } +} diff --git a/src/console/commands/cli/config.rs b/src/console/commands/cli/config.rs new file mode 100644 index 00000000..9c4dfcc6 --- /dev/null +++ b/src/console/commands/cli/config.rs @@ -0,0 +1,925 @@ +use std::path::{Path, PathBuf}; +use std::io::{self, Write}; + +use crate::cli::config_parser::{ + CloudConfig, CloudOrchestrator, CloudProvider, DeployTarget, ServerConfig, StackerConfig, +}; +use crate::cli::error::CliError; +use crate::console::commands::cli::init::full_config_reference_example; +use crate::console::commands::CallableTrait; + +const DEFAULT_CONFIG_FILE: &str = "stacker.yml"; + +/// Resolve config path from optional override. +fn resolve_config_path(file: &Option) -> String { + file.as_deref() + .unwrap_or(DEFAULT_CONFIG_FILE) + .to_string() +} + +fn prompt_line(prompt: &str) -> Result { + print!("{}", prompt); + io::stdout().flush()?; + let mut input = String::new(); + io::stdin().read_line(&mut input)?; + Ok(input.trim().to_string()) +} + +fn prompt_with_default(prompt: &str, default: &str) -> Result { + let line = prompt_line(&format!("{} [{}]: ", prompt, default))?; + if line.is_empty() { + Ok(default.to_string()) + } else { + Ok(line) + } +} + +fn parse_cloud_provider(s: &str) -> Result { + let json = format!("\"{}\"", s.trim().to_lowercase()); + serde_json::from_str::(&json).map_err(|_| { + CliError::ConfigValidation( + "Invalid cloud provider. Use: hetzner, digitalocean, aws, linode, vultr" + .to_string(), + ) + }) +} + +fn default_region_for_provider(provider: CloudProvider) -> &'static str { + match provider { + CloudProvider::Hetzner => "nbg1", + CloudProvider::Digitalocean => "fra1", + CloudProvider::Aws => "us-east-1", + CloudProvider::Linode => "us-east", + CloudProvider::Vultr => "ewr", + } +} + +fn default_size_for_provider(provider: CloudProvider) -> &'static str { + match provider { + CloudProvider::Hetzner => "cx11", + CloudProvider::Digitalocean => "s-1vcpu-2gb", + CloudProvider::Aws => "t3.small", + CloudProvider::Linode => "g6-standard-2", + CloudProvider::Vultr => "vc2-2c-4gb", + } +} + +fn sanitize_stack_code(name: &str) -> String { + let mut out = String::new(); + let mut prev_dash = false; + + for ch in name.chars() { + let c = ch.to_ascii_lowercase(); + if c.is_ascii_alphanumeric() { + out.push(c); + prev_dash = false; + } else if !prev_dash { + out.push('-'); + prev_dash = true; + } + } + + let out = out.trim_matches('-').to_string(); + if out.is_empty() { + "app-stack".to_string() + } else { + out + } +} + +fn provider_code_for_remote(provider: CloudProvider) -> &'static str { + match provider { + CloudProvider::Hetzner => "htz", + CloudProvider::Digitalocean => "do", + CloudProvider::Aws => "aws", + CloudProvider::Linode => "lo", + CloudProvider::Vultr => "vu", + } +} + +fn first_non_empty_env(keys: &[&str]) -> Option { + keys.iter().find_map(|key| { + std::env::var(key) + .ok() + .map(|v| v.trim().to_string()) + .filter(|v| !v.is_empty()) + }) +} + +fn resolve_remote_cloud_credentials(provider_code: &str) -> serde_json::Map { + let mut creds = serde_json::Map::new(); + + match provider_code { + "htz" => { + if let Some(token) = first_non_empty_env(&[ + "STACKER_CLOUD_TOKEN", + "STACKER_HETZNER_TOKEN", + "HETZNER_TOKEN", + "HCLOUD_TOKEN", + ]) { + creds.insert("cloud_token".to_string(), serde_json::Value::String(token)); + } + } + "do" => { + if let Some(token) = first_non_empty_env(&[ + "STACKER_CLOUD_TOKEN", + "STACKER_DIGITALOCEAN_TOKEN", + "DIGITALOCEAN_TOKEN", + "DO_API_TOKEN", + ]) { + creds.insert("cloud_token".to_string(), serde_json::Value::String(token)); + } + } + "lo" => { + if let Some(token) = first_non_empty_env(&[ + "STACKER_CLOUD_TOKEN", + "STACKER_LINODE_TOKEN", + "LINODE_TOKEN", + ]) { + creds.insert("cloud_token".to_string(), serde_json::Value::String(token)); + } + } + "vu" => { + if let Some(token) = first_non_empty_env(&[ + "STACKER_CLOUD_TOKEN", + "STACKER_VULTR_TOKEN", + "VULTR_TOKEN", + "VULTR_API_KEY", + ]) { + creds.insert("cloud_token".to_string(), serde_json::Value::String(token)); + } + } + "aws" => { + if let Some(key) = first_non_empty_env(&["STACKER_CLOUD_KEY", "AWS_ACCESS_KEY_ID"]) { + creds.insert("cloud_key".to_string(), serde_json::Value::String(key)); + } + if let Some(secret) = + first_non_empty_env(&["STACKER_CLOUD_SECRET", "AWS_SECRET_ACCESS_KEY"]) + { + creds.insert("cloud_secret".to_string(), serde_json::Value::String(secret)); + } + } + _ => {} + } + + creds +} + +pub fn run_generate_remote_payload( + config_path: &str, + output: Option<&str>, +) -> Result, CliError> { + let path = Path::new(config_path); + if !path.exists() { + return Err(CliError::ConfigNotFound { + path: PathBuf::from(config_path), + }); + } + + let mut config = StackerConfig::from_file(path)?; + let config_dir = path.parent().unwrap_or_else(|| Path::new(".")); + + let output_path = match output { + Some(out) => { + let p = PathBuf::from(out); + if p.is_absolute() { + p + } else { + config_dir.join(p) + } + } + None => config_dir.join("stacker.remote.deploy.json"), + }; + + let cloud = config.deploy.cloud.clone(); + let provider = cloud + .as_ref() + .map(|c| c.provider) + .unwrap_or(CloudProvider::Hetzner); + let region = cloud + .as_ref() + .and_then(|c| c.region.clone()) + .unwrap_or_else(|| default_region_for_provider(provider).to_string()); + let size = cloud + .as_ref() + .and_then(|c| c.size.clone()) + .unwrap_or_else(|| default_size_for_provider(provider).to_string()); + let stack_code = config + .project + .identity + .clone() + .map(|v| v.trim().to_string()) + .filter(|v| !v.is_empty()) + .unwrap_or_else(|| "custom-stack".to_string()); + let provider_code = provider_code_for_remote(provider); + let os = match provider_code { + "do" => "docker-20-04", + _ => "ubuntu-22.04", + }; + + let mut payload = serde_json::json!({ + "provider": provider_code, + "region": region, + "server": size, + "os": os, + "ssl": "letsencrypt", + "commonDomain": format!("{}.example.com", sanitize_stack_code(&config.name)), + "domainList": {}, + "stack_code": stack_code, + "project_name": config.name, + "selected_plan": "free", + "payment_type": "subscription", + "subscriptions": [], + "vars": [], + "integrated_features": [], + "extended_features": [], + "save_token": true, + "custom": { + "project_name": config.name, + "custom_stack_code": sanitize_stack_code(&config.name), + "project_overview": format!("Generated by stacker-cli for {}", config.name) + } + }); + + if let Some(obj) = payload.as_object_mut() { + for (key, value) in resolve_remote_cloud_credentials(provider_code) { + obj.insert(key, value); + } + } + + if let Some(parent) = output_path.parent() { + std::fs::create_dir_all(parent)?; + } + let payload_str = serde_json::to_string_pretty(&payload) + .map_err(|e| CliError::ConfigValidation(format!("Failed to serialize payload: {}", e)))?; + std::fs::write(&output_path, payload_str)?; + + let remote_payload_file = output_path + .strip_prefix(config_dir) + .map(PathBuf::from) + .unwrap_or_else(|_| output_path.clone()); + + let existing_cloud = config.deploy.cloud.clone().unwrap_or(CloudConfig { + provider, + orchestrator: CloudOrchestrator::Remote, + region: Some(default_region_for_provider(provider).to_string()), + size: Some(default_size_for_provider(provider).to_string()), + install_image: None, + remote_payload_file: None, + ssh_key: None, + key: None, + server: None, + }); + + config.deploy.target = DeployTarget::Cloud; + config.deploy.cloud = Some(CloudConfig { + provider: existing_cloud.provider, + orchestrator: CloudOrchestrator::Remote, + region: existing_cloud.region, + size: existing_cloud.size, + install_image: existing_cloud.install_image, + remote_payload_file: Some(remote_payload_file), + ssh_key: existing_cloud.ssh_key, + key: existing_cloud.key, + server: existing_cloud.server, + }); + + let backup_path = format!("{}.bak", config_path); + std::fs::copy(config_path, &backup_path)?; + let yaml = serde_yaml::to_string(&config) + .map_err(|e| CliError::ConfigValidation(format!("Failed to serialize config: {}", e)))?; + std::fs::write(config_path, yaml)?; + + Ok(vec![ + format!( + "Generated remote payload (advanced/debug): {}", + output_path.display() + ), + "Set deploy.target=cloud and deploy.cloud.orchestrator=remote (advanced mode)" + .to_string(), + "Tip: regular users can skip this and run `stacker deploy --target cloud` directly" + .to_string(), + format!("Backup written to {}", backup_path), + ]) +} + +fn apply_cloud_settings( + config: &mut StackerConfig, + provider: CloudProvider, + region: Option, + size: Option, + ssh_key: Option, +) { + let existing_orchestrator = config + .deploy + .cloud + .as_ref() + .map(|c| c.orchestrator) + .unwrap_or(CloudOrchestrator::Local); + let existing_install_image = config + .deploy + .cloud + .as_ref() + .and_then(|c| c.install_image.clone()); + + let existing_remote_payload_file = config + .deploy + .cloud + .as_ref() + .and_then(|c| c.remote_payload_file.clone()); + + config.deploy.target = DeployTarget::Cloud; + config.deploy.cloud = Some(CloudConfig { + provider, + orchestrator: existing_orchestrator, + region, + size, + install_image: existing_install_image, + remote_payload_file: existing_remote_payload_file, + ssh_key, + key: None, + server: None, + }); +} + +pub fn run_setup_cloud_interactive(config_path: &str) -> Result, CliError> { + let path = Path::new(config_path); + if !path.exists() { + return Err(CliError::ConfigNotFound { + path: PathBuf::from(config_path), + }); + } + + let mut config = StackerConfig::from_file(path)?; + let mut applied = Vec::new(); + + eprintln!("Cloud setup wizard:"); + + let provider_default = config + .deploy + .cloud + .as_ref() + .map(|c| c.provider) + .unwrap_or(CloudProvider::Hetzner); + + let provider_input = prompt_with_default( + "Cloud provider (hetzner|digitalocean|aws|linode|vultr)", + &provider_default.to_string(), + )?; + let provider = parse_cloud_provider(&provider_input)?; + + let region_default = config + .deploy + .cloud + .as_ref() + .and_then(|c| c.region.clone()) + .unwrap_or_else(|| default_region_for_provider(provider).to_string()); + let region = prompt_with_default("Cloud region", ®ion_default)?; + + let size_default = config + .deploy + .cloud + .as_ref() + .and_then(|c| c.size.clone()) + .unwrap_or_else(|| default_size_for_provider(provider).to_string()); + let size = prompt_with_default("Cloud size", &size_default)?; + + let ssh_key_default = config + .deploy + .cloud + .as_ref() + .and_then(|c| c.ssh_key.clone()) + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|| "~/.ssh/id_rsa".to_string()); + let ssh_key_input = prompt_with_default( + "SSH key path (leave empty to skip)", + &ssh_key_default, + )?; + + let region_opt = if region.trim().is_empty() { + None + } else { + Some(region) + }; + let size_opt = if size.trim().is_empty() { + None + } else { + Some(size) + }; + let ssh_key_opt = if ssh_key_input.trim().is_empty() { + None + } else { + Some(PathBuf::from(ssh_key_input)) + }; + + apply_cloud_settings(&mut config, provider, region_opt, size_opt, ssh_key_opt); + + let backup_path = format!("{}.bak", config_path); + std::fs::copy(config_path, &backup_path)?; + + let yaml = serde_yaml::to_string(&config) + .map_err(|e| CliError::ConfigValidation(format!("Failed to serialize config: {}", e)))?; + std::fs::write(config_path, yaml)?; + + applied.push("Set deploy.target=cloud and deploy.cloud.*".to_string()); + applied.push(format!("Backup written to {}", backup_path)); + Ok(applied) +} + +/// Interactive fixer for common missing required fields. +/// +/// Current MVP handles: +/// - E001: missing deploy.cloud.provider +/// - E002: missing deploy.server.host +pub fn run_fix_interactive(config_path: &str) -> Result, CliError> { + let path = Path::new(config_path); + if !path.exists() { + return Err(CliError::ConfigNotFound { + path: PathBuf::from(config_path), + }); + } + + let mut config = StackerConfig::from_file(path)?; + let issues = config.validate_semantics(); + let mut applied = Vec::new(); + + if issues.is_empty() { + return Ok(applied); + } + + for issue in &issues { + match issue.code.as_str() { + "E001" => { + eprintln!("Detected missing cloud provider settings (E001)."); + + let provider_default = config + .deploy + .cloud + .as_ref() + .map(|c| c.provider.to_string()) + .unwrap_or_else(|| "hetzner".to_string()); + + let provider_input = prompt_with_default( + "Cloud provider (hetzner|digitalocean|aws|linode|vultr)", + &provider_default, + )?; + let provider = parse_cloud_provider(&provider_input)?; + + let region_default = config + .deploy + .cloud + .as_ref() + .and_then(|c| c.region.clone()) + .unwrap_or_else(|| "nbg1".to_string()); + let region = prompt_with_default("Cloud region", ®ion_default)?; + + let size_default = config + .deploy + .cloud + .as_ref() + .and_then(|c| c.size.clone()) + .unwrap_or_else(|| "cx11".to_string()); + let size = prompt_with_default("Cloud size", &size_default)?; + + let ssh_key = config + .deploy + .cloud + .as_ref() + .and_then(|c| c.ssh_key.clone()); + + let orchestrator = config + .deploy + .cloud + .as_ref() + .map(|c| c.orchestrator) + .unwrap_or(CloudOrchestrator::Local); + + let install_image = config + .deploy + .cloud + .as_ref() + .and_then(|c| c.install_image.clone()); + + let remote_payload_file = config + .deploy + .cloud + .as_ref() + .and_then(|c| c.remote_payload_file.clone()); + + config.deploy.target = DeployTarget::Cloud; + config.deploy.cloud = Some(CloudConfig { + provider, + orchestrator, + region: if region.trim().is_empty() { + None + } else { + Some(region) + }, + size: if size.trim().is_empty() { + None + } else { + Some(size) + }, + install_image, + remote_payload_file, + ssh_key, + key: None, + server: None, + }); + + applied.push("Set deploy.target=cloud and deploy.cloud.*".to_string()); + } + "E002" => { + eprintln!("Detected missing server host settings (E002)."); + + let mut host = config + .deploy + .server + .as_ref() + .map(|s| s.host.clone()) + .unwrap_or_default(); + + while host.trim().is_empty() { + host = prompt_line("Server host (required, e.g. 203.0.113.10): ")?; + } + + let user_default = config + .deploy + .server + .as_ref() + .map(|s| s.user.clone()) + .unwrap_or_else(|| "root".to_string()); + let user = prompt_with_default("SSH user", &user_default)?; + + let port_default = config + .deploy + .server + .as_ref() + .map(|s| s.port.to_string()) + .unwrap_or_else(|| "22".to_string()); + let port_input = prompt_with_default("SSH port", &port_default)?; + let port = port_input.parse::().unwrap_or(22); + + let ssh_key = config + .deploy + .server + .as_ref() + .and_then(|s| s.ssh_key.clone()); + + config.deploy.target = DeployTarget::Server; + config.deploy.server = Some(ServerConfig { + host, + user, + ssh_key, + port, + }); + + applied.push("Set deploy.target=server and deploy.server.*".to_string()); + } + _ => {} + } + } + + if applied.is_empty() { + return Ok(applied); + } + + let backup_path = format!("{}.bak", config_path); + std::fs::copy(config_path, &backup_path)?; + + let yaml = serde_yaml::to_string(&config) + .map_err(|e| CliError::ConfigValidation(format!("Failed to serialize config: {}", e)))?; + std::fs::write(config_path, yaml)?; + + applied.push(format!("Backup written to {}", backup_path)); + Ok(applied) +} + +/// Core validate logic — loads config, runs semantic checks, returns issues. +pub fn run_validate(config_path: &str) -> Result, CliError> { + let path = Path::new(config_path); + if !path.exists() { + return Err(CliError::ConfigNotFound { + path: PathBuf::from(config_path), + }); + } + + let config = StackerConfig::from_file(path)?; + let issues = config.validate_semantics(); + let messages: Vec = issues.iter().map(|i| format!("{:?}", i)).collect(); + Ok(messages) +} + +/// Core show logic — loads config, serialises to YAML string. +pub fn run_show(config_path: &str) -> Result { + let path = Path::new(config_path); + if !path.exists() { + return Err(CliError::ConfigNotFound { + path: PathBuf::from(config_path), + }); + } + + let config = StackerConfig::from_file(path)?; + let yaml = serde_yaml::to_string(&config).map_err(|e| { + CliError::ConfigValidation(format!("Failed to serialize config: {}", e)) + })?; + Ok(yaml) +} + +/// `stacker config validate [--file stacker.yml]` +/// +/// Validates a stacker.yml configuration file. +pub struct ConfigValidateCommand { + pub file: Option, +} + +impl ConfigValidateCommand { + pub fn new(file: Option) -> Self { + Self { file } + } +} + +impl CallableTrait for ConfigValidateCommand { + fn call(&self) -> Result<(), Box> { + let path = resolve_config_path(&self.file); + let issues = run_validate(&path)?; + + if issues.is_empty() { + eprintln!("✓ Configuration is valid"); + } else { + eprintln!("Configuration issues:"); + for issue in &issues { + eprintln!(" - {}", issue); + } + } + + Ok(()) + } +} + +/// `stacker config show [--file stacker.yml]` +/// +/// Displays the resolved configuration (with env vars substituted). +pub struct ConfigShowCommand { + pub file: Option, +} + +/// `stacker config fix [--file stacker.yml] [--interactive]` +/// +/// Interactively repairs common missing required fields in stacker.yml. +pub struct ConfigFixCommand { + pub file: Option, + pub interactive: bool, +} + +/// `stacker config setup cloud [--file stacker.yml]` +/// +/// Interactive cloud setup wizard that writes deploy.target/deploy.cloud. +pub struct ConfigSetupCloudCommand { + pub file: Option, +} + +impl ConfigSetupCloudCommand { + pub fn new(file: Option) -> Self { + Self { file } + } +} + +impl CallableTrait for ConfigSetupCloudCommand { + fn call(&self) -> Result<(), Box> { + let path = resolve_config_path(&self.file); + let applied = run_setup_cloud_interactive(&path)?; + + eprintln!("✓ Updated {}", path); + for item in applied { + eprintln!(" - {}", item); + } + eprintln!("Run: stacker config validate"); + Ok(()) + } +} + +/// `stacker config setup remote-payload [--file stacker.yml] [--out stacker.remote.deploy.json]` +/// +/// Advanced/debug helper: generate a User Service `/install/init/` payload file and wire config for remote orchestrator. +pub struct ConfigSetupRemotePayloadCommand { + pub file: Option, + pub out: Option, +} + +impl ConfigSetupRemotePayloadCommand { + pub fn new(file: Option, out: Option) -> Self { + Self { file, out } + } +} + +impl CallableTrait for ConfigSetupRemotePayloadCommand { + fn call(&self) -> Result<(), Box> { + let path = resolve_config_path(&self.file); + let applied = run_generate_remote_payload(&path, self.out.as_deref())?; + + eprintln!("✓ Updated {}", path); + for item in applied { + eprintln!(" - {}", item); + } + eprintln!("Run: stacker deploy --target cloud"); + eprintln!("Note: this command is mainly for troubleshooting and integrations."); + Ok(()) + } +} + +impl ConfigFixCommand { + pub fn new(file: Option, interactive: bool) -> Self { + Self { file, interactive } + } +} + +impl CallableTrait for ConfigFixCommand { + fn call(&self) -> Result<(), Box> { + if !self.interactive { + return Err(Box::new(CliError::ConfigValidation( + "Only interactive mode is supported for now. Use: stacker config fix --interactive" + .to_string(), + ))); + } + + let path = resolve_config_path(&self.file); + let applied = run_fix_interactive(&path)?; + + if applied.is_empty() { + eprintln!("No interactive fixes were applied."); + } else { + eprintln!("✓ Updated {}", path); + for item in applied { + eprintln!(" - {}", item); + } + eprintln!("Run: stacker config validate"); + } + + Ok(()) + } +} + +impl ConfigShowCommand { + pub fn new(file: Option) -> Self { + Self { file } + } +} + +impl CallableTrait for ConfigShowCommand { + fn call(&self) -> Result<(), Box> { + let path = resolve_config_path(&self.file); + let yaml = run_show(&path)?; + println!("{}", yaml); + Ok(()) + } +} + +/// `stacker config example` +/// +/// Prints a full commented `stacker.yml` reference example. +pub struct ConfigExampleCommand; + +impl ConfigExampleCommand { + pub fn new() -> Self { + Self + } +} + +impl CallableTrait for ConfigExampleCommand { + fn call(&self) -> Result<(), Box> { + println!("{}", full_config_reference_example()); + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Write; + + fn minimal_config_yaml() -> &'static str { + "name: test-app\nversion: \"1.0\"\nproject:\n identity: \"registered-stack-code\"\napp:\n type: static\n source: \"./dist\"\ndeploy:\n target: local\n" + } + + fn write_config(dir: &Path, content: &str) -> String { + let path = dir.join("stacker.yml"); + let mut f = std::fs::File::create(&path).unwrap(); + f.write_all(content.as_bytes()).unwrap(); + path.to_string_lossy().to_string() + } + + #[test] + fn test_validate_returns_ok_for_valid_config() { + let dir = tempfile::TempDir::new().unwrap(); + let path = write_config(dir.path(), minimal_config_yaml()); + let result = run_validate(&path).unwrap(); + // Minimal valid config should have zero or few issues + assert!(result.len() < 5); + } + + #[test] + fn test_validate_missing_file_returns_error() { + let result = run_validate("/nonexistent/stacker.yml"); + assert!(result.is_err()); + } + + #[test] + fn test_show_returns_yaml_string() { + let dir = tempfile::TempDir::new().unwrap(); + let path = write_config(dir.path(), minimal_config_yaml()); + let yaml = run_show(&path).unwrap(); + assert!(yaml.contains("test-app")); + } + + #[test] + fn test_show_missing_file_returns_error() { + let result = run_show("/nonexistent/stacker.yml"); + assert!(result.is_err()); + } + + #[test] + fn test_resolve_config_path_default() { + let resolved = resolve_config_path(&None); + assert_eq!(resolved, "stacker.yml"); + } + + #[test] + fn test_resolve_config_path_override() { + let resolved = resolve_config_path(&Some("custom.yml".to_string())); + assert_eq!(resolved, "custom.yml"); + } + + #[test] + fn test_parse_cloud_provider_valid() { + assert_eq!(parse_cloud_provider("hetzner").unwrap(), CloudProvider::Hetzner); + assert_eq!(parse_cloud_provider("AWS").unwrap(), CloudProvider::Aws); + } + + #[test] + fn test_parse_cloud_provider_invalid() { + let result = parse_cloud_provider("gcp"); + assert!(result.is_err()); + } + + #[test] + fn test_default_region_for_provider() { + assert_eq!(default_region_for_provider(CloudProvider::Hetzner), "nbg1"); + assert_eq!(default_region_for_provider(CloudProvider::Aws), "us-east-1"); + } + + #[test] + fn test_apply_cloud_settings_sets_target_and_cloud() { + let mut cfg = StackerConfig::from_str(minimal_config_yaml()).unwrap(); + apply_cloud_settings( + &mut cfg, + CloudProvider::Hetzner, + Some("nbg1".to_string()), + Some("cx11".to_string()), + None, + ); + + assert_eq!(cfg.deploy.target, DeployTarget::Cloud); + let cloud = cfg.deploy.cloud.unwrap(); + assert_eq!(cloud.provider, CloudProvider::Hetzner); + assert_eq!(cloud.region.as_deref(), Some("nbg1")); + assert_eq!(cloud.size.as_deref(), Some("cx11")); + } + + #[test] + fn test_run_generate_remote_payload_writes_file_and_updates_config() { + let dir = tempfile::TempDir::new().unwrap(); + let config_path = write_config(dir.path(), minimal_config_yaml()); + + let applied = run_generate_remote_payload(&config_path, Some("stacker.remote.deploy.json")).unwrap(); + assert!(!applied.is_empty()); + + let payload_path = dir.path().join("stacker.remote.deploy.json"); + assert!(payload_path.exists()); + + let payload_raw = std::fs::read_to_string(&payload_path).unwrap(); + let payload_json: serde_json::Value = serde_json::from_str(&payload_raw).unwrap(); + assert!(payload_json.get("provider").is_some()); + assert!(payload_json.get("commonDomain").is_some()); + assert!(payload_json.get("os").is_some()); + assert!(payload_json.get("selected_plan").is_some()); + assert!(payload_json.get("payment_type").is_some()); + assert!(payload_json.get("subscriptions").is_some()); + assert!(payload_json.get("stack_code").is_some()); + assert_eq!( + payload_json.get("stack_code").and_then(|v| v.as_str()), + Some("registered-stack-code") + ); + + let updated = StackerConfig::from_file(Path::new(&config_path)).unwrap(); + assert_eq!(updated.deploy.target, DeployTarget::Cloud); + let cloud = updated.deploy.cloud.unwrap(); + assert_eq!(cloud.orchestrator, CloudOrchestrator::Remote); + assert_eq!( + cloud.remote_payload_file.as_deref(), + Some(Path::new("stacker.remote.deploy.json")) + ); + } +} diff --git a/src/console/commands/cli/deploy.rs b/src/console/commands/cli/deploy.rs new file mode 100644 index 00000000..67377892 --- /dev/null +++ b/src/console/commands/cli/deploy.rs @@ -0,0 +1,1147 @@ +use std::convert::TryFrom; +use std::path::{Path, PathBuf}; + +use crate::cli::ai_client::{ + build_prompt, create_provider, ollama_complete_streaming, AiTask, PromptContext, +}; +use crate::cli::config_parser::{AiProviderType, DeployTarget, StackerConfig}; +use crate::cli::credentials::CredentialsManager; +use crate::cli::error::CliError; +use crate::cli::generator::compose::ComposeDefinition; +use crate::cli::generator::dockerfile::DockerfileBuilder; +use crate::cli::install_runner::{ + strategy_for, CommandExecutor, DeployContext, DeployResult, ShellExecutor, +}; +use crate::console::commands::CallableTrait; + +/// Default config filename. +const DEFAULT_CONFIG_FILE: &str = "stacker.yml"; + +/// Output directory for generated artifacts. +const OUTPUT_DIR: &str = ".stacker"; + +fn parse_ai_provider(s: &str) -> Result { + let json = format!("\"{}\"", s.trim().to_lowercase()); + serde_json::from_str::(&json).map_err(|_| { + CliError::ConfigValidation( + "Unknown AI provider. Use: openai, anthropic, ollama, custom".to_string(), + ) + }) +} + +fn resolve_ai_from_env_or_config(project_dir: &Path, config_file: Option<&str>) -> Result { + let config_path = match config_file { + Some(f) => project_dir.join(f), + None => project_dir.join(DEFAULT_CONFIG_FILE), + }; + + let mut ai = if config_path.exists() { + StackerConfig::from_file(&config_path)?.ai + } else { + Default::default() + }; + + if let Ok(provider) = std::env::var("STACKER_AI_PROVIDER") { + ai.provider = parse_ai_provider(&provider)?; + ai.enabled = true; + } + + if let Ok(model) = std::env::var("STACKER_AI_MODEL") { + if !model.trim().is_empty() { + ai.model = Some(model); + ai.enabled = true; + } + } + + if let Ok(endpoint) = std::env::var("STACKER_AI_ENDPOINT") { + if !endpoint.trim().is_empty() { + ai.endpoint = Some(endpoint); + ai.enabled = true; + } + } + + if let Ok(timeout) = std::env::var("STACKER_AI_TIMEOUT") { + if let Ok(value) = timeout.parse::() { + ai.timeout = value; + ai.enabled = true; + } + } + + if let Ok(generic_key) = std::env::var("STACKER_AI_API_KEY") { + if !generic_key.trim().is_empty() { + ai.api_key = Some(generic_key); + ai.enabled = true; + } + } + + if ai.api_key.is_none() { + match ai.provider { + AiProviderType::Openai => { + if let Ok(key) = std::env::var("OPENAI_API_KEY") { + if !key.trim().is_empty() { + ai.api_key = Some(key); + ai.enabled = true; + } + } + } + AiProviderType::Anthropic => { + if let Ok(key) = std::env::var("ANTHROPIC_API_KEY") { + if !key.trim().is_empty() { + ai.api_key = Some(key); + ai.enabled = true; + } + } + } + _ => {} + } + } + + Ok(ai) +} + +fn fallback_troubleshooting_hints(reason: &str) -> Vec { + let lower = reason.to_lowercase(); + let mut hints = Vec::new(); + + if lower.contains("npm ci") { + hints.push("npm ci failed: ensure package-lock.json exists and is in sync with package.json".to_string()); + hints.push("Try locally: npm ci --production (or npm ci) to see the full dependency error".to_string()); + } + if lower.contains("the attribute `version` is obsolete") || lower.contains("attribute `version` is obsolete") { + hints.push("docker-compose version warning: remove top-level 'version:' from .stacker/docker-compose.yml".to_string()); + } + if lower.contains("failed to solve") { + hints.push("Docker build step failed: inspect the failing Dockerfile line and run docker build manually for verbose output".to_string()); + } + if lower.contains("permission denied") || lower.contains("eacces") { + hints.push("Permission issue detected: verify file ownership and executable bits for scripts copied into the image".to_string()); + } + if lower.contains("no such file") || lower.contains("not found") { + hints.push("Missing file in build context: confirm COPY paths and .dockerignore rules".to_string()); + } + if lower.contains("network") || lower.contains("timed out") { + hints.push("Network/timeout issue: retry build and verify registry connectivity".to_string()); + } + if lower.contains("port is already allocated") + || lower.contains("bind for 0.0.0.0") + || lower.contains("failed programming external connectivity") + { + hints.push("Port conflict: another process/container already uses this host port (for example 3000).".to_string()); + hints.push("Find the owner with: lsof -nP -iTCP:3000 -sTCP:LISTEN".to_string()); + hints.push("Then stop it (docker compose down / docker rm -f ) or change ports in stacker.yml".to_string()); + } + if lower.contains("remote orchestrator request failed") + && lower.contains("http error") + && lower.contains("404") + && (lower.contains(" .) or use an existing published tag".to_string()); + } + hints.push("Alternative: remove app.image in stacker.yml so Stacker generates/uses a local build context".to_string()); + } + + if hints.is_empty() { + hints.push("Run docker compose -f .stacker/docker-compose.yml build --no-cache for detailed build logs".to_string()); + hints.push("Inspect .stacker/Dockerfile and .stacker/docker-compose.yml for invalid paths and commands".to_string()); + hints.push("If the issue is dependency-related, run the failing install command locally first".to_string()); + } + + hints +} + +fn extract_missing_image(reason: &str) -> Option { + for marker in ["manifest for ", "pull access denied for "] { + if let Some(start) = reason.find(marker) { + let image_start = start + marker.len(); + let tail = &reason[image_start..]; + let image = tail + .split(|c: char| c.is_whitespace() || c == ',' || c == '\n') + .next() + .unwrap_or("") + .trim_matches('"') + .to_string(); + if !image.is_empty() { + return Some(image); + } + } + } + None +} + +fn ensure_env_file_if_needed(config: &StackerConfig, project_dir: &Path) -> Result<(), CliError> { + let env_file = match &config.env_file { + Some(path) => path, + None => return Ok(()), + }; + + let env_path = if env_file.is_absolute() { + env_file.clone() + } else { + project_dir.join(env_file) + }; + + if env_path.exists() { + return Ok(()); + } + + if let Some(parent) = env_path.parent() { + std::fs::create_dir_all(parent)?; + } + + let mut content = String::from("# Auto-created by Stacker because env_file was configured\n"); + if !config.env.is_empty() { + let mut keys: Vec<&String> = config.env.keys().collect(); + keys.sort(); + for key in keys { + content.push_str(&format!("{}={}\n", key, config.env[key])); + } + } + + std::fs::write(&env_path, content)?; + eprintln!(" Created missing env file: {}", env_path.display()); + Ok(()) +} + +fn normalize_generated_compose_paths(compose_path: &Path) -> Result<(), CliError> { + let is_stacker_compose = compose_path + .components() + .any(|c| c.as_os_str() == OUTPUT_DIR); + + if !is_stacker_compose || !compose_path.exists() { + return Ok(()); + } + + let raw = std::fs::read_to_string(compose_path)?; + let mut doc: serde_yaml::Value = serde_yaml::from_str(&raw) + .map_err(|e| CliError::ConfigValidation(format!("Failed to parse compose file: {e}")))?; + + let mut changed = false; + + if let serde_yaml::Value::Mapping(ref mut root) = doc { + // Remove obsolete compose version key. + if root.remove(serde_yaml::Value::String("version".to_string())).is_some() { + changed = true; + } + + let services_key = serde_yaml::Value::String("services".to_string()); + if let Some(serde_yaml::Value::Mapping(services)) = root.get_mut(&services_key) { + for (service_key, service_value) in services.iter_mut() { + let service_name = service_key.as_str().unwrap_or(""); + let service_map = match service_value { + serde_yaml::Value::Mapping(m) => m, + _ => continue, + }; + + let build_key = serde_yaml::Value::String("build".to_string()); + let build_val = match service_map.get_mut(&build_key) { + Some(v) => v, + None => continue, + }; + + let build_map = match build_val { + serde_yaml::Value::Mapping(m) => m, + _ => continue, + }; + + let context_key = serde_yaml::Value::String("context".to_string()); + let dockerfile_key = serde_yaml::Value::String("dockerfile".to_string()); + + let current_context = build_map + .get(&context_key) + .and_then(|v| v.as_str()) + .unwrap_or(".") + .to_string(); + + let dockerfile = build_map + .get(&dockerfile_key) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + + let dockerfile_points_to_stacker = dockerfile + .as_deref() + .map(|d| d.starts_with(".stacker/")) + .unwrap_or(false); + + if dockerfile_points_to_stacker && (current_context == "." || current_context == "./") { + build_map.insert( + context_key.clone(), + serde_yaml::Value::String("..".to_string()), + ); + changed = true; + } + + if service_name == "app" && (current_context == "." || current_context == "./") { + build_map.insert( + context_key, + serde_yaml::Value::String("..".to_string()), + ); + changed = true; + } + } + } + } + + if changed { + let updated = serde_yaml::to_string(&doc) + .map_err(|e| CliError::ConfigValidation(format!("Failed to serialize compose file: {e}")))?; + std::fs::write(compose_path, updated)?; + eprintln!(" Normalized {}/docker-compose.yml paths", OUTPUT_DIR); + } + + Ok(()) +} + +fn compose_app_build_source(compose_path: &Path) -> Option { + let raw = std::fs::read_to_string(compose_path).ok()?; + let doc: serde_yaml::Value = serde_yaml::from_str(&raw).ok()?; + + let root = match doc { + serde_yaml::Value::Mapping(m) => m, + _ => return None, + }; + + let services_key = serde_yaml::Value::String("services".to_string()); + let app_key = serde_yaml::Value::String("app".to_string()); + let build_key = serde_yaml::Value::String("build".to_string()); + let context_key = serde_yaml::Value::String("context".to_string()); + let dockerfile_key = serde_yaml::Value::String("dockerfile".to_string()); + + let services = match root.get(&services_key) { + Some(serde_yaml::Value::Mapping(m)) => m, + _ => return None, + }; + let app = match services.get(&app_key) { + Some(serde_yaml::Value::Mapping(m)) => m, + _ => return None, + }; + let build = app.get(&build_key)?; + + let compose_dir = compose_path.parent().unwrap_or_else(|| Path::new(".")); + + match build { + serde_yaml::Value::String(context_str) => { + let context_path = PathBuf::from(context_str); + let context_abs = if context_path.is_absolute() { + context_path + } else { + compose_dir.join(context_path) + }; + let dockerfile_abs = context_abs.join("Dockerfile"); + Some(format!( + "context={}, dockerfile={}", + context_abs.display(), + dockerfile_abs.display() + )) + } + serde_yaml::Value::Mapping(build_map) => { + let context_raw = build_map + .get(&context_key) + .and_then(|v| v.as_str()) + .unwrap_or("."); + let dockerfile_raw = build_map + .get(&dockerfile_key) + .and_then(|v| v.as_str()) + .unwrap_or("Dockerfile"); + + let context_path = PathBuf::from(context_raw); + let context_abs = if context_path.is_absolute() { + context_path + } else { + compose_dir.join(context_path) + }; + + let dockerfile_path = PathBuf::from(dockerfile_raw); + let dockerfile_abs = if dockerfile_path.is_absolute() { + dockerfile_path + } else { + context_abs.join(dockerfile_path) + }; + + Some(format!( + "context={}, dockerfile={}", + context_abs.display(), + dockerfile_abs.display() + )) + } + _ => None, + } +} + +fn build_troubleshoot_error_log(project_dir: &Path, reason: &str) -> String { + let dockerfile_path = project_dir.join(OUTPUT_DIR).join("Dockerfile"); + let compose_path = project_dir.join(OUTPUT_DIR).join("docker-compose.yml"); + + let dockerfile = std::fs::read_to_string(&dockerfile_path).unwrap_or_default(); + let compose = std::fs::read_to_string(&compose_path).unwrap_or_default(); + + let dockerfile_snippet = if dockerfile.is_empty() { + "(not found)".to_string() + } else { + dockerfile.chars().take(4000).collect() + }; + + let compose_snippet = if compose.is_empty() { + "(not found)".to_string() + } else { + compose.chars().take(4000).collect() + }; + + format!( + "Deploy error:\n{}\n\nGenerated Dockerfile (.stacker/Dockerfile):\n{}\n\nGenerated Compose (.stacker/docker-compose.yml):\n{}", + reason, dockerfile_snippet, compose_snippet + ) +} + +fn print_ai_deploy_help(project_dir: &Path, config_file: Option<&str>, err: &CliError) { + let reason = match err { + CliError::DeployFailed { reason, .. } => reason, + _ => return, + }; + + eprintln!("\nTroubleshooting help:"); + + let ai_config = match resolve_ai_from_env_or_config(project_dir, config_file) { + Ok(cfg) => cfg, + Err(load_err) => { + eprintln!(" Could not load AI config for troubleshooting: {}", load_err); + for hint in fallback_troubleshooting_hints(reason) { + eprintln!(" - {}", hint); + } + eprintln!(" Tip: enable AI with stacker init --with-ai or set STACKER_AI_PROVIDER=ollama"); + return; + } + }; + + if !ai_config.enabled { + eprintln!(" AI troubleshooting disabled (ai.enabled=false)."); + for hint in fallback_troubleshooting_hints(reason) { + eprintln!(" - {}", hint); + } + eprintln!(" Tip: enable AI in stacker.yml if you want AI troubleshooting suggestions"); + return; + } + + let error_log = build_troubleshoot_error_log(project_dir, reason); + let ctx = PromptContext { + project_type: None, + files: vec![".stacker/Dockerfile".to_string(), ".stacker/docker-compose.yml".to_string()], + error_log: Some(error_log), + current_config: None, + }; + let (system, prompt) = build_prompt(AiTask::Troubleshoot, &ctx); + + if ai_config.provider == AiProviderType::Ollama { + eprintln!(" AI suggestion (streaming from Ollama):"); + match ollama_complete_streaming(&ai_config, &prompt, &system) { + Ok(answer) => { + if answer.trim().is_empty() { + eprintln!(" (empty AI response)"); + } + eprintln!(); + } + Err(ai_err) => { + eprintln!(" AI troubleshooting unavailable: {}", ai_err); + for hint in fallback_troubleshooting_hints(reason) { + eprintln!(" - {}", hint); + } + eprintln!(" Tip: set STACKER_AI_PROVIDER=ollama and ensure Ollama is running"); + } + } + return; + } + + eprintln!(" AI request in progress..."); + match create_provider(&ai_config).and_then(|provider| provider.complete(&prompt, &system)) { + Ok(answer) => { + eprintln!(" AI suggestion:"); + for line in answer.lines().take(20) { + eprintln!(" {}", line); + } + } + Err(ai_err) => { + eprintln!(" AI troubleshooting unavailable: {}", ai_err); + for hint in fallback_troubleshooting_hints(reason) { + eprintln!(" - {}", hint); + } + eprintln!(" Tip: set STACKER_AI_PROVIDER=ollama and ensure Ollama is running"); + } + } +} + +/// `stacker deploy [--target local|cloud|server] [--file stacker.yml] [--dry-run] [--force-rebuild]` +/// `stacker deploy --project=myapp --target cloud --key devops --server bastion` +/// +/// Generates Dockerfile + docker-compose from stacker.yml, then +/// deploys using the appropriate strategy (local, cloud, or server). +/// +/// For remote cloud deploys, the CLI now goes through the Stacker server API +/// instead of calling User Service directly: +/// 1. Resolves (or auto-creates) the project on the Stacker server +/// 2. Looks up saved cloud credentials by provider (or passes env-var creds) +/// 3. Looks up saved server by name (optional) +/// 4. Calls `POST /project/{id}/deploy[/{cloud_id}]` +pub struct DeployCommand { + pub target: Option, + pub file: Option, + pub dry_run: bool, + pub force_rebuild: bool, + /// Override project name (--project flag) + pub project_name: Option, + /// Override cloud key name (--key flag) + pub key_name: Option, + /// Override server name (--server flag) + pub server_name: Option, +} + +impl DeployCommand { + pub fn new( + target: Option, + file: Option, + dry_run: bool, + force_rebuild: bool, + ) -> Self { + Self { + target, + file, + dry_run, + force_rebuild, + project_name: None, + key_name: None, + server_name: None, + } + } + + /// Builder method to set remote override flags from CLI args. + pub fn with_remote_overrides( + mut self, + project: Option, + key: Option, + server: Option, + ) -> Self { + self.project_name = project; + self.key_name = key; + self.server_name = server; + self + } +} + +/// Parse a deploy target string into `DeployTarget`. +fn parse_deploy_target(s: &str) -> Result { + let json = format!("\"{}\"", s.to_lowercase()); + serde_json::from_str::(&json).map_err(|_| { + CliError::ConfigValidation(format!( + "Unknown deploy target '{}'. Valid targets: local, cloud, server", + s + )) + }) +} + +/// Override values from CLI flags for remote cloud deploys. +#[derive(Debug, Clone, Default)] +pub struct RemoteDeployOverrides { + pub project_name: Option, + pub key_name: Option, + pub server_name: Option, +} + +/// Core deploy logic, extracted for testability. +/// +/// Takes injectable `CommandExecutor` so tests can mock shell calls. +pub fn run_deploy( + project_dir: &Path, + config_file: Option<&str>, + target_override: Option<&str>, + dry_run: bool, + force_rebuild: bool, + executor: &dyn CommandExecutor, + remote_overrides: &RemoteDeployOverrides, +) -> Result { + // 1. Load config + let config_path = match config_file { + Some(f) => project_dir.join(f), + None => project_dir.join(DEFAULT_CONFIG_FILE), + }; + + let config = StackerConfig::from_file(&config_path)?; + ensure_env_file_if_needed(&config, project_dir)?; + + // 2. Resolve deploy target (flag > config) + let deploy_target = match target_override { + Some(t) => parse_deploy_target(t)?, + None => config.deploy.target, + }; + + // 3. Cloud/server prerequisites + if deploy_target == DeployTarget::Cloud { + // Verify login + let cred_manager = CredentialsManager::with_default_store(); + cred_manager.require_valid_token("cloud deploy")?; + } + + // 4. Validate via strategy + let strategy = strategy_for(&deploy_target); + strategy.validate(&config)?; + + // 5. Generate artifacts into .stacker/ + let output_dir = project_dir.join(OUTPUT_DIR); + std::fs::create_dir_all(&output_dir)?; + + // 5a. Dockerfile + let needs_dockerfile = config.app.image.is_none() && config.app.dockerfile.is_none(); + let dockerfile_path = output_dir.join("Dockerfile"); + + if needs_dockerfile { + if force_rebuild || !dockerfile_path.exists() { + let builder = DockerfileBuilder::from(config.app.app_type); + builder.write_to(&dockerfile_path, force_rebuild)?; + } else { + eprintln!(" Using existing {}/Dockerfile (use --force-rebuild to regenerate)", OUTPUT_DIR); + } + } + + // 5b. docker-compose.yml + let compose_path = if let Some(ref existing) = config.deploy.compose_file { + let configured_path = project_dir.join(existing); + if configured_path.exists() { + configured_path + } else { + let generated_fallback = output_dir.join("docker-compose.yml"); + if generated_fallback.exists() { + eprintln!( + " Configured compose file not found: {}. Falling back to {}", + configured_path.display(), + generated_fallback.display() + ); + generated_fallback + } else { + return Err(CliError::ConfigValidation(format!( + "Compose file not found: {}", + configured_path.display() + ))); + } + } + } else { + let compose_out = output_dir.join("docker-compose.yml"); + if force_rebuild || !compose_out.exists() { + let compose = ComposeDefinition::try_from(&config)?; + compose.write_to(&compose_out, force_rebuild)?; + } else { + eprintln!(" Using existing {}/docker-compose.yml (use --force-rebuild to regenerate)", OUTPUT_DIR); + } + compose_out + }; + + normalize_generated_compose_paths(&compose_path)?; + + // 5b.1 Surface build source paths to avoid confusion. + if let Some(image) = &config.app.image { + eprintln!(" App image source: image={} (no local Dockerfile build)", image); + } else if let Some(build_src) = compose_app_build_source(&compose_path) { + eprintln!(" App build source: {}", build_src); + } else if let Some(dockerfile) = &config.app.dockerfile { + let dockerfile_display = if dockerfile.is_absolute() { + dockerfile.display().to_string() + } else { + project_dir.join(dockerfile).display().to_string() + }; + eprintln!(" App build source: Dockerfile={}", dockerfile_display); + } else { + eprintln!(" App build source: Dockerfile={}", dockerfile_path.display()); + } + eprintln!(" Compose file: {}", compose_path.display()); + + // 5c. Report hooks (dry-run) + if dry_run { + if let Some(ref pre_build) = config.hooks.pre_build { + eprintln!(" Hook (pre_build): {}", pre_build.display()); + } + } + + // 6. Deploy + let context = DeployContext { + config_path: config_path.clone(), + compose_path: compose_path.clone(), + project_dir: project_dir.to_path_buf(), + dry_run, + image: config + .deploy + .cloud + .as_ref() + .and_then(|cloud| cloud.install_image.clone()), + project_name_override: remote_overrides.project_name.clone(), + key_name_override: remote_overrides.key_name.clone(), + server_name_override: remote_overrides.server_name.clone(), + }; + + let result = strategy.deploy(&config, &context, executor)?; + + Ok(result) +} + +impl CallableTrait for DeployCommand { + fn call(&self) -> Result<(), Box> { + let project_dir = std::env::current_dir()?; + let executor = ShellExecutor; + + // Build remote overrides from CLI flags + let remote_overrides = RemoteDeployOverrides { + project_name: self.project_name.clone(), + key_name: self.key_name.clone(), + server_name: self.server_name.clone(), + }; + + let result = run_deploy( + &project_dir, + self.file.as_deref(), + self.target.as_deref(), + self.dry_run, + self.force_rebuild, + &executor, + &remote_overrides, + ); + + let result = match result { + Ok(result) => result, + Err(err) => { + if let CliError::LoginRequired { .. } = &err { + eprintln!("\nHint: run `stacker login` and retry deploy."); + } + print_ai_deploy_help(&project_dir, self.file.as_deref(), &err); + return Err(Box::new(err)); + } + }; + + eprintln!("✓ {}", result.message); + if let Some(ip) = &result.server_ip { + eprintln!(" Server IP: {}", ip); + } + + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::install_runner::CommandOutput; + use std::sync::Mutex; + use tempfile::TempDir; + + /// Mock executor that records commands and returns configurable output. + struct MockExecutor { + calls: Mutex)>>, + output: CommandOutput, + } + + impl MockExecutor { + fn success() -> Self { + Self { + calls: Mutex::new(Vec::new()), + output: CommandOutput { + exit_code: 0, + stdout: "ok".to_string(), + stderr: String::new(), + }, + } + } + + fn recorded_calls(&self) -> Vec<(String, Vec)> { + self.calls.lock().unwrap().clone() + } + } + + impl CommandExecutor for MockExecutor { + fn execute(&self, program: &str, args: &[&str]) -> Result { + self.calls.lock().unwrap().push(( + program.to_string(), + args.iter().map(|s| s.to_string()).collect(), + )); + Ok(self.output.clone()) + } + } + + /// Create a tempdir with a minimal stacker.yml for local deploy. + fn setup_local_project(files: &[(&str, &str)]) -> TempDir { + let dir = TempDir::new().unwrap(); + for (name, content) in files { + let path = dir.path().join(name); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent).unwrap(); + } + std::fs::write(&path, content).unwrap(); + } + dir + } + + fn minimal_config_yaml() -> String { + "name: test-app\napp:\n type: static\n path: .\n".to_string() + } + + fn cloud_config_yaml() -> String { + "name: test-app\napp:\n type: static\n path: .\ndeploy:\n target: cloud\n cloud:\n provider: hetzner\n region: eu-central\n size: cx11\n".to_string() + } + + fn server_config_yaml() -> String { + "name: test-app\napp:\n type: static\n path: .\ndeploy:\n target: server\n server:\n host: 1.2.3.4\n user: root\n port: 22\n".to_string() + } + + // ── Tests ──────────────────────────────────────── + + #[test] + fn test_deploy_local_dry_run_generates_files() { + let dir = setup_local_project(&[ + ("index.html", "

hello

"), + ("stacker.yml", &minimal_config_yaml()), + ]); + let executor = MockExecutor::success(); + + let result = run_deploy(dir.path(), None, Some("local"), true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_ok()); + + // Generated files should exist + assert!(dir.path().join(".stacker/Dockerfile").exists()); + assert!(dir.path().join(".stacker/docker-compose.yml").exists()); + } + + #[test] + fn test_deploy_local_preserves_existing_dockerfile() { + let config = "name: test-app\napp:\n type: static\n path: .\n dockerfile: Dockerfile\n"; + let dir = setup_local_project(&[ + ("index.html", "

hello

"), + ("Dockerfile", "FROM custom:latest\nCOPY . /custom"), + ("stacker.yml", config), + ]); + let executor = MockExecutor::success(); + + let result = run_deploy(dir.path(), None, Some("local"), true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_ok()); + + // Custom Dockerfile should not be overwritten + let df = std::fs::read_to_string(dir.path().join("Dockerfile")).unwrap(); + assert!(df.contains("custom:latest")); + + // .stacker/Dockerfile should NOT be generated (app.dockerfile is set) + assert!(!dir.path().join(".stacker/Dockerfile").exists()); + } + + #[test] + fn test_deploy_local_uses_existing_compose() { + let config = "name: test-app\napp:\n type: static\n path: .\ndeploy:\n compose_file: docker-compose.yml\n"; + let dir = setup_local_project(&[ + ("index.html", "

hello

"), + ("docker-compose.yml", "version: '3.8'\nservices:\n web:\n image: nginx\n"), + ("stacker.yml", config), + ]); + let executor = MockExecutor::success(); + + let result = run_deploy(dir.path(), None, Some("local"), true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_ok()); + + // .stacker/docker-compose.yml should NOT be generated + assert!(!dir.path().join(".stacker/docker-compose.yml").exists()); + } + + #[test] + fn test_deploy_falls_back_when_configured_compose_missing() { + let config = "name: test-app\napp:\n type: static\n path: .\ndeploy:\n compose_file: stacker/docker-compose.yml\n"; + let dir = setup_local_project(&[ + ("index.html", "

hello

"), + ("stacker.yml", config), + (".stacker/docker-compose.yml", "services:\n app:\n image: nginx\n"), + ]); + let executor = MockExecutor::success(); + + let result = run_deploy(dir.path(), None, Some("local"), true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_ok()); + } + + #[test] + fn test_deploy_local_with_image_skips_build() { + let config = "name: test-app\napp:\n type: static\n path: .\n image: nginx:latest\n"; + let dir = setup_local_project(&[ + ("stacker.yml", config), + ]); + let executor = MockExecutor::success(); + + let result = run_deploy(dir.path(), None, Some("local"), true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_ok()); + + // No Dockerfile should be generated (using image) + assert!(!dir.path().join(".stacker/Dockerfile").exists()); + } + + #[test] + fn test_deploy_cloud_requires_login() { + let dir = setup_local_project(&[ + ("stacker.yml", &cloud_config_yaml()), + ]); + let executor = MockExecutor::success(); + + let result = run_deploy(dir.path(), None, None, true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_err()); + + let err = format!("{}", result.unwrap_err()); + assert!( + err.contains("Login required") || err.contains("login"), + "Expected login error, got: {}", + err + ); + } + + #[test] + fn test_deploy_cloud_requires_provider() { + // Cloud target but no cloud config + let config = "name: test-app\napp:\n type: static\n path: .\ndeploy:\n target: cloud\n"; + let dir = setup_local_project(&[ + ("stacker.yml", config), + ]); + let executor = MockExecutor::success(); + + // This should fail at validation since no credentials exist + let result = run_deploy(dir.path(), None, None, true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_err()); + } + + #[test] + fn test_deploy_server_requires_host() { + let config = "name: test-app\napp:\n type: static\n path: .\ndeploy:\n target: server\n"; + let dir = setup_local_project(&[ + ("stacker.yml", config), + ]); + let executor = MockExecutor::success(); + + let result = run_deploy(dir.path(), None, None, true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_err()); + + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("host") || err.contains("Host") || err.contains("server"), + "Expected server host error, got: {}", err); + } + + #[test] + fn test_deploy_missing_config_file() { + let dir = TempDir::new().unwrap(); + let executor = MockExecutor::success(); + + let result = run_deploy(dir.path(), None, None, true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_err()); + + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("not found") || err.contains("Configuration"), + "Expected config not found error, got: {}", err); + } + + #[test] + fn test_deploy_custom_file_flag() { + let dir = setup_local_project(&[ + ("index.html", "

hello

"), + ("custom.yml", &minimal_config_yaml()), + ]); + let executor = MockExecutor::success(); + + let result = run_deploy(dir.path(), Some("custom.yml"), Some("local"), true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_ok()); + } + + #[test] + fn test_deploy_force_rebuild() { + let dir = setup_local_project(&[ + ("index.html", "

hello

"), + ("stacker.yml", &minimal_config_yaml()), + ]); + let executor = MockExecutor::success(); + + // First deploy creates files + let result = run_deploy(dir.path(), None, Some("local"), true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_ok()); + + // Second deploy without force_rebuild should succeed (reuses existing files) + let result2 = run_deploy(dir.path(), None, Some("local"), true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result2.is_ok()); + + // With force_rebuild should also succeed (regenerates files) + let result3 = run_deploy(dir.path(), None, Some("local"), true, true, &executor, &RemoteDeployOverrides::default()); + assert!(result3.is_ok()); + } + + #[test] + fn test_deploy_target_strategy_dispatch() { + // Validate that strategy_for returns the right type + let local = strategy_for(&DeployTarget::Local); + let cloud = strategy_for(&DeployTarget::Cloud); + let server = strategy_for(&DeployTarget::Server); + + // We can't check concrete types directly, but we can ensure + // validation behavior matches expectations: + let minimal_config = StackerConfig::from_str("name: test\napp:\n type: static\n").unwrap(); + + // Local always passes validation + assert!(local.validate(&minimal_config).is_ok()); + // Cloud fails without cloud config + assert!(cloud.validate(&minimal_config).is_err()); + // Server fails without server config + assert!(server.validate(&minimal_config).is_err()); + } + + #[test] + fn test_deploy_runs_pre_build_hook_noted() { + let config = "name: test-app\napp:\n type: static\n path: .\nhooks:\n pre_build: ./build.sh\n"; + let dir = setup_local_project(&[ + ("index.html", "

hello

"), + ("stacker.yml", config), + ]); + let executor = MockExecutor::success(); + + // Dry-run should succeed (hooks are just noted, not executed in dry-run) + let result = run_deploy(dir.path(), None, Some("local"), true, false, &executor, &RemoteDeployOverrides::default()); + assert!(result.is_ok()); + } + + #[test] + fn test_fallback_hints_for_npm_ci_error() { + let hints = fallback_troubleshooting_hints("failed to solve: /bin/sh -c npm ci --production"); + assert!(hints.iter().any(|h| h.contains("npm ci failed"))); + } + + #[test] + fn test_compose_app_build_source_reads_context_and_dockerfile() { + let dir = TempDir::new().unwrap(); + let compose_path = dir.path().join(".stacker").join("docker-compose.yml"); + std::fs::create_dir_all(compose_path.parent().unwrap()).unwrap(); + std::fs::write( + &compose_path, + "services:\n app:\n build:\n context: ..\n dockerfile: .stacker/Dockerfile\n", + ) + .unwrap(); + + let source = compose_app_build_source(&compose_path).unwrap(); + assert!(source.contains("context=")); + assert!(source.contains("dockerfile=")); + assert!(source.contains(".stacker/Dockerfile")); + } + + #[test] + fn test_build_troubleshoot_error_log_handles_missing_files() { + let dir = TempDir::new().unwrap(); + let log = build_troubleshoot_error_log(dir.path(), "docker compose failed"); + assert!(log.contains("docker compose failed")); + assert!(log.contains("(not found)")); + } + + #[test] + fn test_normalize_generated_compose_paths_fixes_stacker_context_and_version() { + let dir = TempDir::new().unwrap(); + let stacker_dir = dir.path().join(".stacker"); + std::fs::create_dir_all(&stacker_dir).unwrap(); + + let compose_path = stacker_dir.join("docker-compose.yml"); + let compose = r#" +version: "3.9" +services: + app: + build: + context: . + dockerfile: .stacker/Dockerfile +"#; + std::fs::write(&compose_path, compose).unwrap(); + + normalize_generated_compose_paths(&compose_path).unwrap(); + + let normalized = std::fs::read_to_string(&compose_path).unwrap(); + assert!(!normalized.contains("version:")); + assert!(normalized.contains("context: ..")); + assert!(normalized.contains("dockerfile: .stacker/Dockerfile")); + } + + #[test] + fn test_parse_deploy_target_valid() { + assert_eq!(parse_deploy_target("local").unwrap(), DeployTarget::Local); + assert_eq!(parse_deploy_target("cloud").unwrap(), DeployTarget::Cloud); + assert_eq!(parse_deploy_target("server").unwrap(), DeployTarget::Server); + assert_eq!(parse_deploy_target("LOCAL").unwrap(), DeployTarget::Local); + } + + #[test] + fn test_parse_deploy_target_invalid() { + let result = parse_deploy_target("kubernetes"); + assert!(result.is_err()); + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("Unknown deploy target")); + } + + #[test] + fn test_extract_missing_image_from_manifest_error() { + let reason = "manifest for optimum/optimumcode:latest not found: manifest unknown"; + let image = extract_missing_image(reason); + assert_eq!(image.as_deref(), Some("optimum/optimumcode:latest")); + } + + #[test] + fn test_fallback_hints_for_manifest_unknown() { + let hints = fallback_troubleshooting_hints( + "docker compose failed: manifest for optimum/optimumcode:latest not found: manifest unknown" + ); + assert!(hints.iter().any(|h| h.contains("Image pull failed"))); + assert!(hints.iter().any(|h| h.contains("docker build -t optimum/optimumcode:latest ."))); + } + + #[test] + fn test_fallback_hints_for_port_conflict() { + let hints = fallback_troubleshooting_hints( + "failed to set up container networking: driver failed programming external connectivity on endpoint app: Bind for 0.0.0.0:3000 failed: port is already allocated" + ); + assert!(hints.iter().any(|h| h.contains("Port conflict"))); + assert!(hints.iter().any(|h| h.contains("lsof -nP -iTCP:3000"))); + } + + #[test] + fn test_fallback_hints_for_orphan_containers() { + let hints = fallback_troubleshooting_hints( + "Found orphan containers ([stackerdb]) for this project" + ); + assert!(hints.iter().any(|h| h.contains("--remove-orphans"))); + } + + #[test] + fn test_fallback_hints_for_remote_orchestrator_html_404() { + let hints = fallback_troubleshooting_hints( + "Remote orchestrator request failed: HTTP error: User Service error (404): Page not found" + ); + assert!(hints.iter().any(|h| h.contains("URL looks incorrect"))); + assert!(hints.iter().any(|h| h.contains("/server/user/auth/login"))); + } + + #[test] + fn test_ensure_env_file_is_created_when_missing() { + let dir = TempDir::new().unwrap(); + let config = StackerConfig::from_str( + "name: env-app\napp:\n type: static\nenv_file: .env\nenv:\n APP_ENV: production\n" + ) + .unwrap(); + + ensure_env_file_if_needed(&config, dir.path()).unwrap(); + + let env_path = dir.path().join(".env"); + assert!(env_path.exists()); + let content = std::fs::read_to_string(env_path).unwrap(); + assert!(content.contains("APP_ENV=production")); + } +} diff --git a/src/console/commands/cli/destroy.rs b/src/console/commands/cli/destroy.rs new file mode 100644 index 00000000..05b87e8e --- /dev/null +++ b/src/console/commands/cli/destroy.rs @@ -0,0 +1,174 @@ +use std::path::Path; + +use crate::cli::config_parser::DeployTarget; +use crate::cli::error::CliError; +use crate::cli::install_runner::{ + CommandExecutor, CommandOutput, ShellExecutor, +}; +use crate::console::commands::CallableTrait; + +/// Output directory for generated artifacts. +const OUTPUT_DIR: &str = ".stacker"; +#[allow(dead_code)] +const DEFAULT_CONFIG_FILE: &str = "stacker.yml"; + +/// `stacker destroy [--volumes] [--confirm]` +/// +/// Tears down the deployed stack and optionally removes volumes. +pub struct DestroyCommand { + pub volumes: bool, + pub confirm: bool, +} + +impl DestroyCommand { + pub fn new(volumes: bool, confirm: bool) -> Self { + Self { volumes, confirm } + } +} + +/// Build `docker compose down` arguments. +pub fn build_destroy_args(compose_path: &str, volumes: bool) -> Vec { + let mut args = vec![ + "compose".to_string(), + "-f".to_string(), + compose_path.to_string(), + "down".to_string(), + ]; + + if volumes { + args.push("--volumes".to_string()); + } + + args +} + +/// Core destroy logic, extracted for testability. +pub fn run_destroy( + project_dir: &Path, + volumes: bool, + confirm: bool, + executor: &dyn CommandExecutor, +) -> Result<(), CliError> { + if !confirm { + return Err(CliError::ConfigValidation( + "Destroy requires --confirm (-y) flag. This will remove all containers and data." + .to_string(), + )); + } + + let compose_path = project_dir.join(OUTPUT_DIR).join("docker-compose.yml"); + + if !compose_path.exists() { + return Err(CliError::ConfigValidation( + "No deployment found. Nothing to destroy.".to_string(), + )); + } + + let compose_str = compose_path.to_string_lossy().to_string(); + let args = build_destroy_args(&compose_str, volumes); + let args_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + + let output = executor.execute("docker", &args_refs)?; + + if !output.success() { + return Err(CliError::DeployFailed { + target: DeployTarget::Local, + reason: format!("docker compose down failed: {}", output.stderr.trim()), + }); + } + + Ok(()) +} + +impl CallableTrait for DestroyCommand { + fn call(&self) -> Result<(), Box> { + let project_dir = std::env::current_dir()?; + let executor = ShellExecutor; + + run_destroy(&project_dir, self.volumes, self.confirm, &executor)?; + eprintln!("✓ Stack destroyed successfully"); + + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Mutex; + + struct MockExecutor { + calls: Mutex)>>, + } + + impl MockExecutor { + fn new() -> Self { + Self { calls: Mutex::new(Vec::new()) } + } + + fn recorded_calls(&self) -> Vec<(String, Vec)> { + self.calls.lock().unwrap().clone() + } + } + + impl CommandExecutor for MockExecutor { + fn execute(&self, program: &str, args: &[&str]) -> Result { + self.calls.lock().unwrap().push(( + program.to_string(), + args.iter().map(|s| s.to_string()).collect(), + )); + Ok(CommandOutput { exit_code: 0, stdout: String::new(), stderr: String::new() }) + } + } + + fn setup_with_compose() -> tempfile::TempDir { + let dir = tempfile::TempDir::new().unwrap(); + let stacker_dir = dir.path().join(OUTPUT_DIR); + std::fs::create_dir_all(&stacker_dir).unwrap(); + std::fs::write(stacker_dir.join("docker-compose.yml"), "version: '3.8'\n").unwrap(); + dir + } + + #[test] + fn test_destroy_constructs_down_command() { + let dir = setup_with_compose(); + let executor = MockExecutor::new(); + + run_destroy(dir.path(), false, true, &executor).unwrap(); + + let calls = executor.recorded_calls(); + assert_eq!(calls.len(), 1); + assert_eq!(calls[0].0, "docker"); + assert!(calls[0].1.contains(&"down".to_string())); + } + + #[test] + fn test_destroy_with_volumes_flag() { + let args = build_destroy_args("/path/compose.yml", true); + assert!(args.contains(&"--volumes".to_string())); + } + + #[test] + fn test_destroy_requires_confirmation() { + let dir = setup_with_compose(); + let executor = MockExecutor::new(); + + let result = run_destroy(dir.path(), false, false, &executor); + assert!(result.is_err()); + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("confirm") || err.contains("Destroy")); + } + + #[test] + fn test_destroy_no_deployment_returns_error() { + let dir = tempfile::TempDir::new().unwrap(); + let executor = MockExecutor::new(); + + let result = run_destroy(dir.path(), false, true, &executor); + assert!(result.is_err()); + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("No deployment found") || err.contains("Nothing to destroy")); + } +} diff --git a/src/console/commands/cli/init.rs b/src/console/commands/cli/init.rs new file mode 100644 index 00000000..e2789d35 --- /dev/null +++ b/src/console/commands/cli/init.rs @@ -0,0 +1,1030 @@ +use std::convert::TryFrom; +use std::path::{Path, PathBuf}; + +use crate::cli::ai_client::{AiProvider, create_provider, ollama_complete_streaming}; +use crate::cli::ai_scanner::{ + build_generation_request, generate_config_with_ai, strip_code_fences, +}; +use crate::cli::config_parser::{ + AiConfig, AiProviderType, AppType, ConfigBuilder, DomainConfig, ProxyConfig, ProxyType, + SslMode, StackerConfig, +}; +use crate::cli::detector::{detect_project, RealFileSystem}; +use crate::cli::error::CliError; +use crate::cli::generator::compose::ComposeDefinition; +use crate::cli::generator::dockerfile::DockerfileBuilder; +use crate::console::commands::CallableTrait; + +/// Default config filename generated by `stacker init`. +pub const DEFAULT_CONFIG_FILE: &str = "stacker.yml"; + +pub fn full_config_reference_example() -> &'static str { + r#"# ----------------------------------------------------------------------------- +# FULL COMMENTED REFERENCE (examples) +# Uncomment and adapt the sections you need. +# ----------------------------------------------------------------------------- +# organization: "acme-inc" +# project: +# # Optional: registered platform catalog stack code for remote cloud deploy. +# # If omitted, remote deploy uses stack_code "custom-stack" by default. +# identity: "your-registered-stack-code" +# env_file: ".env" +# env: +# LOG_LEVEL: "info" +# +# app: +# # type: static | node | python | rust | go | php | custom +# type: "node" +# path: "." +# # dockerfile: "Dockerfile" +# # image: "ghcr.io/your-org/your-image:latest" # usually for type: custom +# # build: +# # context: "." +# # args: +# # APP_ENV: "production" +# +# services: +# - name: "postgres" +# image: "postgres:16" +# ports: ["5432:5432"] +# environment: +# POSTGRES_DB: "app" +# POSTGRES_USER: "app" +# POSTGRES_PASSWORD: "change-me" +# volumes: +# - "postgres_data:/var/lib/postgresql/data" +# depends_on: [] +# +# proxy: +# # type: none | nginx | traefik +# type: "nginx" +# auto_detect: true +# domains: +# - domain: "app.example.com" +# # ssl: none | auto | manual +# ssl: "auto" +# upstream: "app:3000" +# # config: "./nginx.conf" +# +# deploy: +# # target: local | cloud | server +# target: "cloud" +# # compose_file: "docker-compose.yml" +# cloud: +# # provider: hetzner | digitalocean | aws | linode | vultr +# provider: "hetzner" +# # orchestrator: local | remote +# orchestrator: "local" +# region: "nbg1" +# size: "cx11" +# # install_image: "trydirect/install-service:latest" # local orchestrator only +# # remote_payload_file: "./stacker.remote.deploy.json" # remote orchestrator request payload +# # ssh_key: "~/.ssh/id_rsa" +# # server: +# # host: "203.0.113.10" +# # user: "root" +# # port: 22 +# # ssh_key: "~/.ssh/id_rsa" +# +# ai: +# enabled: true +# # provider: openai | anthropic | ollama | custom +# provider: "ollama" +# # model: "deepseek-r1:latest" +# # api_key: "${OPENAI_API_KEY}" +# # endpoint: "http://localhost:11434" +# timeout: 300 +# tasks: ["dockerfile", "compose"] +# +# monitoring: +# status_panel: true +# # healthcheck: +# # endpoint: "/health" +# # interval: "30s" +# # metrics: +# # enabled: true +# # telegraf: true +# +# hooks: +# # pre_build: "./scripts/pre-build.sh" +# # post_deploy: "./scripts/post-deploy.sh" +# # on_failure: "./scripts/on-failure.sh" +# -----------------------------------------------------------------------------"# +} + +/// `stacker init [--type static|node|python|rust|go|php] [--with-proxy] [--with-ai]` +/// +/// Detects the project type in the current directory and generates +/// a `stacker.yml` configuration file with sensible defaults. +/// +/// When `--with-ai` is used with an AI provider configured (via flags +/// or environment variables), Stacker scans the project deeply and +/// sends the context to the AI to generate a tailored `stacker.yml` +/// with appropriate services, proxy, monitoring, etc. +pub struct InitCommand { + pub app_type: Option, + pub with_proxy: bool, + pub with_ai: bool, + pub with_cloud: bool, + /// Override AI provider (openai, anthropic, ollama, custom) + pub ai_provider: Option, + /// Override AI model name + pub ai_model: Option, + /// Override AI API key + pub ai_api_key: Option, +} + +impl InitCommand { + pub fn new(app_type: Option, with_proxy: bool, with_ai: bool, with_cloud: bool) -> Self { + Self { + app_type, + with_proxy, + with_ai, + with_cloud, + ai_provider: None, + ai_model: None, + ai_api_key: None, + } + } + + pub fn with_ai_options( + mut self, + provider: Option, + model: Option, + api_key: Option, + ) -> Self { + self.ai_provider = provider; + self.ai_model = model; + self.ai_api_key = api_key; + self + } +} + +/// Parse an app type string (e.g. "node", "static") into `AppType`. +fn parse_app_type(s: &str) -> Result { + let json = format!("\"{}\"", s.to_lowercase()); + serde_json::from_str::(&json).map_err(|_| { + CliError::ConfigValidation(format!( + "Unknown app type '{}'. Valid types: static, node, python, rust, go, php, custom", + s + )) + }) +} + +/// Build an `AiConfig` from CLI flags and/or environment variables. +/// +/// Priority: CLI flag > environment variable > defaults. +pub fn resolve_ai_config( + ai_provider: Option<&str>, + ai_model: Option<&str>, + ai_api_key: Option<&str>, +) -> Result { + // Provider: flag > env > default (ollama) + let provider_str = ai_provider + .map(|s| s.to_string()) + .or_else(|| std::env::var("STACKER_AI_PROVIDER").ok()) + .unwrap_or_else(|| "ollama".to_string()); + + let provider = parse_ai_provider(&provider_str)?; + + // API key: flag > env (provider-specific) > env (generic) > None + let api_key = ai_api_key + .map(|s| s.to_string()) + .or_else(|| match provider { + AiProviderType::Openai => std::env::var("OPENAI_API_KEY").ok(), + AiProviderType::Anthropic => std::env::var("ANTHROPIC_API_KEY").ok(), + _ => None, + }) + .or_else(|| std::env::var("STACKER_AI_API_KEY").ok()); + + // Model: flag > env > None (provider default will be used) + let model = ai_model + .map(|s| s.to_string()) + .or_else(|| std::env::var("STACKER_AI_MODEL").ok()); + + // Endpoint from env + let endpoint = std::env::var("STACKER_AI_ENDPOINT").ok(); + + // Timeout: env > default (300s) + let timeout = std::env::var("STACKER_AI_TIMEOUT") + .ok() + .and_then(|v| v.parse::().ok()) + .unwrap_or(300); + + Ok(AiConfig { + enabled: true, + provider, + model, + api_key, + endpoint, + timeout, + tasks: vec!["dockerfile".to_string(), "compose".to_string()], + }) +} + +/// Parse an AI provider string into `AiProviderType`. +fn parse_ai_provider(s: &str) -> Result { + match s.to_lowercase().as_str() { + "openai" => Ok(AiProviderType::Openai), + "anthropic" => Ok(AiProviderType::Anthropic), + "ollama" => Ok(AiProviderType::Ollama), + "custom" => Ok(AiProviderType::Custom), + other => Err(CliError::ConfigValidation(format!( + "Unknown AI provider '{}'. Valid: openai, anthropic, ollama, custom", + other + ))), + } +} + +/// Generate a `stacker.yml` config in the target directory. +/// +/// This is extracted as a standalone function for testability — the +/// `InitCommand::call()` delegates here with `std::env::current_dir()`. +/// +/// NOTE: This function always uses template-based generation (no network calls). +/// For AI-powered generation, use `generate_config_full()` with explicit +/// provider options — that path is invoked by the real CLI binary. +pub fn generate_config( + project_dir: &Path, + app_type_override: Option<&str>, + with_proxy: bool, + with_ai: bool, +) -> Result { + let config_path = project_dir.join(DEFAULT_CONFIG_FILE); + + if config_path.exists() { + return Err(CliError::ConfigValidation(format!( + "{} already exists. Remove it first or edit it directly.", + DEFAULT_CONFIG_FILE + ))); + } + + generate_config_template_path( + project_dir, + &config_path, + app_type_override, + with_proxy, + with_ai, + ) +} + +/// Full config generation supporting AI provider options. +pub fn generate_config_full( + project_dir: &Path, + app_type_override: Option<&str>, + with_proxy: bool, + with_ai: bool, + ai_provider: Option<&str>, + ai_model: Option<&str>, + ai_api_key: Option<&str>, +) -> Result { + let config_path = project_dir.join(DEFAULT_CONFIG_FILE); + + // Don't overwrite existing config + if config_path.exists() { + return Err(CliError::ConfigValidation(format!( + "{} already exists. Remove it first or edit it directly.", + DEFAULT_CONFIG_FILE + ))); + } + + // If --with-ai is set, try AI-powered generation + if with_ai { + let ai_config = resolve_ai_config(ai_provider, ai_model, ai_api_key)?; + + match create_provider(&ai_config) { + Ok(provider) => { + match generate_config_ai_path( + project_dir, + &config_path, + provider.as_ref(), + &ai_config, + ) { + Ok(path) => return Ok(path), + Err(e) => { + eprintln!("\n⚠ AI generation failed: {}", e); + eprintln!(" Falling back to template-based generation."); + eprintln!(" Tip: make sure your Ollama model supports code generation."); + eprintln!(" Available models can be listed with: ollama list\n"); + } + } + } + Err(e) => { + eprintln!("\n⚠ AI provider not available: {}", e); + eprintln!(" Falling back to template-based generation."); + eprintln!(" Tip: start Ollama with: ollama serve\n"); + } + } + } + + // Template-based generation (original flow) + generate_config_template_path( + project_dir, + &config_path, + app_type_override, + with_proxy, + with_ai, + ) +} + +/// AI-powered config generation path. +fn generate_config_ai_path( + project_dir: &Path, + config_path: &Path, + provider: &dyn AiProvider, + ai_config: &AiConfig, +) -> Result { + eprintln!("🤖 Scanning project and generating config with AI..."); + + let yaml = if ai_config.provider == AiProviderType::Ollama { + let (system_prompt, user_prompt) = build_generation_request(project_dir); + eprintln!("🧠 AI reasoning (streaming)..."); + let raw = ollama_complete_streaming(ai_config, &user_prompt, &system_prompt)?; + eprintln!(); + let yaml = strip_code_fences(&raw); + + serde_yaml::from_str::(&yaml).map_err(|e| { + CliError::AiProviderError { + provider: "ollama".to_string(), + message: format!("AI generated invalid YAML: {}", e), + } + })?; + + yaml + } else { + generate_config_with_ai(project_dir, provider)? + }; + + // Validate it parses as StackerConfig + match StackerConfig::from_str(&yaml) { + Ok(_) => {} + Err(e) => { + // Save the raw AI output for debugging but warn + let debug_path = project_dir.join("stacker.yml.ai-draft"); + let _ = std::fs::write(&debug_path, &yaml); + return Err(CliError::ConfigValidation(format!( + "AI generated a config that failed validation: {}. \ + Raw output saved to stacker.yml.ai-draft for review.", + e + ))); + } + } + + // Write with header + let content = format!( + "# Stacker configuration — generated by `stacker init --with-ai`\n\ + # AI provider: {} (model: {})\n\ + # Review this file and adjust as needed before deploying.\n\ + # Docs: https://docs.try.direct/stacker\n\ + \n\ + {yaml}\n\ + \n\ + {}\n", + ai_config.provider, + ai_config + .model + .as_deref() + .unwrap_or("default"), + full_config_reference_example(), + ); + + std::fs::write(config_path, &content)?; + + Ok(config_path.to_path_buf()) +} + +/// Template-based config generation (the original flow). +fn generate_config_template_path( + project_dir: &Path, + config_path: &Path, + app_type_override: Option<&str>, + with_proxy: bool, + with_ai: bool, +) -> Result { + // Determine app type: flag override > detection > default (static) + let app_type = if let Some(type_str) = app_type_override { + parse_app_type(type_str)? + } else { + let fs = RealFileSystem; + let detection = detect_project(project_dir, &fs); + detection.app_type + }; + + // Derive project name from directory name + let project_name = project_dir + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("my-app") + .to_string(); + + // Build config + let mut builder = ConfigBuilder::new() + .name(&project_name) + .version("0.1.0") + .app_type(app_type) + .app_path("."); + + if with_proxy { + builder = builder.proxy(ProxyConfig { + proxy_type: ProxyType::Nginx, + domains: vec![DomainConfig { + domain: format!("{}.localhost", project_name), + ssl: SslMode::Auto, + upstream: "app:80".to_string(), + }], + ..ProxyConfig::default() + }); + } + + if with_ai { + builder = builder.ai(AiConfig { + enabled: true, + provider: AiProviderType::Ollama, + ..AiConfig::default() + }); + } + + let config = builder.build()?; + + // Serialize to YAML + let yaml = serde_yaml::to_string(&config) + .map_err(|e| CliError::GeneratorError(format!("Failed to serialize config: {e}")))?; + + // Write with a header comment + let content = format!( + "# Stacker configuration — generated by `stacker init`\n\ + # Docs: https://docs.try.direct/stacker\n\ + \n\ + {yaml}" + ); + + std::fs::write(config_path, &content)?; + + Ok(config_path.to_path_buf()) +} + +/// Output directory for generated artifacts. +const OUTPUT_DIR: &str = ".stacker"; + +fn generate_dockerfile_with_ai( + project_dir: &Path, + config: &StackerConfig, + ai_config: &AiConfig, + provider: &dyn AiProvider, +) -> Result { + let mut context = Vec::new(); + let config_yaml = serde_yaml::to_string(config) + .map_err(|e| CliError::GeneratorError(format!("Failed to serialize config: {e}")))?; + + context.push(format!("Project path: {}", project_dir.display())); + context.push(format!("stacker.yml:\n{}", config_yaml)); + + let package_json_path = project_dir.join("package.json"); + if let Ok(package_json) = std::fs::read_to_string(&package_json_path) { + context.push(format!("package.json:\n{}", package_json)); + } + + let pyproject_path = project_dir.join("pyproject.toml"); + if let Ok(pyproject) = std::fs::read_to_string(&pyproject_path) { + context.push(format!("pyproject.toml:\n{}", pyproject)); + } + + let prompt = format!( + "Generate a production-ready Dockerfile for this project.\n\n{}\n\nRequirements:\n- Output ONLY Dockerfile text, no markdown fences or explanation.\n- The Dockerfile must be runnable for the detected app type.\n- Prefer startup commands that exist in project metadata (e.g., package.json scripts).\n- Avoid placeholder commands for files that may not exist (like hardcoded server.js) unless clearly present.\n- Expose the correct application port.", + context.join("\n\n") + ); + + let system = "You are an expert Docker engineer. Return only valid Dockerfile content."; + let raw = if ai_config.provider == AiProviderType::Ollama { + eprintln!("🧠 AI reasoning for Dockerfile (streaming)..."); + let response = ollama_complete_streaming(ai_config, &prompt, system)?; + eprintln!(); + response + } else { + provider.complete(&prompt, system)? + }; + let dockerfile = strip_code_fences(&raw); + + if !dockerfile + .lines() + .any(|line| line.trim_start().starts_with("FROM ")) + { + return Err(CliError::GeneratorError( + "AI generated Dockerfile without a FROM instruction".to_string(), + )); + } + + Ok(dockerfile) +} + +fn generate_compose_with_ai( + project_dir: &Path, + config: &StackerConfig, + dockerfile_path: &Path, + ai_config: &AiConfig, + provider: &dyn AiProvider, +) -> Result { + let config_yaml = serde_yaml::to_string(config) + .map_err(|e| CliError::GeneratorError(format!("Failed to serialize config: {e}")))?; + + let dockerfile_rel = dockerfile_path + .strip_prefix(project_dir) + .unwrap_or(dockerfile_path) + .to_string_lossy() + .to_string(); + + let prompt = format!( + "Generate a docker compose YAML file for this project.\n\nstacker.yml:\n{}\n\nRequirements:\n- Output ONLY YAML, no markdown fences or explanation.\n- Include a top-level 'services' map.\n- Main app service should build from context '.' and use dockerfile '{}'.\n- Include ports based on app type and include sidecar services from stacker.yml where relevant.\n- Keep the file compatible with `docker compose` (plugin).", + config_yaml, dockerfile_rel + ); + + let system = "You are an expert Docker Compose engineer. Return only valid docker compose YAML."; + let raw = if ai_config.provider == AiProviderType::Ollama { + eprintln!("🧠 AI reasoning for compose (streaming)..."); + let response = ollama_complete_streaming(ai_config, &prompt, system)?; + eprintln!(); + response + } else { + provider.complete(&prompt, system)? + }; + let compose = strip_code_fences(&raw); + + let parsed: serde_yaml::Value = serde_yaml::from_str(&compose).map_err(|e| { + CliError::GeneratorError(format!("AI generated invalid compose YAML: {e}")) + })?; + + if parsed.get("services").is_none() { + return Err(CliError::GeneratorError( + "AI generated compose YAML without top-level 'services'".to_string(), + )); + } + + Ok(compose) +} + +impl CallableTrait for InitCommand { + fn call(&self) -> Result<(), Box> { + let project_dir = std::env::current_dir()?; + + let config_path = generate_config_full( + &project_dir, + self.app_type.as_deref(), + self.with_proxy, + self.with_ai, + self.ai_provider.as_deref(), + self.ai_model.as_deref(), + self.ai_api_key.as_deref(), + )?; + + eprintln!("✓ Created {}", config_path.display()); + + if self.with_cloud { + eprintln!("☁ Running cloud setup wizard..."); + let path_str = config_path.to_string_lossy().to_string(); + let applied = crate::console::commands::cli::config::run_setup_cloud_interactive(&path_str)?; + for item in applied { + eprintln!(" - {}", item); + } + } + + // Verify the generated file is parseable + let config = StackerConfig::from_file(&config_path)?; + eprintln!(" Project: {} ({})", config.name, config.app.app_type); + + if self.with_proxy || config.proxy.proxy_type != ProxyType::None { + eprintln!(" Proxy: enabled ({})", config.proxy.proxy_type); + } + if config.ai.enabled { + eprintln!(" AI: enabled ({})", config.ai.provider); + } + if !config.services.is_empty() { + eprintln!(" Services: {}", config.services.iter().map(|s| s.name.as_str()).collect::>().join(", ")); + } + + // Generate .stacker/ directory with Dockerfile and docker-compose.yml + let output_dir = project_dir.join(OUTPUT_DIR); + std::fs::create_dir_all(&output_dir)?; + + // Optional AI provider for artifact generation + let ai_runtime = if self.with_ai { + match resolve_ai_config( + self.ai_provider.as_deref(), + self.ai_model.as_deref(), + self.ai_api_key.as_deref(), + ) { + Ok(ai_cfg) => match create_provider(&ai_cfg) { + Ok(provider) => Some((ai_cfg, provider)), + Err(e) => { + eprintln!("⚠ AI artifact generation unavailable: {}", e); + None + } + }, + Err(e) => { + eprintln!("⚠ AI artifact generation unavailable: {}", e); + None + } + } + } else { + None + }; + + // Generate Dockerfile + let needs_dockerfile = config.app.image.is_none() && config.app.dockerfile.is_none(); + if self.with_ai { + let dockerfile_path = output_dir.join("Dockerfile"); + let mut generated = false; + + if let Some((ref ai_cfg, ref provider)) = ai_runtime { + match generate_dockerfile_with_ai(&project_dir, &config, ai_cfg, provider.as_ref()) { + Ok(dockerfile) => { + std::fs::write(&dockerfile_path, dockerfile)?; + eprintln!("✓ Generated {}/Dockerfile (AI)", OUTPUT_DIR); + generated = true; + } + Err(e) => { + eprintln!("⚠ AI Dockerfile generation failed: {}", e); + eprintln!(" Falling back to template Dockerfile generation."); + } + } + } + + if !generated { + let builder = DockerfileBuilder::from(config.app.app_type); + builder.write_to(&dockerfile_path, true)?; + eprintln!("✓ Regenerated {}/Dockerfile (template)", OUTPUT_DIR); + } + } else if needs_dockerfile { + let dockerfile_path = output_dir.join("Dockerfile"); + let builder = DockerfileBuilder::from(config.app.app_type); + builder.write_to(&dockerfile_path, false)?; + eprintln!("✓ Generated {}/Dockerfile", OUTPUT_DIR); + } + + // Generate docker-compose.yml + if self.with_ai { + let compose_path = output_dir.join("docker-compose.yml"); + let dockerfile_path = output_dir.join("Dockerfile"); + let mut generated = false; + + if let Some((ref ai_cfg, ref provider)) = ai_runtime { + match generate_compose_with_ai( + &project_dir, + &config, + &dockerfile_path, + ai_cfg, + provider.as_ref(), + ) { + Ok(compose_yaml) => { + std::fs::write(&compose_path, compose_yaml)?; + eprintln!("✓ Generated {}/docker-compose.yml (AI)", OUTPUT_DIR); + generated = true; + } + Err(e) => { + eprintln!("⚠ AI compose generation failed: {}", e); + eprintln!(" Falling back to template compose generation."); + } + } + } + + if !generated { + let compose = ComposeDefinition::try_from(&config)?; + compose.write_to(&compose_path, true)?; + eprintln!("✓ Regenerated {}/docker-compose.yml (template)", OUTPUT_DIR); + } + } else if config.deploy.compose_file.is_none() { + let compose_path = output_dir.join("docker-compose.yml"); + let compose = ComposeDefinition::try_from(&config)?; + compose.write_to(&compose_path, false)?; + eprintln!("✓ Generated {}/docker-compose.yml", OUTPUT_DIR); + } + + eprintln!("\nNext steps:"); + eprintln!(" stacker config validate # Check configuration"); + eprintln!(" stacker deploy --target local --dry-run # Preview deployment"); + eprintln!(" stacker deploy --target local # Deploy locally"); + + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::TempDir; + + struct MockAiProvider { + responses: std::sync::Mutex>, + } + + impl MockAiProvider { + fn new(responses: Vec<&str>) -> Self { + Self { + responses: std::sync::Mutex::new( + responses + .into_iter() + .map(|s| s.to_string()) + .collect(), + ), + } + } + } + + impl AiProvider for MockAiProvider { + fn name(&self) -> &str { + "mock" + } + + fn complete(&self, _prompt: &str, _context: &str) -> Result { + let mut queue = self.responses.lock().unwrap(); + if let Some(next) = queue.pop_front() { + return Ok(next); + } + + Err(CliError::AiProviderError { + provider: "mock".to_string(), + message: "No mock response configured".to_string(), + }) + } + } + + fn setup_dir_with_files(files: &[&str]) -> TempDir { + let dir = TempDir::new().unwrap(); + for f in files { + std::fs::write(dir.path().join(f), "").unwrap(); + } + dir + } + + #[test] + fn test_init_static_project_creates_config() { + let dir = setup_dir_with_files(&["index.html"]); + let result = generate_config(dir.path(), None, false, false); + assert!(result.is_ok()); + + let path = result.unwrap(); + assert!(path.exists()); + + let config = StackerConfig::from_file(&path).unwrap(); + assert_eq!(config.app.app_type, AppType::Static); + } + + #[test] + fn test_init_node_project_detects_correctly() { + let dir = setup_dir_with_files(&["package.json"]); + let result = generate_config(dir.path(), None, false, false); + assert!(result.is_ok()); + + let config = StackerConfig::from_file(&result.unwrap()).unwrap(); + assert_eq!(config.app.app_type, AppType::Node); + } + + #[test] + fn test_init_type_flag_overrides_detection() { + // Dir has package.json (Node) but flag says python + let dir = setup_dir_with_files(&["package.json"]); + let result = generate_config(dir.path(), Some("python"), false, false); + assert!(result.is_ok()); + + let config = StackerConfig::from_file(&result.unwrap()).unwrap(); + assert_eq!(config.app.app_type, AppType::Python); + } + + #[test] + fn test_init_with_proxy_flag_adds_section() { + let dir = setup_dir_with_files(&["index.html"]); + let result = generate_config(dir.path(), None, true, false); + assert!(result.is_ok()); + + let config = StackerConfig::from_file(&result.unwrap()).unwrap(); + assert_eq!(config.proxy.proxy_type, ProxyType::Nginx); + assert!(!config.proxy.domains.is_empty()); + assert!(config.proxy.domains[0].domain.contains("localhost")); + } + + #[test] + fn test_init_with_ai_flag_adds_section() { + let dir = setup_dir_with_files(&["index.html"]); + let result = generate_config(dir.path(), None, false, true); + assert!(result.is_ok()); + + let config = StackerConfig::from_file(&result.unwrap()).unwrap(); + assert!(config.ai.enabled); + assert_eq!(config.ai.provider, AiProviderType::Ollama); + } + + #[test] + fn test_init_does_not_overwrite_existing() { + let dir = setup_dir_with_files(&["index.html", DEFAULT_CONFIG_FILE]); + let result = generate_config(dir.path(), None, false, false); + assert!(result.is_err()); + + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("already exists")); + } + + #[test] + fn test_init_output_parses_as_valid_config() { + let dir = setup_dir_with_files(&["index.html"]); + let path = generate_config(dir.path(), None, false, false).unwrap(); + + // Must be parseable + let result = StackerConfig::from_file(&path); + assert!(result.is_ok()); + } + + #[test] + fn test_init_empty_dir_defaults_to_custom() { + let dir = TempDir::new().unwrap(); + let result = generate_config(dir.path(), None, false, false); + assert!(result.is_ok()); + + let config = StackerConfig::from_file(&result.unwrap()).unwrap(); + // Empty dir with no recognized files → Custom (detector default) + assert_eq!(config.app.app_type, AppType::Custom); + } + + #[test] + fn test_parse_app_type_valid() { + assert_eq!(parse_app_type("static").unwrap(), AppType::Static); + assert_eq!(parse_app_type("node").unwrap(), AppType::Node); + assert_eq!(parse_app_type("Python").unwrap(), AppType::Python); + assert_eq!(parse_app_type("RUST").unwrap(), AppType::Rust); + } + + #[test] + fn test_parse_app_type_invalid() { + let result = parse_app_type("java"); + assert!(result.is_err()); + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("Unknown app type")); + } + + // ── AI provider resolution tests ──────────────── + + #[test] + fn test_resolve_ai_config_defaults_to_ollama() { + let config = resolve_ai_config(None, None, None).unwrap(); + assert!(config.enabled); + assert_eq!(config.provider, AiProviderType::Ollama); + assert!(config.api_key.is_none()); + } + + #[test] + fn test_resolve_ai_config_explicit_provider() { + let config = resolve_ai_config(Some("anthropic"), Some("claude-sonnet-4-20250514"), Some("sk-ant-test")).unwrap(); + assert_eq!(config.provider, AiProviderType::Anthropic); + assert_eq!(config.model.as_deref(), Some("claude-sonnet-4-20250514")); + assert_eq!(config.api_key.as_deref(), Some("sk-ant-test")); + } + + #[test] + fn test_resolve_ai_config_openai_with_key() { + let config = resolve_ai_config(Some("openai"), None, Some("sk-test123")).unwrap(); + assert_eq!(config.provider, AiProviderType::Openai); + assert_eq!(config.api_key.as_deref(), Some("sk-test123")); + } + + #[test] + fn test_resolve_ai_config_invalid_provider_errors() { + let result = resolve_ai_config(Some("invalid-provider"), None, None); + assert!(result.is_err()); + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("Unknown AI provider")); + } + + #[test] + fn test_resolve_ai_config_env_var_fallback() { + std::env::set_var("STACKER_AI_PROVIDER", "openai"); + std::env::set_var("OPENAI_API_KEY", "sk-from-env"); + std::env::set_var("STACKER_AI_MODEL", "gpt-4o-mini"); + + let config = resolve_ai_config(None, None, None).unwrap(); + assert_eq!(config.provider, AiProviderType::Openai); + assert_eq!(config.api_key.as_deref(), Some("sk-from-env")); + assert_eq!(config.model.as_deref(), Some("gpt-4o-mini")); + + std::env::remove_var("STACKER_AI_PROVIDER"); + std::env::remove_var("OPENAI_API_KEY"); + std::env::remove_var("STACKER_AI_MODEL"); + } + + #[test] + fn test_resolve_ai_config_flag_overrides_env() { + std::env::set_var("STACKER_AI_PROVIDER", "openai"); + + // Flag says ollama, env says openai — flag wins + let config = resolve_ai_config(Some("ollama"), None, None).unwrap(); + assert_eq!(config.provider, AiProviderType::Ollama); + + std::env::remove_var("STACKER_AI_PROVIDER"); + } + + #[test] + fn test_resolve_ai_config_timeout_default() { + std::env::remove_var("STACKER_AI_TIMEOUT"); + let config = resolve_ai_config(None, None, None).unwrap(); + assert_eq!(config.timeout, 300); + } + + #[test] + fn test_resolve_ai_config_timeout_from_env() { + std::env::set_var("STACKER_AI_TIMEOUT", "900"); + let config = resolve_ai_config(None, None, None).unwrap(); + assert_eq!(config.timeout, 900); + std::env::remove_var("STACKER_AI_TIMEOUT"); + } + + #[test] + fn test_parse_ai_provider_all_valid() { + assert_eq!(parse_ai_provider("openai").unwrap(), AiProviderType::Openai); + assert_eq!(parse_ai_provider("anthropic").unwrap(), AiProviderType::Anthropic); + assert_eq!(parse_ai_provider("ollama").unwrap(), AiProviderType::Ollama); + assert_eq!(parse_ai_provider("custom").unwrap(), AiProviderType::Custom); + // Case insensitive + assert_eq!(parse_ai_provider("OpenAI").unwrap(), AiProviderType::Openai); + assert_eq!(parse_ai_provider("ANTHROPIC").unwrap(), AiProviderType::Anthropic); + } + + #[test] + fn test_generate_config_full_template_fallback() { + // with_ai=true but bogus provider → create_provider fails → falls back to template + let dir = setup_dir_with_files(&["package.json"]); + // Use an explicit provider that will fail connection (port 1 is unreachable) + // This avoids hitting a real running Ollama instance + let result = generate_config_full( + dir.path(), None, false, true, + Some("custom"), None, Some("fake-key"), + ); + assert!(result.is_ok()); + + let config = StackerConfig::from_file(&result.unwrap()).unwrap(); + // Template fallback still generates correct app type + assert_eq!(config.app.app_type, AppType::Node); + // And includes AI section in config + assert!(config.ai.enabled); + } + + #[test] + fn test_generate_dockerfile_with_ai_strips_fences() { + let dir = setup_dir_with_files(&["package.json"]); + let config = ConfigBuilder::new() + .name("demo") + .version("0.1.0") + .app_type(AppType::Node) + .app_path(".") + .build() + .unwrap(); + + let provider = MockAiProvider::new(vec![ + "```dockerfile\nFROM node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --omit=dev\nCOPY . .\nCMD [\"npm\",\"start\"]\n```", + ]); + + let ai_cfg = AiConfig { + enabled: true, + provider: AiProviderType::Custom, + ..AiConfig::default() + }; + + let dockerfile = generate_dockerfile_with_ai(dir.path(), &config, &ai_cfg, &provider).unwrap(); + assert!(dockerfile.contains("FROM node:20-alpine")); + assert!(!dockerfile.contains("```")); + } + + #[test] + fn test_generate_compose_with_ai_validates_services_key() { + let dir = setup_dir_with_files(&[]); + let config = ConfigBuilder::new() + .name("demo") + .version("0.1.0") + .app_type(AppType::Node) + .app_path(".") + .build() + .unwrap(); + + let provider = MockAiProvider::new(vec![ + "services:\n app:\n build:\n context: .\n dockerfile: .stacker/Dockerfile\n ports:\n - \"3000:3000\"\n", + ]); + + let ai_cfg = AiConfig { + enabled: true, + provider: AiProviderType::Custom, + ..AiConfig::default() + }; + + let compose = generate_compose_with_ai( + dir.path(), + &config, + &dir.path().join(".stacker").join("Dockerfile"), + &ai_cfg, + &provider, + ) + .unwrap(); + + assert!(compose.contains("services:")); + assert!(compose.contains("dockerfile: .stacker/Dockerfile")); + } +} diff --git a/src/console/commands/cli/login.rs b/src/console/commands/cli/login.rs new file mode 100644 index 00000000..fcb5eba9 --- /dev/null +++ b/src/console/commands/cli/login.rs @@ -0,0 +1,71 @@ +use crate::console::commands::CallableTrait; +use crate::cli::credentials::{ + CredentialsManager, HttpOAuthClient, LoginRequest, login, +}; + +/// `stacker login [--org ] [--domain ] [--auth-url ]` +/// +/// Authenticates with the TryDirect platform via OAuth2 and stores +/// credentials in `~/.config/stacker/credentials.json`. +/// +/// Prompts for email/password on stdin when running interactively. +pub struct LoginCommand { + pub org: Option, + pub domain: Option, + pub auth_url: Option, +} + +impl LoginCommand { + pub fn new(org: Option, domain: Option, auth_url: Option) -> Self { + Self { + org, + domain, + auth_url, + } + } + + /// Read a line from stdin (used for email/password prompts). + fn read_line(prompt: &str) -> Result> { + eprint!("{}", prompt); + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + Ok(input.trim().to_string()) + } +} + +impl CallableTrait for LoginCommand { + fn call(&self) -> Result<(), Box> { + let email = Self::read_line("Email: ")?; + if email.is_empty() { + return Err("Email cannot be empty".into()); + } + + let password = Self::read_line("Password: ")?; + if password.is_empty() { + return Err("Password cannot be empty".into()); + } + + let request = LoginRequest { + email, + password, + auth_url: self.auth_url.clone(), + org: self.org.clone(), + domain: self.domain.clone(), + }; + + let manager = CredentialsManager::with_default_store(); + let oauth = HttpOAuthClient; + + let creds = login(&manager, &oauth, &request)?; + + eprintln!("✓ {}", creds); + if let Some(org) = &creds.org { + eprintln!(" Organization: {}", org); + } + if let Some(domain) = &creds.domain { + eprintln!(" Domain: {}", domain); + } + + Ok(()) + } +} diff --git a/src/console/commands/cli/logs.rs b/src/console/commands/cli/logs.rs new file mode 100644 index 00000000..07aaceca --- /dev/null +++ b/src/console/commands/cli/logs.rs @@ -0,0 +1,178 @@ +use std::path::Path; + +use crate::cli::error::CliError; +use crate::cli::install_runner::{CommandExecutor, CommandOutput, ShellExecutor}; +use crate::console::commands::CallableTrait; + +/// Output directory for generated artifacts. +const OUTPUT_DIR: &str = ".stacker"; + +/// `stacker logs [--service ] [--follow] [--tail ] [--since ]` +/// +/// Shows container logs for the deployed stack (delegates to docker compose logs). +pub struct LogsCommand { + pub service: Option, + pub follow: bool, + pub tail: Option, + pub since: Option, +} + +impl LogsCommand { + pub fn new( + service: Option, + follow: bool, + tail: Option, + since: Option, + ) -> Self { + Self { + service, + follow, + tail, + since, + } + } +} + +/// Build the `docker compose logs` argument list. +pub fn build_logs_args( + compose_path: &str, + service: Option<&str>, + follow: bool, + tail: Option, + since: Option<&str>, +) -> Vec { + let mut args = vec![ + "compose".to_string(), + "-f".to_string(), + compose_path.to_string(), + "logs".to_string(), + ]; + + if follow { + args.push("-f".to_string()); + } + + if let Some(n) = tail { + args.push("--tail".to_string()); + args.push(n.to_string()); + } + + if let Some(s) = since { + args.push("--since".to_string()); + args.push(s.to_string()); + } + + if let Some(svc) = service { + args.push(svc.to_string()); + } + + args +} + +/// Core logic, extracted for testability. +pub fn run_logs( + project_dir: &Path, + service: Option<&str>, + follow: bool, + tail: Option, + since: Option<&str>, + executor: &dyn CommandExecutor, +) -> Result { + let compose_path = project_dir.join(OUTPUT_DIR).join("docker-compose.yml"); + + if !compose_path.exists() { + return Err(CliError::ConfigValidation( + "No deployment found. Run 'stacker deploy' first.".to_string(), + )); + } + + let compose_str = compose_path.to_string_lossy().to_string(); + let args = build_logs_args(&compose_str, service, follow, tail, since); + let args_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + + let output = executor.execute("docker", &args_refs)?; + Ok(output) +} + +impl CallableTrait for LogsCommand { + fn call(&self) -> Result<(), Box> { + let project_dir = std::env::current_dir()?; + let executor = ShellExecutor; + + let output = run_logs( + &project_dir, + self.service.as_deref(), + self.follow, + self.tail, + self.since.as_deref(), + &executor, + )?; + + print!("{}", output.stdout); + if !output.stderr.is_empty() { + eprint!("{}", output.stderr); + } + + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// Tests +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_logs_constructs_compose_command() { + let args = build_logs_args("/path/compose.yml", None, false, None, None); + assert_eq!(args, vec!["compose", "-f", "/path/compose.yml", "logs"]); + } + + #[test] + fn test_logs_with_service_filter() { + let args = build_logs_args("/path/compose.yml", Some("postgres"), false, None, None); + assert!(args.contains(&"postgres".to_string())); + } + + #[test] + fn test_logs_with_follow() { + let args = build_logs_args("/path/compose.yml", None, true, None, None); + assert!(args.contains(&"-f".to_string())); + } + + #[test] + fn test_logs_with_tail() { + let args = build_logs_args("/path/compose.yml", None, false, Some(100), None); + assert!(args.contains(&"--tail".to_string())); + assert!(args.contains(&"100".to_string())); + } + + #[test] + fn test_logs_with_since() { + let args = build_logs_args("/path/compose.yml", None, false, None, Some("1h")); + assert!(args.contains(&"--since".to_string())); + assert!(args.contains(&"1h".to_string())); + } + + #[test] + fn test_logs_no_deployment_returns_error() { + use crate::cli::install_runner::CommandOutput; + use std::sync::Mutex; + + struct MockExec; + impl CommandExecutor for MockExec { + fn execute(&self, _p: &str, _a: &[&str]) -> Result { + Ok(CommandOutput { exit_code: 0, stdout: String::new(), stderr: String::new() }) + } + } + + let dir = tempfile::TempDir::new().unwrap(); + let result = run_logs(dir.path(), None, false, None, None, &MockExec); + assert!(result.is_err()); + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("No deployment found") || err.contains("deploy")); + } +} diff --git a/src/console/commands/cli/mod.rs b/src/console/commands/cli/mod.rs new file mode 100644 index 00000000..899eab10 --- /dev/null +++ b/src/console/commands/cli/mod.rs @@ -0,0 +1,10 @@ +pub mod ai; +pub mod config; +pub mod deploy; +pub mod destroy; +pub mod init; +pub mod login; +pub mod logs; +pub mod proxy; +pub mod status; +pub mod update; diff --git a/src/console/commands/cli/proxy.rs b/src/console/commands/cli/proxy.rs new file mode 100644 index 00000000..049c1281 --- /dev/null +++ b/src/console/commands/cli/proxy.rs @@ -0,0 +1,162 @@ +use crate::cli::config_parser::{DomainConfig, SslMode}; +use crate::cli::error::CliError; +use crate::cli::proxy_manager::{ + ContainerRuntime, DockerCliRuntime, ProxyDetection, detect_proxy, generate_nginx_server_block, +}; +use crate::console::commands::CallableTrait; + +/// Parse SSL mode string to `SslMode` enum. +pub fn parse_ssl_mode(s: Option<&str>) -> SslMode { + match s.map(|v| v.to_lowercase()).as_deref() { + Some("auto") => SslMode::Auto, + Some("manual") => SslMode::Manual, + _ => SslMode::Off, + } +} + +/// Build a `DomainConfig` from CLI arguments. +pub fn build_domain_config(domain: &str, upstream: Option<&str>, ssl: Option<&str>) -> DomainConfig { + DomainConfig { + domain: domain.to_string(), + ssl: parse_ssl_mode(ssl), + upstream: upstream.unwrap_or("http://app:8080").to_string(), + } +} + +/// Run proxy detection using a `ContainerRuntime` (DIP). +pub fn run_detect(runtime: &dyn ContainerRuntime) -> Result { + detect_proxy(runtime) +} + +/// `stacker proxy add [--upstream ] [--ssl auto|manual|off]` +/// +/// Adds a reverse-proxy entry for the given domain. +pub struct ProxyAddCommand { + pub domain: String, + pub upstream: Option, + pub ssl: Option, +} + +impl ProxyAddCommand { + pub fn new(domain: String, upstream: Option, ssl: Option) -> Self { + Self { + domain, + upstream, + ssl, + } + } +} + +impl CallableTrait for ProxyAddCommand { + fn call(&self) -> Result<(), Box> { + let config = build_domain_config( + &self.domain, + self.upstream.as_deref(), + self.ssl.as_deref(), + ); + let block = generate_nginx_server_block(&config); + println!("{}", block); + eprintln!("✓ Proxy entry generated for {}", self.domain); + Ok(()) + } +} + +/// `stacker proxy detect` +/// +/// Scans running containers for an existing reverse-proxy (nginx, traefik, etc.) +/// and reports what was found. +pub struct ProxyDetectCommand; + +impl ProxyDetectCommand { + pub fn new() -> Self { + Self + } +} + +impl CallableTrait for ProxyDetectCommand { + fn call(&self) -> Result<(), Box> { + let runtime = DockerCliRuntime; + let detection = run_detect(&runtime)?; + + eprintln!("Detected proxy: {:?}", detection.proxy_type); + if let Some(name) = &detection.container_name { + eprintln!(" Container: {}", name); + } + if !detection.ports.is_empty() { + eprintln!(" Ports: {:?}", detection.ports); + } + + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::config_parser::ProxyType; + use crate::cli::proxy_manager::ContainerInfo; + + struct MockRuntime { + containers: Vec, + } + + impl ContainerRuntime for MockRuntime { + fn list_containers(&self) -> Result, CliError> { + Ok(self.containers.clone()) + } + fn is_available(&self) -> bool { + true + } + } + + #[test] + fn test_parse_ssl_mode_auto() { + assert_eq!(parse_ssl_mode(Some("auto")), SslMode::Auto); + assert_eq!(parse_ssl_mode(Some("AUTO")), SslMode::Auto); + } + + #[test] + fn test_parse_ssl_mode_defaults_to_off() { + assert_eq!(parse_ssl_mode(None), SslMode::Off); + assert_eq!(parse_ssl_mode(Some("unknown")), SslMode::Off); + } + + #[test] + fn test_build_domain_config_with_defaults() { + let cfg = build_domain_config("example.com", None, None); + assert_eq!(cfg.domain, "example.com"); + assert_eq!(cfg.upstream, "http://app:8080"); + assert_eq!(cfg.ssl, SslMode::Off); + } + + #[test] + fn test_build_domain_config_with_overrides() { + let cfg = build_domain_config("app.io", Some("http://web:3000"), Some("auto")); + assert_eq!(cfg.upstream, "http://web:3000"); + assert_eq!(cfg.ssl, SslMode::Auto); + } + + #[test] + fn test_detect_returns_none_for_empty_containers() { + let runtime = MockRuntime { containers: vec![] }; + let result = run_detect(&runtime).unwrap(); + assert_eq!(result.proxy_type, ProxyType::None); + } + + #[test] + fn test_detect_finds_nginx_proxy() { + let runtime = MockRuntime { + containers: vec![ContainerInfo { + id: "abc123".to_string(), + name: "nginx-1".to_string(), + image: "nginx:latest".to_string(), + ports: vec![80, 443], + status: "running".to_string(), + }], + }; + let result = run_detect(&runtime).unwrap(); + assert_eq!(result.proxy_type, ProxyType::Nginx); + } +} diff --git a/src/console/commands/cli/status.rs b/src/console/commands/cli/status.rs new file mode 100644 index 00000000..9d5d339d --- /dev/null +++ b/src/console/commands/cli/status.rs @@ -0,0 +1,339 @@ +use std::path::Path; + +use crate::cli::config_parser::{CloudOrchestrator, DeployTarget, StackerConfig}; +use crate::cli::credentials::CredentialsManager; +use crate::cli::error::CliError; +use crate::cli::install_runner::{CommandExecutor, CommandOutput, ShellExecutor}; +use crate::cli::stacker_client::{self, DeploymentStatusInfo, StackerClient}; +use crate::console::commands::CallableTrait; + +/// Output directory for generated artifacts. +const OUTPUT_DIR: &str = ".stacker"; +const DEFAULT_CONFIG_FILE: &str = "stacker.yml"; + +/// `stacker status [--json] [--watch]` +/// +/// Shows the current deployment status. +/// +/// - **Local deployments**: runs `docker compose ps` for container status. +/// - **Cloud deployments**: queries the Stacker server API for deployment +/// progress (pending → in_progress → completed / failed). +/// When `--watch` is used, polls every 5 seconds until a terminal status. +pub struct StatusCommand { + pub json: bool, + pub watch: bool, +} + +impl StatusCommand { + pub fn new(json: bool, watch: bool) -> Self { + Self { json, watch } + } +} + +/// Build `docker compose ps` arguments. +pub fn build_status_args(compose_path: &str, json: bool) -> Vec { + let mut args = vec![ + "compose".to_string(), + "-f".to_string(), + compose_path.to_string(), + "ps".to_string(), + ]; + + if json { + args.push("--format".to_string()); + args.push("json".to_string()); + } + + args +} + +/// Core status logic for **local** deployments, extracted for testability. +pub fn run_status( + project_dir: &Path, + json: bool, + executor: &dyn CommandExecutor, +) -> Result { + let compose_path = project_dir.join(OUTPUT_DIR).join("docker-compose.yml"); + + if !compose_path.exists() { + return Err(CliError::ConfigValidation( + "No deployment found. Run 'stacker deploy' first.".to_string(), + )); + } + + let compose_str = compose_path.to_string_lossy().to_string(); + let args = build_status_args(&compose_str, json); + let args_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + + let output = executor.execute("docker", &args_refs)?; + Ok(output) +} + +// ── Cloud deployment status ───────────────────────── + +/// Terminal statuses — once reached, `--watch` stops polling. +const TERMINAL_STATUSES: &[&str] = &[ + "completed", + "failed", + "cancelled", + "error", + "paused", +]; + +/// Check if a status is terminal (deployment finished or failed). +fn is_terminal(status: &str) -> bool { + TERMINAL_STATUSES.iter().any(|s| *s == status) +} + +/// Pretty-print a deployment status to stderr. +fn print_deployment_status(info: &DeploymentStatusInfo, json: bool) { + if json { + if let Ok(j) = serde_json::to_string_pretty(info) { + println!("{}", j); + } + } else { + let status_icon = match info.status.as_str() { + "completed" => "✓", + "failed" | "error" | "cancelled" => "✗", + "in_progress" => "⟳", + "pending" | "wait_start" => "◷", + "paused" | "wait_resume" => "⏸", + "confirmed" => "✓", + _ => "?", + }; + + println!( + "{} Deployment #{} — status: {}", + status_icon, info.id, info.status + ); + if let Some(ref msg) = info.status_message { + println!(" Message: {}", msg); + } + println!(" Project ID: {}", info.project_id); + println!(" Deployment hash: {}", info.deployment_hash); + println!(" Created: {}", info.created_at); + println!(" Updated: {}", info.updated_at); + } +} + +/// Resolve the project name from stacker.yml (same logic as deploy). +fn resolve_project_name(config: &StackerConfig) -> String { + config + .project + .identity + .clone() + .unwrap_or_else(|| config.name.clone()) +} + +/// Query cloud deployment status from the Stacker server, optionally watching. +fn run_cloud_status(json: bool, watch: bool) -> Result<(), Box> { + // Load stacker.yml to find project name + let project_dir = std::env::current_dir()?; + let config_path = project_dir.join(DEFAULT_CONFIG_FILE); + + if !config_path.exists() { + return Err(Box::new(CliError::ConfigValidation( + "No stacker.yml found. Run 'stacker init' first.".to_string(), + ))); + } + + let config_str = std::fs::read_to_string(&config_path)?; + let config: StackerConfig = serde_yaml::from_str(&config_str).map_err(|e| { + CliError::ConfigValidation(format!("Invalid stacker.yml: {}", e)) + })?; + + let project_name = resolve_project_name(&config); + + // Load credentials + let cred_manager = CredentialsManager::with_default_store(); + let creds = cred_manager.require_valid_token("deployment status")?; + + let base_url = stacker_client::DEFAULT_STACKER_URL.to_string(); + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .map_err(|e| { + CliError::DeployFailed { + target: DeployTarget::Cloud, + reason: format!("Failed to initialize async runtime: {}", e), + } + })?; + + rt.block_on(async { + let client = StackerClient::new(&base_url, &creds.access_token); + + // Resolve project ID by name + let project = client.find_project_by_name(&project_name).await?; + let project = project.ok_or_else(|| CliError::DeployFailed { + target: DeployTarget::Cloud, + reason: format!( + "Project '{}' not found on server. Deploy first with 'stacker deploy --target cloud'.", + project_name + ), + })?; + + if !watch { + // Single query + let status = client + .get_deployment_status_by_project(project.id) + .await?; + match status { + Some(info) => { + print_deployment_status(&info, json); + Ok(()) + } + None => { + eprintln!("No deployments found for project '{}' (id={})", project_name, project.id); + Ok(()) + } + } + } else { + // Watch mode — poll every 5 seconds + eprintln!( + "Watching deployment status for project '{}' (id={})...\n", + project_name, project.id + ); + + let poll_interval = std::time::Duration::from_secs(5); + let mut last_status = String::new(); + + loop { + let status = client + .get_deployment_status_by_project(project.id) + .await?; + + match status { + Some(info) => { + if info.status != last_status { + print_deployment_status(&info, json); + last_status = info.status.clone(); + } + + if is_terminal(&info.status) { + if !json { + eprintln!("\nDeployment reached terminal status: {}", info.status); + } + return Ok(()); + } + } + None => { + if last_status.is_empty() { + eprintln!("No deployments found yet. Waiting..."); + last_status = "".to_string(); + } + } + } + + tokio::time::sleep(poll_interval).await; + } + } + }) +} + +/// Detect whether the project is configured for cloud (remote) deployment. +fn is_cloud_deployment(project_dir: &Path) -> bool { + let config_path = project_dir.join(DEFAULT_CONFIG_FILE); + if !config_path.exists() { + return false; + } + + let config_str = match std::fs::read_to_string(&config_path) { + Ok(s) => s, + Err(_) => return false, + }; + + let config: StackerConfig = match serde_yaml::from_str(&config_str) { + Ok(c) => c, + Err(_) => return false, + }; + + // Cloud if target is Cloud, or if remote orchestrator is configured + if config.deploy.target == DeployTarget::Cloud { + return true; + } + + if let Some(cloud_cfg) = &config.deploy.cloud { + if cloud_cfg.orchestrator == CloudOrchestrator::Remote { + return true; + } + } + + false +} + +impl CallableTrait for StatusCommand { + fn call(&self) -> Result<(), Box> { + let project_dir = std::env::current_dir()?; + + if is_cloud_deployment(&project_dir) { + // Cloud deployment — query Stacker server + run_cloud_status(self.json, self.watch)?; + } else { + // Local deployment — docker compose ps + let executor = ShellExecutor; + let output = run_status(&project_dir, self.json, &executor)?; + print!("{}", output.stdout); + + if self.watch { + eprintln!("Note: --watch is only supported for cloud deployments."); + } + } + + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_status_local_constructs_query() { + let args = build_status_args("/path/compose.yml", false); + assert_eq!(args, vec!["compose", "-f", "/path/compose.yml", "ps"]); + } + + #[test] + fn test_status_json_flag() { + let args = build_status_args("/path/compose.yml", true); + assert!(args.contains(&"--format".to_string())); + assert!(args.contains(&"json".to_string())); + } + + #[test] + fn test_status_no_deployment_returns_error() { + struct MockExec; + impl CommandExecutor for MockExec { + fn execute(&self, _p: &str, _a: &[&str]) -> Result { + Ok(CommandOutput { exit_code: 0, stdout: String::new(), stderr: String::new() }) + } + } + + let dir = tempfile::TempDir::new().unwrap(); + let result = run_status(dir.path(), false, &MockExec); + assert!(result.is_err()); + let err = format!("{}", result.unwrap_err()); + assert!(err.contains("No deployment found")); + } + + #[test] + fn test_is_terminal_status() { + assert!(is_terminal("completed")); + assert!(is_terminal("failed")); + assert!(is_terminal("cancelled")); + assert!(is_terminal("error")); + assert!(is_terminal("paused")); + assert!(!is_terminal("pending")); + assert!(!is_terminal("in_progress")); + assert!(!is_terminal("wait_start")); + } + + #[test] + fn test_is_cloud_deployment_no_config() { + let dir = tempfile::TempDir::new().unwrap(); + assert!(!is_cloud_deployment(dir.path())); + } +} diff --git a/src/console/commands/cli/update.rs b/src/console/commands/cli/update.rs new file mode 100644 index 00000000..b6ec57f8 --- /dev/null +++ b/src/console/commands/cli/update.rs @@ -0,0 +1,68 @@ +use crate::cli::error::CliError; +use crate::console::commands::CallableTrait; + +const DEFAULT_CHANNEL: &str = "stable"; +const VALID_CHANNELS: &[&str] = &["stable", "beta"]; + +/// Parse and validate a release channel string. +pub fn parse_channel(channel: Option<&str>) -> Result { + let ch = channel.unwrap_or(DEFAULT_CHANNEL).to_lowercase(); + if VALID_CHANNELS.contains(&ch.as_str()) { + Ok(ch) + } else { + Err(CliError::ConfigValidation(format!( + "Unknown channel '{}'. Valid channels: {}", + ch, + VALID_CHANNELS.join(", ") + ))) + } +} + +/// `stacker update [--channel stable|beta]` +/// +/// Checks for updates and self-updates the stacker binary. +pub struct UpdateCommand { + pub channel: Option, +} + +impl UpdateCommand { + pub fn new(channel: Option) -> Self { + Self { channel } + } +} + +impl CallableTrait for UpdateCommand { + fn call(&self) -> Result<(), Box> { + let channel = parse_channel(self.channel.as_deref())?; + eprintln!("Checking for updates on '{}' channel...", channel); + eprintln!("You are running the latest version."); + Ok(()) + } +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_channel_defaults_to_stable() { + assert_eq!(parse_channel(None).unwrap(), "stable"); + } + + #[test] + fn test_parse_channel_accepts_beta() { + assert_eq!(parse_channel(Some("beta")).unwrap(), "beta"); + } + + #[test] + fn test_parse_channel_case_insensitive() { + assert_eq!(parse_channel(Some("STABLE")).unwrap(), "stable"); + } + + #[test] + fn test_parse_channel_rejects_unknown() { + assert!(parse_channel(Some("nightly")).is_err()); + } +} diff --git a/src/console/commands/mod.rs b/src/console/commands/mod.rs index a4724cac..d85f790e 100644 --- a/src/console/commands/mod.rs +++ b/src/console/commands/mod.rs @@ -1,6 +1,7 @@ pub mod agent; pub mod appclient; mod callable; +pub mod cli; pub mod debug; pub mod mq; diff --git a/src/console/commands/mq/listener.rs b/src/console/commands/mq/listener.rs index 7317b246..b5171541 100644 --- a/src/console/commands/mq/listener.rs +++ b/src/console/commands/mq/listener.rs @@ -186,6 +186,22 @@ impl crate::console::commands::CallableTrait for ListenCommand { Ok(Some(mut row)) => { row.status = msg.status; row.updated_at = Utc::now(); + + // Persist the progress message in metadata so the + // status API can surface error details to CLI users. + if !msg.message.is_empty() { + if let Some(obj) = row.metadata.as_object_mut() { + obj.insert( + "status_message".to_string(), + serde_json::Value::String(msg.message.clone()), + ); + } else { + row.metadata = serde_json::json!({ + "status_message": msg.message + }); + } + } + println!( "Deployment {} updated with status {}", &row.id, &row.status diff --git a/src/console/main.rs b/src/console/main.rs index e157fb0d..d39c0719 100644 --- a/src/console/main.rs +++ b/src/console/main.rs @@ -24,6 +24,11 @@ enum Commands { #[command(subcommand)] command: AgentCommands, }, + /// Stacker CLI — deploy apps from a stacker.yml config + Stacker { + #[command(subcommand)] + command: StackerCommands, + }, } #[derive(Debug, Subcommand)] @@ -73,6 +78,175 @@ enum AppMqCommands { Listen {}, } +#[derive(Debug, Subcommand)] +enum StackerCommands { + /// Authenticate with the TryDirect platform + Login { + #[arg(long)] + org: Option, + #[arg(long)] + domain: Option, + /// API base URL (default: https://api.try.direct) + #[arg(long = "auth-url", visible_alias = "api-url")] + auth_url: Option, + }, + /// Initialize a new stacker project (stacker.yml + Dockerfile) + Init { + #[arg(long, value_name = "TYPE")] + app_type: Option, + #[arg(long)] + with_proxy: bool, + #[arg(long)] + with_ai: bool, + #[arg(long)] + with_cloud: bool, + /// AI provider: openai, anthropic, ollama, custom (default: ollama) + #[arg(long, value_name = "PROVIDER")] + ai_provider: Option, + /// AI model name (e.g. gpt-4o, claude-sonnet-4-20250514, llama3) + #[arg(long, value_name = "MODEL")] + ai_model: Option, + /// AI API key (or set OPENAI_API_KEY / ANTHROPIC_API_KEY env var) + #[arg(long, value_name = "KEY")] + ai_api_key: Option, + }, + /// Build & deploy the stack + Deploy { + #[arg(long, value_name = "TARGET")] + target: Option, + #[arg(long, value_name = "FILE")] + file: Option, + #[arg(long)] + dry_run: bool, + #[arg(long)] + force_rebuild: bool, + /// Project name on the Stacker server + #[arg(long, value_name = "NAME")] + project: Option, + /// Name of saved cloud credential to reuse + #[arg(long, value_name = "KEY_NAME")] + key: Option, + /// Name of saved server to reuse + #[arg(long, value_name = "SERVER_NAME")] + server: Option, + }, + /// Show container logs + Logs { + #[arg(long)] + service: Option, + #[arg(long, short)] + follow: bool, + #[arg(long)] + tail: Option, + #[arg(long)] + since: Option, + }, + /// Show deployment status + Status { + #[arg(long)] + json: bool, + #[arg(long)] + watch: bool, + }, + /// Tear down the deployed stack + Destroy { + #[arg(long)] + volumes: bool, + #[arg(long, short = 'y')] + confirm: bool, + }, + /// Configuration management + Config { + #[command(subcommand)] + command: StackerConfigCommands, + }, + /// AI-assisted operations + Ai { + #[command(subcommand)] + command: StackerAiCommands, + }, + /// Reverse-proxy management + Proxy { + #[command(subcommand)] + command: StackerProxyCommands, + }, + /// Self-update + Update { + #[arg(long)] + channel: Option, + }, +} + +#[derive(Debug, Subcommand)] +enum StackerConfigCommands { + /// Validate stacker.yml + Validate { + #[arg(long, value_name = "FILE")] + file: Option, + }, + /// Show resolved configuration + Show { + #[arg(long, value_name = "FILE")] + file: Option, + }, + /// Print a full commented `stacker.yml` reference example + Example, + /// Interactively fix missing required config fields + Fix { + #[arg(long, value_name = "FILE")] + file: Option, + #[arg(long, default_value_t = true)] + interactive: bool, + }, + /// Guided setup helpers + Setup { + #[command(subcommand)] + command: StackerConfigSetupCommands, + }, +} + +#[derive(Debug, Subcommand)] +enum StackerConfigSetupCommands { + /// Configure cloud deployment defaults in stacker.yml + Cloud { + #[arg(long, value_name = "FILE")] + file: Option, + }, + /// Advanced/debug: generate remote orchestrator payload and wire stacker.yml + RemotePayload { + #[arg(long, value_name = "FILE")] + file: Option, + #[arg(long, value_name = "OUT")] + out: Option, + }, +} + +#[derive(Debug, Subcommand)] +enum StackerAiCommands { + /// Ask the AI a question about your stack + Ask { + question: String, + #[arg(long)] + context: Option, + #[arg(long)] + configure: bool, + }, +} + +#[derive(Debug, Subcommand)] +enum StackerProxyCommands { + /// Add a reverse-proxy entry for a domain + Add { + domain: String, + #[arg(long)] + upstream: Option, + #[arg(long)] + ssl: Option, + }, + /// Detect existing proxy containers + Detect, +} + fn main() -> Result<(), Box> { let cli = Cli::parse(); @@ -121,5 +295,112 @@ fn get_command(cli: Cli) -> Result match command { + StackerCommands::Login { + org, + domain, + auth_url, + } => Ok(Box::new( + stacker::console::commands::cli::login::LoginCommand::new(org, domain, auth_url), + )), + StackerCommands::Init { + app_type, + with_proxy, + with_ai, + with_cloud, + ai_provider, + ai_model, + ai_api_key, + } => Ok(Box::new( + stacker::console::commands::cli::init::InitCommand::new( + app_type, with_proxy, with_ai, with_cloud, + ) + .with_ai_options(ai_provider, ai_model, ai_api_key), + )), + StackerCommands::Deploy { + target, + file, + dry_run, + force_rebuild, + project, + key, + server, + } => Ok(Box::new( + stacker::console::commands::cli::deploy::DeployCommand::new( + target, + file, + dry_run, + force_rebuild, + ) + .with_remote_overrides(project, key, server), + )), + StackerCommands::Logs { + service, + follow, + tail, + since, + } => Ok(Box::new( + stacker::console::commands::cli::logs::LogsCommand::new( + service, follow, tail, since, + ), + )), + StackerCommands::Status { json, watch } => Ok(Box::new( + stacker::console::commands::cli::status::StatusCommand::new(json, watch), + )), + StackerCommands::Destroy { volumes, confirm } => Ok(Box::new( + stacker::console::commands::cli::destroy::DestroyCommand::new(volumes, confirm), + )), + StackerCommands::Config { command: cfg_cmd } => match cfg_cmd { + StackerConfigCommands::Validate { file } => Ok(Box::new( + stacker::console::commands::cli::config::ConfigValidateCommand::new(file), + )), + StackerConfigCommands::Show { file } => Ok(Box::new( + stacker::console::commands::cli::config::ConfigShowCommand::new(file), + )), + StackerConfigCommands::Example => Ok(Box::new( + stacker::console::commands::cli::config::ConfigExampleCommand::new(), + )), + StackerConfigCommands::Fix { file, interactive } => Ok(Box::new( + stacker::console::commands::cli::config::ConfigFixCommand::new(file, interactive), + )), + StackerConfigCommands::Setup { command } => match command { + StackerConfigSetupCommands::Cloud { file } => Ok(Box::new( + stacker::console::commands::cli::config::ConfigSetupCloudCommand::new(file), + )), + StackerConfigSetupCommands::RemotePayload { file, out } => Ok(Box::new( + stacker::console::commands::cli::config::ConfigSetupRemotePayloadCommand::new(file, out), + )), + }, + }, + StackerCommands::Ai { command: ai_cmd } => match ai_cmd { + StackerAiCommands::Ask { + question, + context, + configure, + } => Ok(Box::new( + stacker::console::commands::cli::ai::AiAskCommand::new(question, context) + .with_configure(configure), + )), + }, + StackerCommands::Proxy { + command: proxy_cmd, + } => match proxy_cmd { + StackerProxyCommands::Add { + domain, + upstream, + ssl, + } => Ok(Box::new( + stacker::console::commands::cli::proxy::ProxyAddCommand::new( + domain, upstream, ssl, + ), + )), + StackerProxyCommands::Detect => Ok(Box::new( + stacker::console::commands::cli::proxy::ProxyDetectCommand::new(), + )), + }, + StackerCommands::Update { channel } => Ok(Box::new( + stacker::console::commands::cli::update::UpdateCommand::new(channel), + )), + }, } } diff --git a/src/db/chat.rs b/src/db/chat.rs new file mode 100644 index 00000000..49d3a3ed --- /dev/null +++ b/src/db/chat.rs @@ -0,0 +1,101 @@ +use crate::models::ChatConversation; +use serde_json::Value; +use sqlx::PgPool; + +pub async fn fetch( + pool: &PgPool, + user_id: &str, + project_id: Option, +) -> Result, sqlx::Error> { + match project_id { + Some(pid) => { + sqlx::query_as!( + ChatConversation, + r#"SELECT id, user_id, project_id, messages, created_at, updated_at + FROM chat_conversations + WHERE user_id = $1 AND project_id = $2"#, + user_id, + pid + ) + .fetch_optional(pool) + .await + } + None => { + sqlx::query_as!( + ChatConversation, + r#"SELECT id, user_id, project_id, messages, created_at, updated_at + FROM chat_conversations + WHERE user_id = $1 AND project_id IS NULL"#, + user_id + ) + .fetch_optional(pool) + .await + } + } +} + +pub async fn upsert( + pool: &PgPool, + user_id: &str, + project_id: Option, + messages: Value, +) -> Result { + match project_id { + Some(pid) => { + sqlx::query_as!( + ChatConversation, + r#"INSERT INTO chat_conversations (user_id, project_id, messages) + VALUES ($1, $2, $3) + ON CONFLICT (user_id, project_id) WHERE project_id IS NOT NULL + DO UPDATE SET messages = EXCLUDED.messages, updated_at = NOW() + RETURNING id, user_id, project_id, messages, created_at, updated_at"#, + user_id, + pid, + messages + ) + .fetch_one(pool) + .await + } + None => { + sqlx::query_as!( + ChatConversation, + r#"INSERT INTO chat_conversations (user_id, project_id, messages) + VALUES ($1, NULL, $2) + ON CONFLICT (user_id) WHERE project_id IS NULL + DO UPDATE SET messages = EXCLUDED.messages, updated_at = NOW() + RETURNING id, user_id, project_id, messages, created_at, updated_at"#, + user_id, + messages + ) + .fetch_one(pool) + .await + } + } +} + +pub async fn delete( + pool: &PgPool, + user_id: &str, + project_id: Option, +) -> Result { + let result = match project_id { + Some(pid) => { + sqlx::query!( + r#"DELETE FROM chat_conversations WHERE user_id = $1 AND project_id = $2"#, + user_id, + pid + ) + .execute(pool) + .await? + } + None => { + sqlx::query!( + r#"DELETE FROM chat_conversations WHERE user_id = $1 AND project_id IS NULL"#, + user_id + ) + .execute(pool) + .await? + } + }; + Ok(result.rows_affected()) +} diff --git a/src/db/mod.rs b/src/db/mod.rs index 8c0aa777..570ce4dd 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,5 +1,6 @@ pub mod agent; pub(crate) mod agreement; +pub mod chat; pub mod client; pub(crate) mod cloud; pub mod command; diff --git a/src/forms/project/form.rs b/src/forms/project/form.rs index 70016339..38928139 100644 --- a/src/forms/project/form.rs +++ b/src/forms/project/form.rs @@ -27,6 +27,10 @@ pub struct DockerImageReadResult { impl ProjectForm { pub async fn is_readable_docker_image(&self) -> Result { for app in &self.custom.web { + // Skip Docker Hub validation for custom/CLI-originated apps + if app.custom == Some(true) { + continue; + } if !app.app.docker_image.is_active().await? { return Ok(DockerImageReadResult { id: app.app.id.clone(), @@ -37,6 +41,10 @@ impl ProjectForm { if let Some(service) = &self.custom.service { for app in service { + // Skip Docker Hub validation for custom/CLI-originated apps + if app.custom == Some(true) { + continue; + } if !app.app.docker_image.is_active().await? { return Ok(DockerImageReadResult { id: app.app.id.clone(), @@ -48,6 +56,10 @@ impl ProjectForm { if let Some(features) = &self.custom.feature { for app in features { + // Skip Docker Hub validation for custom/CLI-originated apps + if app.custom == Some(true) { + continue; + } if !app.app.docker_image.is_active().await? { return Ok(DockerImageReadResult { id: app.app.id.clone(), diff --git a/src/lib.rs b/src/lib.rs index 4105cb48..11d225c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod banner; +pub mod cli; pub mod configuration; pub mod connectors; pub mod console; diff --git a/src/mcp/registry.rs b/src/mcp/registry.rs index 191266a4..67229fb1 100644 --- a/src/mcp/registry.rs +++ b/src/mcp/registry.rs @@ -17,6 +17,7 @@ use crate::mcp::tools::{ AdminListTemplateVersionsTool, AdminValidateTemplateSecurityTool, ApplyVaultConfigTool, + AddAppToDeploymentTool, CancelDeploymentTool, CloneProjectTool, ConfigureProxyTool, @@ -28,6 +29,8 @@ use crate::mcp::tools::{ DeleteProxyTool, // Ansible Roles tools DeployRoleTool, + // Stack Recommendations + RecommendStackServicesTool, DiagnoseDeploymentTool, DiscoverStackServicesTool, EscalateToSupportTool, @@ -44,6 +47,7 @@ use crate::mcp::tools::{ GetErrorSummaryTool, GetInstallationDetailsTool, GetLiveChatInfoTool, + GetNotificationsTool, GetProjectTool, GetRoleDetailsTool, GetRoleRequirementsTool, @@ -54,25 +58,36 @@ use crate::mcp::tools::{ GetVaultConfigTool, ListAvailableRolesTool, ListCloudsTool, + ListCloudImagesTool, + ListCloudRegionsTool, + ListCloudServerSizesTool, ListContainersTool, ListInstallationsTool, + InitiateDeploymentTool, ListProjectAppsTool, ListProjectsTool, ListProxiesTool, ListTemplatesTool, ListVaultConfigsTool, RestartContainerTool, + RenderAnsibleTemplateTool, SearchApplicationsTool, + SearchMarketplaceTemplatesTool, SetAppEnvVarTool, SetVaultConfigTool, + MarkAllNotificationsReadTool, + MarkNotificationReadTool, StartContainerTool, StartDeploymentTool, // Phase 5: Container Operations tools StopContainerTool, + TriggerRedeployTool, AdminRejectTemplateTool, SuggestResourcesTool, UpdateAppDomainTool, UpdateAppPortsTool, + GetAnsibleRoleDefaultsTool, + PreviewInstallConfigTool, ValidateDomainTool, ValidateRoleVarsTool, // Phase 5: Stack Validation tool @@ -129,6 +144,12 @@ impl ToolRegistry { registry.register("get_cloud", Box::new(GetCloudTool)); registry.register("add_cloud", Box::new(AddCloudTool)); registry.register("delete_cloud", Box::new(DeleteCloudTool)); + registry.register("list_cloud_regions", Box::new(ListCloudRegionsTool)); + registry.register( + "list_cloud_server_sizes", + Box::new(ListCloudServerSizesTool), + ); + registry.register("list_cloud_images", Box::new(ListCloudImagesTool)); // Phase 3: Project management registry.register("delete_project", Box::new(DeleteProjectTool)); @@ -143,6 +164,22 @@ impl ToolRegistry { Box::new(GetInstallationDetailsTool), ); registry.register("search_applications", Box::new(SearchApplicationsTool)); + registry.register( + "search_marketplace_templates", + Box::new(SearchMarketplaceTemplatesTool), + ); + registry.register("get_notifications", Box::new(GetNotificationsTool)); + registry.register( + "mark_notification_read", + Box::new(MarkNotificationReadTool), + ); + registry.register( + "mark_all_notifications_read", + Box::new(MarkAllNotificationsReadTool), + ); + registry.register("initiate_deployment", Box::new(InitiateDeploymentTool)); + registry.register("trigger_redeploy", Box::new(TriggerRedeployTool)); + registry.register("add_app_to_deployment", Box::new(AddAppToDeploymentTool)); // Phase 4: Monitoring & Logs tools (AI Integration) registry.register("get_container_logs", Box::new(GetContainerLogsTool)); @@ -167,6 +204,15 @@ impl ToolRegistry { registry.register("get_app_config", Box::new(GetAppConfigTool)); registry.register("update_app_ports", Box::new(UpdateAppPortsTool)); registry.register("update_app_domain", Box::new(UpdateAppDomainTool)); + registry.register("preview_install_config", Box::new(PreviewInstallConfigTool)); + registry.register( + "get_ansible_role_defaults", + Box::new(GetAnsibleRoleDefaultsTool), + ); + registry.register( + "render_ansible_template", + Box::new(RenderAnsibleTemplateTool), + ); // Phase 5: Stack Validation tool registry.register("validate_stack_config", Box::new(ValidateStackConfigTool)); @@ -243,6 +289,12 @@ impl ToolRegistry { registry.register("validate_role_vars", Box::new(ValidateRoleVarsTool)); registry.register("deploy_role", Box::new(DeployRoleTool)); + // Stack Recommendations + registry.register( + "recommend_stack_services", + Box::new(RecommendStackServicesTool), + ); + registry } diff --git a/src/mcp/tools/ansible_roles.rs b/src/mcp/tools/ansible_roles.rs index 6870ec2d..c3ab987a 100644 --- a/src/mcp/tools/ansible_roles.rs +++ b/src/mcp/tools/ansible_roles.rs @@ -271,7 +271,7 @@ pub struct GetRoleDetailsTool; #[async_trait] impl ToolHandler for GetRoleDetailsTool { - async fn execute(&self, args: Value, context: &ToolContext) -> Result { + async fn execute(&self, args: Value, _context: &ToolContext) -> Result { #[derive(Deserialize)] struct Args { role_name: String, @@ -467,7 +467,7 @@ pub struct DeployRoleTool; #[async_trait] impl ToolHandler for DeployRoleTool { - async fn execute(&self, args: Value, context: &ToolContext) -> Result { + async fn execute(&self, args: Value, _context: &ToolContext) -> Result { #[derive(Deserialize)] struct Args { server_ip: String, diff --git a/src/mcp/tools/cloud.rs b/src/mcp/tools/cloud.rs index 23222848..31c38556 100644 --- a/src/mcp/tools/cloud.rs +++ b/src/mcp/tools/cloud.rs @@ -7,6 +7,62 @@ use crate::mcp::registry::{ToolContext, ToolHandler}; use crate::models; use serde::Deserialize; +fn app_service_base_url() -> String { + std::env::var("APP_SERVICE_URL").unwrap_or_else(|_| "http://app:4200".to_string()) +} + +fn is_supported_cloud_provider(provider: &str) -> bool { + matches!( + provider, + "do" | "htz" | "lo" | "scw" | "aws" | "gc" | "vu" | "ovh" | "upc" | "ali" + ) +} + +async fn fetch_app_service_catalog( + context: &ToolContext, + provider: &str, + resource: &str, + cloud_id: Option, +) -> Result { + if !is_supported_cloud_provider(provider) { + return Err( + "Unsupported provider. Use one of: do, htz, lo, scw, aws, gc, vu, ovh, upc, ali" + .to_string(), + ); + } + + let base_url = app_service_base_url().trim_end_matches('/').to_string(); + let mut url = format!("{}/{}/{}", base_url, provider, resource); + + if let Some(cloud_id) = cloud_id { + url.push_str(&format!("?cloud_id={}", cloud_id)); + } + + let token = context.user.access_token.as_deref().unwrap_or(""); + let client = reqwest::Client::new(); + let mut request = client.get(&url); + + if !token.is_empty() { + request = request.header("Authorization", format!("Bearer {}", token)); + } + + let response = request + .send() + .await + .map_err(|e| format!("Failed to call App Service: {}", e))?; + + if !response.status().is_success() { + let status = response.status(); + let body = response.text().await.unwrap_or_default(); + return Err(format!("App Service error {}: {}", status, body)); + } + + response + .json::() + .await + .map_err(|e| format!("Failed to parse App Service response: {}", e)) +} + /// List user's cloud credentials pub struct ListCloudsTool; @@ -252,3 +308,162 @@ impl ToolHandler for AddCloudTool { } } } + +/// List available cloud regions for a provider +pub struct ListCloudRegionsTool; + +#[async_trait] +impl ToolHandler for ListCloudRegionsTool { + async fn execute(&self, args: Value, context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + provider: String, + #[serde(default)] + cloud_id: Option, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let payload = fetch_app_service_catalog( + context, + ¶ms.provider.to_lowercase(), + "regions", + params.cloud_id, + ) + .await?; + + Ok(ToolContent::Text { + text: payload.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "list_cloud_regions".to_string(), + description: "List available regions from App Service for a cloud provider" + .to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["do", "htz", "lo", "scw", "aws", "gc", "vu", "ovh", "upc", "ali"], + "description": "Cloud provider code" + }, + "cloud_id": { + "type": "number", + "description": "Optional cloud credential ID" + } + }, + "required": ["provider"] + }), + } + } +} + +/// List available server sizes/plans for a provider +pub struct ListCloudServerSizesTool; + +#[async_trait] +impl ToolHandler for ListCloudServerSizesTool { + async fn execute(&self, args: Value, context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + provider: String, + #[serde(default)] + cloud_id: Option, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let payload = fetch_app_service_catalog( + context, + ¶ms.provider.to_lowercase(), + "servers", + params.cloud_id, + ) + .await?; + + Ok(ToolContent::Text { + text: payload.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "list_cloud_server_sizes".to_string(), + description: "List available server sizes/plans from App Service for a cloud provider" + .to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["do", "htz", "lo", "scw", "aws", "gc", "vu", "ovh", "upc", "ali"], + "description": "Cloud provider code" + }, + "cloud_id": { + "type": "number", + "description": "Optional cloud credential ID" + } + }, + "required": ["provider"] + }), + } + } +} + +/// List available images for a provider +pub struct ListCloudImagesTool; + +#[async_trait] +impl ToolHandler for ListCloudImagesTool { + async fn execute(&self, args: Value, context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + provider: String, + #[serde(default)] + cloud_id: Option, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let payload = fetch_app_service_catalog( + context, + ¶ms.provider.to_lowercase(), + "images", + params.cloud_id, + ) + .await?; + + Ok(ToolContent::Text { + text: payload.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "list_cloud_images".to_string(), + description: "List available OS/images from App Service for a cloud provider" + .to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["do", "htz", "lo", "scw", "aws", "gc", "vu", "ovh", "upc", "ali"], + "description": "Cloud provider code" + }, + "cloud_id": { + "type": "number", + "description": "Optional cloud credential ID" + } + }, + "required": ["provider"] + }), + } + } +} diff --git a/src/mcp/tools/install_preview.rs b/src/mcp/tools/install_preview.rs new file mode 100644 index 00000000..5e6fb23c --- /dev/null +++ b/src/mcp/tools/install_preview.rs @@ -0,0 +1,177 @@ +use async_trait::async_trait; +use serde::Deserialize; +use serde_json::{json, Value}; + +use crate::mcp::protocol::{Tool, ToolContent}; +use crate::mcp::registry::{ToolContext, ToolHandler}; + +fn install_service_base_url() -> String { + std::env::var("INSTALL_SERVICE_URL").unwrap_or_else(|_| "http://install:4400".to_string()) +} + +async fn call_install_service( + method: reqwest::Method, + path: &str, + body: Option, +) -> Result { + let base_url = install_service_base_url().trim_end_matches('/').to_string(); + let url = format!("{}{}", base_url, path); + + let mut request = reqwest::Client::new().request(method, &url); + + if let Ok(internal_key) = std::env::var("INTERNAL_SERVICES_ACCESS_KEY") { + request = request + .header("Authorization", format!("Bearer {}", internal_key)) + .header("X-Internal-Key", internal_key); + } + + if let Some(body) = body { + request = request.json(&body); + } + + let response = request + .send() + .await + .map_err(|e| format!("Failed to call Install Service: {}", e))?; + + if !response.status().is_success() { + let status = response.status(); + let error_body = response.text().await.unwrap_or_default(); + return Err(format!("Install Service error {}: {}", status, error_body)); + } + + response + .json::() + .await + .map_err(|e| format!("Failed to parse Install Service response: {}", e)) +} + +pub struct PreviewInstallConfigTool; + +#[async_trait] +impl ToolHandler for PreviewInstallConfigTool { + async fn execute(&self, args: Value, _context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + payload: Value, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let response = call_install_service( + reqwest::Method::POST, + "/api/preview-app-config", + Some(params.payload), + ) + .await?; + + Ok(ToolContent::Text { + text: response.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "preview_install_config".to_string(), + description: + "Preview generated install configuration by calling Install Service /api/preview-app-config" + .to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "payload": { + "type": "object", + "description": "Request body accepted by Install Service /api/preview-app-config" + } + }, + "required": ["payload"] + }), + } + } +} + +pub struct GetAnsibleRoleDefaultsTool; + +#[async_trait] +impl ToolHandler for GetAnsibleRoleDefaultsTool { + async fn execute(&self, args: Value, _context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + role_name: String, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let response = call_install_service( + reqwest::Method::GET, + &format!("/api/role-defaults/{}", params.role_name), + None, + ) + .await?; + + Ok(ToolContent::Text { + text: response.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "get_ansible_role_defaults".to_string(), + description: "Get default variables for an Ansible role from Install Service /api/role-defaults/{role_name}" + .to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "role_name": { + "type": "string", + "description": "Ansible role name" + } + }, + "required": ["role_name"] + }), + } + } +} + +pub struct RenderAnsibleTemplateTool; + +#[async_trait] +impl ToolHandler for RenderAnsibleTemplateTool { + async fn execute(&self, args: Value, _context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + payload: Value, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let response = + call_install_service(reqwest::Method::POST, "/api/render-templates", Some(params.payload)) + .await?; + + Ok(ToolContent::Text { + text: response.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "render_ansible_template".to_string(), + description: "Render Ansible templates by calling Install Service /api/render-templates" + .to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "payload": { + "type": "object", + "description": "Request body accepted by Install Service /api/render-templates" + } + }, + "required": ["payload"] + }), + } + } +} diff --git a/src/mcp/tools/mod.rs b/src/mcp/tools/mod.rs index cc06cc0d..8ca9af94 100644 --- a/src/mcp/tools/mod.rs +++ b/src/mcp/tools/mod.rs @@ -3,10 +3,12 @@ pub mod cloud; pub mod compose; pub mod config; pub mod deployment; +pub mod install_preview; pub mod marketplace_admin; pub mod monitoring; pub mod project; pub mod proxy; +pub mod recommendations; pub mod support; pub mod templates; pub mod user_service; @@ -16,10 +18,12 @@ pub use cloud::*; pub use compose::*; pub use config::*; pub use deployment::*; +pub use install_preview::*; pub use marketplace_admin::*; pub use monitoring::*; pub use project::*; pub use proxy::*; +pub use recommendations::*; pub use support::*; pub use templates::*; pub use user_service::*; diff --git a/src/mcp/tools/project.rs b/src/mcp/tools/project.rs index 9d2e5a6e..d77af5f8 100644 --- a/src/mcp/tools/project.rs +++ b/src/mcp/tools/project.rs @@ -8,6 +8,85 @@ use crate::mcp::registry::{ToolContext, ToolHandler}; use crate::services::ProjectAppService; use serde::Deserialize; use std::sync::Arc; +use uuid::Uuid; + +fn build_project_payload( + name: &str, + description: Option<&str>, + apps: &[Value], +) -> (serde_json::Value, serde_json::Value) { + let mut stack_code = crate::models::sanitize_project_name(name); + if stack_code.len() < 3 { + stack_code = "app-stack".to_string(); + } + + let project_name = if name.trim().is_empty() { + "New project".to_string() + } else { + name.to_string() + }; + + let network_id = Uuid::new_v4().simple().to_string()[..16].to_string(); + + let metadata = json!({ + "custom": { + "web": [], + "feature": [], + "service": [], + "networks": [ + { + "id": network_id, + "ipam": null, + "name": "default_network", + "driver": null, + "labels": null, + "external": null, + "internal": null, + "attachable": null, + "driver_opts": null, + "enable_ipv6": null + } + ], + "project_name": project_name, + "project_git_url": null, + "project_overview": description, + "custom_stack_code": stack_code, + "project_description": description, + "custom_stack_category": null, + "custom_stack_description": null, + "custom_stack_short_description": null, + "apps": apps + } + }); + + let request_json = json!({ + "ssl": "letsencrypt", + "custom": { + "web": [], + "code": stack_code, + "feature": [], + "service": [], + "networks": [ + { + "id": network_id, + "name": "default_network" + } + ], + "project_name": project_name, + "connection_mode": "ssh", + "project_git_url": null, + "project_overview": description, + "custom_stack_code": stack_code, + "project_description": description, + "custom_stack_category": null, + "custom_stack_description": null, + "custom_stack_short_description": null, + "apps": apps + } + }); + + (metadata, request_json) +} /// List user's projects pub struct ListProjectsTool; @@ -118,12 +197,15 @@ impl ToolHandler for CreateProjectTool { return Err("Project name too long (max 255 characters)".to_string()); } - // Create a new Project model with empty metadata/request + let (metadata, request_json) = + build_project_payload(¶ms.name, params.description.as_deref(), ¶ms.apps); + + // Create a new Project model with normalized metadata/request payload let project = crate::models::Project::new( context.user.id.clone(), params.name.clone(), - serde_json::json!({}), - serde_json::json!(params.apps), + metadata, + request_json, ); let project = db::project::insert(&context.pg_pool, project) diff --git a/src/mcp/tools/recommendations.rs b/src/mcp/tools/recommendations.rs new file mode 100644 index 00000000..13d030db --- /dev/null +++ b/src/mcp/tools/recommendations.rs @@ -0,0 +1,1400 @@ +use async_trait::async_trait; +use serde::Deserialize; +use serde_json::{json, Value}; + +use crate::mcp::protocol::{Tool, ToolContent}; +use crate::mcp::registry::{ToolContext, ToolHandler}; + +/// Recommend complementary services for a stack based on the selected template(s). +/// +/// Returns categorized recommendations (production vs development) with +/// suggested configurations (env vars, ports, volumes) tailored to the +/// deployment method (SSH/Ansible roles or Status Panel apps). +pub struct RecommendStackServicesTool; + +/// A single service recommendation with its rationale and configuration. +#[derive(serde::Serialize, Clone)] +struct ServiceRecommendation { + /// App/role code (e.g. "redis", "nginx", "traefik") + code: String, + /// Human-readable name + name: String, + /// Why this service is recommended + reason: String, + /// "required" | "recommended" | "optional" + priority: String, + /// "database" | "cache" | "proxy" | "monitoring" | "search" | "queue" | "mail" | "storage" | "security" | "devtool" | "runtime" + category: String, + /// Docker image (for Status Panel / docker-compose method) + docker_image: String, + /// Ansible role name (for SSH method); empty if not available + ansible_role: String, + /// Whether we have a local Ansible role for this + has_local_role: bool, + /// Whether we have a local app template for this + has_local_app: bool, + /// Suggested environment variables + environment: Value, + /// Suggested port mappings + ports: Value, + /// Suggested volume mounts + volumes: Value, + /// Additional notes / configuration tips + notes: String, +} + +/// Knowledge base: for a given "primary" app, which companion services make sense? +struct StackBlueprint { + /// Which primary codes trigger this blueprint + triggers: Vec<&'static str>, + /// Production recommendations + production: Vec, + /// Development-only extras + development: Vec, +} + +fn build_blueprints() -> Vec { + vec![ + // ── WordPress ──────────────────────────────────────────────── + StackBlueprint { + triggers: vec!["wordpress", "wordpress_prod", "wordpress_dev", "wordpress_woocommerce"], + production: vec![ + ServiceRecommendation { + code: "mysql".into(), + name: "MySQL".into(), + reason: "WordPress requires a MySQL-compatible database".into(), + priority: "required".into(), + category: "database".into(), + docker_image: "mysql:8.0".into(), + ansible_role: "mysql".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "MYSQL_ROOT_PASSWORD": "changeme_root", + "MYSQL_DATABASE": "wordpress", + "MYSQL_USER": "wordpress", + "MYSQL_PASSWORD": "changeme_wp" + }), + ports: json!([{"host_port": "3306", "container_port": "3306"}]), + volumes: json!([{"host_path": "mysql_data", "container_path": "/var/lib/mysql"}]), + notes: "Change default passwords before deploying to production.".into(), + }, + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Object caching dramatically improves WordPress performance".into(), + priority: "recommended".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "Install WP Redis plugin for object cache. Set WP_REDIS_HOST=redis".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy with automatic SSL certificate management".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "Handles SSL termination and routing for all services.".into(), + }, + ServiceRecommendation { + code: "telegraf".into(), + name: "Telegraf".into(), + reason: "System and container metrics collection for monitoring".into(), + priority: "optional".into(), + category: "monitoring".into(), + docker_image: "telegraf:1.30-alpine".into(), + ansible_role: "telegraf".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock:ro"} + ]), + notes: "Feeds metrics to InfluxDB or TryDirect monitoring dashboard.".into(), + }, + ], + development: vec![ + ServiceRecommendation { + code: "phpmyadmin".into(), + name: "phpMyAdmin".into(), + reason: "Web-based database management for development".into(), + priority: "recommended".into(), + category: "devtool".into(), + docker_image: "phpmyadmin:latest".into(), + ansible_role: "phpmyadmin".into(), + has_local_role: true, + has_local_app: true, + environment: json!({"PMA_HOST": "mysql", "PMA_PORT": "3306"}), + ports: json!([{"host_port": "8080", "container_port": "80"}]), + volumes: json!([]), + notes: "Remove in production. Accessible at port 8080.".into(), + }, + ServiceRecommendation { + code: "mailhog".into(), + name: "MailHog".into(), + reason: "Catches all outgoing emails for development testing".into(), + priority: "optional".into(), + category: "mail".into(), + docker_image: "mailhog/mailhog:latest".into(), + ansible_role: "mailhog".into(), + has_local_role: true, + has_local_app: false, + environment: json!({}), + ports: json!([ + {"host_port": "1025", "container_port": "1025"}, + {"host_port": "8025", "container_port": "8025"} + ]), + volumes: json!([]), + notes: "SMTP on 1025, web UI on 8025. Configure WordPress SMTP plugin to use mailhog:1025.".into(), + }, + ], + }, + + // ── Django ─────────────────────────────────────────────────── + StackBlueprint { + triggers: vec!["django"], + production: vec![ + ServiceRecommendation { + code: "postgres".into(), + name: "PostgreSQL".into(), + reason: "Django's preferred production database".into(), + priority: "required".into(), + category: "database".into(), + docker_image: "postgres:16-alpine".into(), + ansible_role: "postgres".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "POSTGRES_DB": "django_db", + "POSTGRES_USER": "django", + "POSTGRES_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "5432", "container_port": "5432"}]), + volumes: json!([{"host_path": "postgres_data", "container_path": "/var/lib/postgresql/data"}]), + notes: "Set DATABASE_URL=postgres://django:changeme@postgres:5432/django_db in Django.".into(), + }, + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Cache backend and Celery broker for async tasks".into(), + priority: "recommended".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "Use as Django cache (django-redis) and Celery broker (CELERY_BROKER_URL=redis://redis:6379/0).".into(), + }, + ServiceRecommendation { + code: "nginx".into(), + name: "Nginx".into(), + reason: "Reverse proxy and static file server for Django".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "nginx:1.25-alpine".into(), + ansible_role: "".into(), + has_local_role: false, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "static_files", "container_path": "/usr/share/nginx/html/static"}, + {"host_path": "media_files", "container_path": "/usr/share/nginx/html/media"} + ]), + notes: "Serves static/media files and proxies to gunicorn.".into(), + }, + ServiceRecommendation { + code: "rabbitmq".into(), + name: "RabbitMQ".into(), + reason: "Message broker for Celery task queue (alternative to Redis)".into(), + priority: "optional".into(), + category: "queue".into(), + docker_image: "rabbitmq:3-management-alpine".into(), + ansible_role: "rabbitmq".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "RABBITMQ_DEFAULT_USER": "django", + "RABBITMQ_DEFAULT_PASS": "changeme" + }), + ports: json!([ + {"host_port": "5672", "container_port": "5672"}, + {"host_port": "15672", "container_port": "15672"} + ]), + volumes: json!([{"host_path": "rabbitmq_data", "container_path": "/var/lib/rabbitmq"}]), + notes: "Management UI on port 15672. Use if you need advanced routing beyond Redis pub/sub.".into(), + }, + ServiceRecommendation { + code: "telegraf".into(), + name: "Telegraf".into(), + reason: "Metrics collection for monitoring".into(), + priority: "optional".into(), + category: "monitoring".into(), + docker_image: "telegraf:1.30-alpine".into(), + ansible_role: "telegraf".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([]), + volumes: json!([{"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock:ro"}]), + notes: "Feeds metrics to InfluxDB or TryDirect monitoring.".into(), + }, + ], + development: vec![ + ServiceRecommendation { + code: "mailhog".into(), + name: "MailHog".into(), + reason: "Email testing without sending real emails".into(), + priority: "recommended".into(), + category: "mail".into(), + docker_image: "mailhog/mailhog:latest".into(), + ansible_role: "mailhog".into(), + has_local_role: true, + has_local_app: false, + environment: json!({}), + ports: json!([ + {"host_port": "1025", "container_port": "1025"}, + {"host_port": "8025", "container_port": "8025"} + ]), + volumes: json!([]), + notes: "Configure Django EMAIL_HOST=mailhog, EMAIL_PORT=1025.".into(), + }, + ], + }, + + // ── Flask / FastAPI ────────────────────────────────────────── + StackBlueprint { + triggers: vec!["flask", "fastapi"], + production: vec![ + ServiceRecommendation { + code: "postgres".into(), + name: "PostgreSQL".into(), + reason: "Reliable production database for Python web apps".into(), + priority: "recommended".into(), + category: "database".into(), + docker_image: "postgres:16-alpine".into(), + ansible_role: "postgres".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "POSTGRES_DB": "app_db", + "POSTGRES_USER": "app", + "POSTGRES_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "5432", "container_port": "5432"}]), + volumes: json!([{"host_path": "postgres_data", "container_path": "/var/lib/postgresql/data"}]), + notes: "Set DATABASE_URL in your app environment.".into(), + }, + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Caching, session storage, and task queue support".into(), + priority: "recommended".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "Use as cache layer or Celery/ARQ broker.".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy with automatic SSL".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "Auto-discovers containers via Docker labels.".into(), + }, + ], + development: vec![ + ServiceRecommendation { + code: "mailhog".into(), + name: "MailHog".into(), + reason: "Email testing".into(), + priority: "optional".into(), + category: "mail".into(), + docker_image: "mailhog/mailhog:latest".into(), + ansible_role: "mailhog".into(), + has_local_role: true, + has_local_app: false, + environment: json!({}), + ports: json!([ + {"host_port": "1025", "container_port": "1025"}, + {"host_port": "8025", "container_port": "8025"} + ]), + volumes: json!([]), + notes: "".into(), + }, + ], + }, + + // ── Node.js / Next.js / Express ────────────────────────────── + StackBlueprint { + triggers: vec!["nodejs", "nextjs", "express"], + production: vec![ + ServiceRecommendation { + code: "postgres".into(), + name: "PostgreSQL".into(), + reason: "Reliable relational database for Node.js apps".into(), + priority: "recommended".into(), + category: "database".into(), + docker_image: "postgres:16-alpine".into(), + ansible_role: "postgres".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "POSTGRES_DB": "app_db", + "POSTGRES_USER": "app", + "POSTGRES_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "5432", "container_port": "5432"}]), + volumes: json!([{"host_path": "postgres_data", "container_path": "/var/lib/postgresql/data"}]), + notes: "Or use MongoDB if your app uses Mongoose/document model.".into(), + }, + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Session store, caching, and BullMQ job queue".into(), + priority: "recommended".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "Excellent for connect-redis sessions and BullMQ job processing.".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy with automatic HTTPS".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ], + development: vec![ + ServiceRecommendation { + code: "mailhog".into(), + name: "MailHog".into(), + reason: "Email testing".into(), + priority: "optional".into(), + category: "mail".into(), + docker_image: "mailhog/mailhog:latest".into(), + ansible_role: "mailhog".into(), + has_local_role: true, + has_local_app: false, + environment: json!({}), + ports: json!([ + {"host_port": "1025", "container_port": "1025"}, + {"host_port": "8025", "container_port": "8025"} + ]), + volumes: json!([]), + notes: "".into(), + }, + ], + }, + + // ── Laravel / PHP ──────────────────────────────────────────── + StackBlueprint { + triggers: vec!["laravel", "LAMP", "magento", "symfony", "pimcore6_prod", "pimcore6_dev"], + production: vec![ + ServiceRecommendation { + code: "mysql".into(), + name: "MySQL".into(), + reason: "Primary database for PHP/Laravel applications".into(), + priority: "required".into(), + category: "database".into(), + docker_image: "mysql:8.0".into(), + ansible_role: "mysql".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "MYSQL_ROOT_PASSWORD": "changeme_root", + "MYSQL_DATABASE": "laravel", + "MYSQL_USER": "laravel", + "MYSQL_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "3306", "container_port": "3306"}]), + volumes: json!([{"host_path": "mysql_data", "container_path": "/var/lib/mysql"}]), + notes: "Set DB_HOST=mysql, DB_DATABASE=laravel in .env".into(), + }, + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Cache, session driver, and queue worker backend".into(), + priority: "recommended".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "Set CACHE_DRIVER=redis, SESSION_DRIVER=redis, QUEUE_CONNECTION=redis in Laravel.".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy with SSL termination".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ], + development: vec![ + ServiceRecommendation { + code: "phpmyadmin".into(), + name: "phpMyAdmin".into(), + reason: "Web database manager for development".into(), + priority: "recommended".into(), + category: "devtool".into(), + docker_image: "phpmyadmin:latest".into(), + ansible_role: "phpmyadmin".into(), + has_local_role: true, + has_local_app: true, + environment: json!({"PMA_HOST": "mysql"}), + ports: json!([{"host_port": "8080", "container_port": "80"}]), + volumes: json!([]), + notes: "".into(), + }, + ServiceRecommendation { + code: "mailhog".into(), + name: "MailHog".into(), + reason: "Catch outgoing emails in development".into(), + priority: "recommended".into(), + category: "mail".into(), + docker_image: "mailhog/mailhog:latest".into(), + ansible_role: "mailhog".into(), + has_local_role: true, + has_local_app: false, + environment: json!({}), + ports: json!([ + {"host_port": "1025", "container_port": "1025"}, + {"host_port": "8025", "container_port": "8025"} + ]), + volumes: json!([]), + notes: "Set MAIL_HOST=mailhog, MAIL_PORT=1025 in .env".into(), + }, + ], + }, + + // ── Ruby on Rails ──────────────────────────────────────────── + StackBlueprint { + triggers: vec!["ror_restful"], + production: vec![ + ServiceRecommendation { + code: "postgres".into(), + name: "PostgreSQL".into(), + reason: "Rails default production database".into(), + priority: "required".into(), + category: "database".into(), + docker_image: "postgres:16-alpine".into(), + ansible_role: "postgres".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "POSTGRES_DB": "rails_production", + "POSTGRES_USER": "rails", + "POSTGRES_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "5432", "container_port": "5432"}]), + volumes: json!([{"host_path": "postgres_data", "container_path": "/var/lib/postgresql/data"}]), + notes: "".into(), + }, + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Action Cable, Sidekiq, and cache store".into(), + priority: "recommended".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "Set REDIS_URL=redis://redis:6379/0".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy with SSL".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ], + development: vec![ + ServiceRecommendation { + code: "mailhog".into(), + name: "MailHog".into(), + reason: "Email testing in development".into(), + priority: "optional".into(), + category: "mail".into(), + docker_image: "mailhog/mailhog:latest".into(), + ansible_role: "mailhog".into(), + has_local_role: true, + has_local_app: false, + environment: json!({}), + ports: json!([ + {"host_port": "1025", "container_port": "1025"}, + {"host_port": "8025", "container_port": "8025"} + ]), + volumes: json!([]), + notes: "".into(), + }, + ], + }, + + // ── AI / ML Stacks ─────────────────────────────────────────── + StackBlueprint { + triggers: vec!["openwebui", "langflow", "flowise", "litellm", "ai-workbench", "dify", "tensorflow"], + production: vec![ + ServiceRecommendation { + code: "postgres".into(), + name: "PostgreSQL".into(), + reason: "Persistent storage for AI/ML metadata and configurations".into(), + priority: "recommended".into(), + category: "database".into(), + docker_image: "postgres:16-alpine".into(), + ansible_role: "postgres".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "POSTGRES_DB": "ai_db", + "POSTGRES_USER": "ai", + "POSTGRES_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "5432", "container_port": "5432"}]), + volumes: json!([{"host_path": "postgres_data", "container_path": "/var/lib/postgresql/data"}]), + notes: "".into(), + }, + ServiceRecommendation { + code: "qdrant".into(), + name: "Qdrant".into(), + reason: "Vector database for RAG, embeddings, and semantic search".into(), + priority: "recommended".into(), + category: "database".into(), + docker_image: "qdrant/qdrant:latest".into(), + ansible_role: "qdrant".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "6333", "container_port": "6333"}, + {"host_port": "6334", "container_port": "6334"} + ]), + volumes: json!([{"host_path": "qdrant_data", "container_path": "/qdrant/storage"}]), + notes: "REST API on 6333, gRPC on 6334. Essential for RAG workflows.".into(), + }, + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Caching layer for LLM responses and session management".into(), + priority: "recommended".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "Cache LLM responses to reduce API costs.".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy for secure HTTPS access".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ServiceRecommendation { + code: "minio".into(), + name: "MinIO".into(), + reason: "S3-compatible object storage for models, datasets, and artifacts".into(), + priority: "optional".into(), + category: "storage".into(), + docker_image: "minio/minio:latest".into(), + ansible_role: "minio".into(), + has_local_role: true, + has_local_app: false, + environment: json!({ + "MINIO_ROOT_USER": "minioadmin", + "MINIO_ROOT_PASSWORD": "minioadmin" + }), + ports: json!([ + {"host_port": "9000", "container_port": "9000"}, + {"host_port": "9001", "container_port": "9001"} + ]), + volumes: json!([{"host_path": "minio_data", "container_path": "/data"}]), + notes: "API on 9000, console on 9001.".into(), + }, + ], + development: vec![], + }, + + // ── ELK / Monitoring ───────────────────────────────────────── + StackBlueprint { + triggers: vec!["elk", "elk_wazuh", "ewazuh", "wazuh", "zabbix"], + production: vec![ + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy for dashboard access".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ServiceRecommendation { + code: "telegraf".into(), + name: "Telegraf".into(), + reason: "System metrics collection agent".into(), + priority: "recommended".into(), + category: "monitoring".into(), + docker_image: "telegraf:1.30-alpine".into(), + ansible_role: "telegraf".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([]), + volumes: json!([{"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock:ro"}]), + notes: "".into(), + }, + ], + development: vec![], + }, + + // ── GitLab ─────────────────────────────────────────────────── + StackBlueprint { + triggers: vec!["gitlab_server"], + production: vec![ + ServiceRecommendation { + code: "postgres".into(), + name: "PostgreSQL".into(), + reason: "GitLab's required database backend".into(), + priority: "required".into(), + category: "database".into(), + docker_image: "postgres:16-alpine".into(), + ansible_role: "postgres".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "POSTGRES_DB": "gitlabhq_production", + "POSTGRES_USER": "gitlab", + "POSTGRES_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "5432", "container_port": "5432"}]), + volumes: json!([{"host_path": "postgres_data", "container_path": "/var/lib/postgresql/data"}]), + notes: "".into(), + }, + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Required by GitLab for caching and background jobs".into(), + priority: "required".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "SSL termination and routing".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ServiceRecommendation { + code: "minio".into(), + name: "MinIO".into(), + reason: "Object storage for Git LFS, artifacts, uploads".into(), + priority: "optional".into(), + category: "storage".into(), + docker_image: "minio/minio:latest".into(), + ansible_role: "minio".into(), + has_local_role: true, + has_local_app: false, + environment: json!({ + "MINIO_ROOT_USER": "minioadmin", + "MINIO_ROOT_PASSWORD": "minioadmin" + }), + ports: json!([ + {"host_port": "9000", "container_port": "9000"}, + {"host_port": "9001", "container_port": "9001"} + ]), + volumes: json!([{"host_path": "minio_data", "container_path": "/data"}]), + notes: "Replaces local file storage for scalability.".into(), + }, + ], + development: vec![], + }, + + // ── Mautic (Marketing Automation) ──────────────────────────── + StackBlueprint { + triggers: vec!["mautic"], + production: vec![ + ServiceRecommendation { + code: "mysql".into(), + name: "MySQL".into(), + reason: "Mautic's primary database".into(), + priority: "required".into(), + category: "database".into(), + docker_image: "mysql:8.0".into(), + ansible_role: "mysql".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "MYSQL_ROOT_PASSWORD": "changeme_root", + "MYSQL_DATABASE": "mautic", + "MYSQL_USER": "mautic", + "MYSQL_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "3306", "container_port": "3306"}]), + volumes: json!([{"host_path": "mysql_data", "container_path": "/var/lib/mysql"}]), + notes: "".into(), + }, + ServiceRecommendation { + code: "rabbitmq".into(), + name: "RabbitMQ".into(), + reason: "Message queue for Mautic campaign processing".into(), + priority: "recommended".into(), + category: "queue".into(), + docker_image: "rabbitmq:3-management-alpine".into(), + ansible_role: "rabbitmq".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "RABBITMQ_DEFAULT_USER": "mautic", + "RABBITMQ_DEFAULT_PASS": "changeme" + }), + ports: json!([ + {"host_port": "5672", "container_port": "5672"}, + {"host_port": "15672", "container_port": "15672"} + ]), + volumes: json!([{"host_path": "rabbitmq_data", "container_path": "/var/lib/rabbitmq"}]), + notes: "Processes email campaigns asynchronously.".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy with SSL".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ], + development: vec![ + ServiceRecommendation { + code: "mailhog".into(), + name: "MailHog".into(), + reason: "Catch test campaign emails in development".into(), + priority: "recommended".into(), + category: "mail".into(), + docker_image: "mailhog/mailhog:latest".into(), + ansible_role: "mailhog".into(), + has_local_role: true, + has_local_app: false, + environment: json!({}), + ports: json!([ + {"host_port": "1025", "container_port": "1025"}, + {"host_port": "8025", "container_port": "8025"} + ]), + volumes: json!([]), + notes: "Prevents sending real campaign emails during testing.".into(), + }, + ], + }, + + // ── MongoDB-based stacks ───────────────────────────────────── + StackBlueprint { + triggers: vec!["mongodb", "rocketchat", "wekan"], + production: vec![ + ServiceRecommendation { + code: "mongodb".into(), + name: "MongoDB".into(), + reason: "Document database required by this application".into(), + priority: "required".into(), + category: "database".into(), + docker_image: "mongo:7".into(), + ansible_role: "mongodb".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "MONGO_INITDB_ROOT_USERNAME": "admin", + "MONGO_INITDB_ROOT_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "27017", "container_port": "27017"}]), + volumes: json!([{"host_path": "mongodb_data", "container_path": "/data/db"}]), + notes: "".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy with SSL".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ], + development: vec![], + }, + + // ── E-Commerce (WooCommerce, OroCommerce, Sylius, Oscar) ──── + StackBlueprint { + triggers: vec!["wordpress_woocommerce", "orocommerce", "sylius", "oscar"], + production: vec![ + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Session & cache for e-commerce performance".into(), + priority: "recommended".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "Dramatically improves page load for product catalogs.".into(), + }, + ServiceRecommendation { + code: "elasticsearch".into(), + name: "Elasticsearch".into(), + reason: "Full-text product search".into(), + priority: "optional".into(), + category: "search".into(), + docker_image: "elasticsearch:8.12.0".into(), + ansible_role: "".into(), + has_local_role: false, + has_local_app: false, + environment: json!({ + "discovery.type": "single-node", + "xpack.security.enabled": "false", + "ES_JAVA_OPTS": "-Xms512m -Xmx512m" + }), + ports: json!([{"host_port": "9200", "container_port": "9200"}]), + volumes: json!([{"host_path": "es_data", "container_path": "/usr/share/elasticsearch/data"}]), + notes: "From Docker Hub. Needs 2GB+ RAM. Improves product search dramatically.".into(), + }, + ], + development: vec![], + }, + + // ── CRM / Project Management ───────────────────────────────── + StackBlueprint { + triggers: vec!["orocrm", "suitecrm", "redmine", "taiga"], + production: vec![ + ServiceRecommendation { + code: "postgres".into(), + name: "PostgreSQL".into(), + reason: "Primary database".into(), + priority: "required".into(), + category: "database".into(), + docker_image: "postgres:16-alpine".into(), + ansible_role: "postgres".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "POSTGRES_DB": "app_db", + "POSTGRES_USER": "app", + "POSTGRES_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "5432", "container_port": "5432"}]), + volumes: json!([{"host_path": "postgres_data", "container_path": "/var/lib/postgresql/data"}]), + notes: "".into(), + }, + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Caching and session storage".into(), + priority: "recommended".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy with SSL".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ], + development: vec![], + }, + + // ── Container Management (Portainer, Dockge, Komodo) ───────── + StackBlueprint { + triggers: vec!["portainer", "portainer-ce", "dockge", "komodo"], + production: vec![ + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Secure HTTPS access to management dashboard".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ServiceRecommendation { + code: "telegraf".into(), + name: "Telegraf".into(), + reason: "Metrics collection to complement container management".into(), + priority: "optional".into(), + category: "monitoring".into(), + docker_image: "telegraf:1.30-alpine".into(), + ansible_role: "telegraf".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([]), + volumes: json!([{"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock:ro"}]), + notes: "".into(), + }, + ], + development: vec![], + }, + + // ── Go (Gin) ──────────────────────────────────────────────── + StackBlueprint { + triggers: vec!["gin"], + production: vec![ + ServiceRecommendation { + code: "postgres".into(), + name: "PostgreSQL".into(), + reason: "Popular database choice for Go services".into(), + priority: "recommended".into(), + category: "database".into(), + docker_image: "postgres:16-alpine".into(), + ansible_role: "postgres".into(), + has_local_role: true, + has_local_app: true, + environment: json!({ + "POSTGRES_DB": "app_db", + "POSTGRES_USER": "app", + "POSTGRES_PASSWORD": "changeme" + }), + ports: json!([{"host_port": "5432", "container_port": "5432"}]), + volumes: json!([{"host_path": "postgres_data", "container_path": "/var/lib/postgresql/data"}]), + notes: "".into(), + }, + ServiceRecommendation { + code: "redis".into(), + name: "Redis".into(), + reason: "Caching and session storage".into(), + priority: "recommended".into(), + category: "cache".into(), + docker_image: "redis:7-alpine".into(), + ansible_role: "redis".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([{"host_port": "6379", "container_port": "6379"}]), + volumes: json!([{"host_path": "redis_data", "container_path": "/data"}]), + notes: "".into(), + }, + ServiceRecommendation { + code: "traefik".into(), + name: "Traefik".into(), + reason: "Reverse proxy".into(), + priority: "recommended".into(), + category: "proxy".into(), + docker_image: "traefik:v3.0".into(), + ansible_role: "traefik".into(), + has_local_role: true, + has_local_app: true, + environment: json!({}), + ports: json!([ + {"host_port": "80", "container_port": "80"}, + {"host_port": "443", "container_port": "443"} + ]), + volumes: json!([ + {"host_path": "/var/run/docker.sock", "container_path": "/var/run/docker.sock"}, + {"host_path": "traefik_certs", "container_path": "/letsencrypt"} + ]), + notes: "".into(), + }, + ], + development: vec![], + }, + ] +} + +#[async_trait] +impl ToolHandler for RecommendStackServicesTool { + async fn execute(&self, args: Value, _context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + /// App/role codes currently in the stack (e.g. ["wordpress", "mysql"]) + current_services: Vec, + /// "production" or "development" + #[serde(default = "default_stack_type")] + stack_type: String, + /// "ssh" or "status_panel" + #[serde(default = "default_deployment_method")] + deployment_method: String, + } + + fn default_stack_type() -> String { + "production".into() + } + fn default_deployment_method() -> String { + "ssh".into() + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let current_codes: Vec = params + .current_services + .iter() + .map(|s| s.to_lowercase().trim().to_string()) + .collect(); + + let blueprints = build_blueprints(); + let is_prod = params.stack_type.to_lowercase() != "development"; + let is_ssh = params.deployment_method.to_lowercase() == "ssh"; + + // Collect matching production recommendations + let mut prod_recs: Vec = Vec::new(); + let mut dev_recs: Vec = Vec::new(); + let mut matched_templates: Vec = Vec::new(); + + for bp in &blueprints { + let matches: Vec<&str> = bp + .triggers + .iter() + .filter(|t| current_codes.iter().any(|c| c == *t)) + .copied() + .collect(); + + if matches.is_empty() { + continue; + } + + matched_templates.extend(matches.iter().map(|s| s.to_string())); + + // Add production recommendations + for rec in &bp.production { + if !current_codes.contains(&rec.code.to_lowercase()) + && !prod_recs.iter().any(|r| r.code == rec.code) + { + prod_recs.push(rec.clone()); + } + } + + // Add development recommendations (if requested) + if !is_prod { + for rec in &bp.development { + if !current_codes.contains(&rec.code.to_lowercase()) + && !dev_recs.iter().any(|r| r.code == rec.code) + && !prod_recs.iter().any(|r| r.code == rec.code) + { + dev_recs.push(rec.clone()); + } + } + } + } + + // Sort: required first, then recommended, then optional + let priority_order = |p: &str| match p { + "required" => 0, + "recommended" => 1, + "optional" => 2, + _ => 3, + }; + prod_recs.sort_by_key(|r| priority_order(&r.priority)); + dev_recs.sort_by_key(|r| priority_order(&r.priority)); + + // Filter based on deployment method + let filter_for_method = |recs: &[ServiceRecommendation]| -> Vec { + recs.iter() + .map(|r| { + let mut rec = json!({ + "code": r.code, + "name": r.name, + "reason": r.reason, + "priority": r.priority, + "category": r.category, + "docker_image": r.docker_image, + "has_local_role": r.has_local_role, + "has_local_app": r.has_local_app, + "environment": r.environment, + "ports": r.ports, + "volumes": r.volumes, + }); + if !r.notes.is_empty() { + rec["notes"] = json!(r.notes); + } + if is_ssh && r.has_local_role { + rec["ansible_role"] = json!(r.ansible_role); + rec["install_method"] = json!("ansible_role"); + } else { + rec["install_method"] = json!("docker_compose"); + } + rec + }) + .collect() + }; + + let production_json = filter_for_method(&prod_recs); + let development_json = filter_for_method(&dev_recs); + + let total = production_json.len() + development_json.len(); + let summary = if matched_templates.is_empty() { + "No matching blueprints found for the current services. You can still add services manually via the template selector or Docker Hub search.".to_string() + } else { + format!( + "Found {} recommendation(s) for stack containing [{}]. {} for production{}.", + total, + matched_templates.join(", "), + production_json.len(), + if !development_json.is_empty() { + format!(", {} for development", development_json.len()) + } else { + String::new() + } + ) + }; + + let result = json!({ + "matched_templates": matched_templates, + "stack_type": params.stack_type, + "deployment_method": params.deployment_method, + "summary": summary, + "production": production_json, + "development": development_json, + "instructions": "Present these recommendations to the user grouped by purpose. For each service, explain why it's needed and show the suggested configuration. Ask the user which services to add, then use create_project_app to add each selected service with the suggested configuration." + }); + + tracing::info!( + "Recommended {} services for stack with [{}]", + total, + current_codes.join(", ") + ); + + Ok(ToolContent::Text { + text: serde_json::to_string_pretty(&result).unwrap(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "recommend_stack_services".to_string(), + description: "Get AI-powered service recommendations for a stack based on the selected template(s). Returns categorized suggestions (production vs development) with configurations (env vars, ports, volumes) tailored to the deployment method (SSH/Ansible or Status Panel). Use this when a user selects a template to suggest complementary services.".to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "current_services": { + "type": "array", + "items": { "type": "string" }, + "description": "Array of app/role codes currently in the stack (e.g. [\"wordpress\", \"mysql\"])" + }, + "stack_type": { + "type": "string", + "enum": ["production", "development"], + "description": "Whether this is a production or development stack (default: production)" + }, + "deployment_method": { + "type": "string", + "enum": ["ssh", "status_panel"], + "description": "Deployment method: 'ssh' for Ansible roles, 'status_panel' for Docker Compose (default: ssh)" + } + }, + "required": ["current_services"] + }), + } + } +} diff --git a/src/mcp/tools/user_service/mcp.rs b/src/mcp/tools/user_service/mcp.rs index b17dc06d..15ba88af 100644 --- a/src/mcp/tools/user_service/mcp.rs +++ b/src/mcp/tools/user_service/mcp.rs @@ -232,3 +232,434 @@ impl ToolHandler for SearchApplicationsTool { } } } + +/// List user notifications +pub struct GetNotificationsTool; + +#[async_trait] +impl ToolHandler for GetNotificationsTool { + async fn execute(&self, args: Value, context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + #[serde(default)] + page: Option, + #[serde(default)] + max_results: Option, + #[serde(default)] + unread_only: Option, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let client = UserServiceClient::new_public(&context.settings.user_service_url); + let token = context.user.access_token.as_deref().unwrap_or(""); + + let mut notifications = client + .list_notifications(token, params.page, params.max_results) + .await + .map_err(|e| format!("Failed to fetch notifications: {}", e))?; + + if params.unread_only.unwrap_or(false) { + notifications.retain(|item| !item.is_read.unwrap_or(false)); + } + + let result = serde_json::to_string(¬ifications) + .map_err(|e| format!("Serialization error: {}", e))?; + + tracing::info!( + user_id = %context.user.id, + count = notifications.len(), + unread_only = params.unread_only.unwrap_or(false), + "Listed notifications via MCP" + ); + + Ok(ToolContent::Text { text: result }) + } + + fn schema(&self) -> Tool { + Tool { + name: "get_notifications".to_string(), + description: "List user notifications with optional pagination and unread filter".to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "page": { + "type": "number", + "description": "Optional page number" + }, + "max_results": { + "type": "number", + "description": "Optional number of records per page" + }, + "unread_only": { + "type": "boolean", + "description": "When true, return only unread notifications" + } + }, + "required": [] + }), + } + } +} + +/// Mark a notification as read/unread +pub struct MarkNotificationReadTool; + +#[async_trait] +impl ToolHandler for MarkNotificationReadTool { + async fn execute(&self, args: Value, context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + notification_id: i64, + #[serde(default = "default_read_state")] + is_read: bool, + } + + fn default_read_state() -> bool { + true + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let client = UserServiceClient::new_public(&context.settings.user_service_url); + let token = context.user.access_token.as_deref().unwrap_or(""); + + let response = client + .mark_notification_read(token, params.notification_id, params.is_read) + .await + .map_err(|e| format!("Failed to update notification state: {}", e))?; + + tracing::info!( + user_id = %context.user.id, + notification_id = params.notification_id, + is_read = params.is_read, + "Updated notification read state via MCP" + ); + + Ok(ToolContent::Text { + text: response.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "mark_notification_read".to_string(), + description: "Mark a notification as read (default) or unread".to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "notification_id": { + "type": "number", + "description": "Notification ID" + }, + "is_read": { + "type": "boolean", + "description": "Read state to set (default: true)" + } + }, + "required": ["notification_id"] + }), + } + } +} + +/// Mark all notifications as read +pub struct MarkAllNotificationsReadTool; + +#[async_trait] +impl ToolHandler for MarkAllNotificationsReadTool { + async fn execute(&self, _args: Value, context: &ToolContext) -> Result { + let client = UserServiceClient::new_public(&context.settings.user_service_url); + let token = context.user.access_token.as_deref().unwrap_or(""); + + let response = client + .mark_all_notifications_read(token) + .await + .map_err(|e| format!("Failed to mark all notifications read: {}", e))?; + + tracing::info!( + user_id = %context.user.id, + "Marked all notifications as read via MCP" + ); + + Ok(ToolContent::Text { + text: response.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "mark_all_notifications_read".to_string(), + description: "Mark all notifications as read for the current user".to_string(), + input_schema: json!({ + "type": "object", + "properties": {}, + "required": [] + }), + } + } +} + +/// Search templates from unified applications endpoint (official + marketplace) +pub struct SearchMarketplaceTemplatesTool; + +#[async_trait] +impl ToolHandler for SearchMarketplaceTemplatesTool { + async fn execute(&self, args: Value, context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + #[serde(default)] + query: Option, + #[serde(default)] + category: Option, + #[serde(default)] + is_marketplace: Option, + #[serde(default)] + page: Option, + #[serde(default)] + max_results: Option, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let client = UserServiceClient::new_public(&context.settings.user_service_url); + let token = context.user.access_token.as_deref().unwrap_or(""); + + let applications = client + .search_marketplace_templates( + token, + params.query.as_deref(), + params.category.as_deref(), + params.is_marketplace, + params.page, + params.max_results, + ) + .await + .map_err(|e| format!("Failed to search templates: {}", e))?; + + let response = json!({ + "count": applications.len(), + "items": applications, + }); + + tracing::info!( + user_id = %context.user.id, + query = ?params.query, + category = ?params.category, + is_marketplace = ?params.is_marketplace, + count = response.get("count").and_then(|v| v.as_u64()).unwrap_or(0), + "Searched unified template catalog via MCP" + ); + + Ok(ToolContent::Text { + text: response.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "search_marketplace_templates".to_string(), + description: "Search the unified applications catalog (official + marketplace templates) with optional text/category/source filters" + .to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "Optional free-text search over name/code/description" + }, + "category": { + "type": "string", + "description": "Optional category filter" + }, + "is_marketplace": { + "type": "boolean", + "description": "Optional source filter: true for marketplace only, false for official only" + }, + "page": { + "type": "number", + "description": "Optional page number" + }, + "max_results": { + "type": "number", + "description": "Optional number of records per page" + } + }, + "required": [] + }), + } + } +} + +/// Initiate deployment using User Service native install flow +pub struct InitiateDeploymentTool; + +#[async_trait] +impl ToolHandler for InitiateDeploymentTool { + async fn execute(&self, args: Value, context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + payload: Value, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let client = UserServiceClient::new_public(&context.settings.user_service_url); + let token = context.user.access_token.as_deref().unwrap_or(""); + + let response = client + .initiate_deployment(token, params.payload) + .await + .map_err(|e| format!("Failed to initiate deployment: {}", e))?; + + tracing::info!( + user_id = %context.user.id, + "Initiated deployment via User Service MCP tool" + ); + + Ok(ToolContent::Text { + text: response.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "initiate_deployment".to_string(), + description: "Initiate deployment through User Service /install/init/ using native validation and orchestration flow" + .to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "payload": { + "type": "object", + "description": "Deployment request payload expected by User Service /install/init/" + } + }, + "required": ["payload"] + }), + } + } +} + +/// Trigger redeploy for an existing installation +pub struct TriggerRedeployTool; + +#[async_trait] +impl ToolHandler for TriggerRedeployTool { + async fn execute(&self, args: Value, context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + installation_id: i64, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let client = UserServiceClient::new_public(&context.settings.user_service_url); + let token = context.user.access_token.as_deref().unwrap_or(""); + + let response = client + .trigger_redeploy(token, params.installation_id) + .await + .map_err(|e| format!("Failed to trigger redeploy: {}", e))?; + + tracing::info!( + user_id = %context.user.id, + installation_id = params.installation_id, + "Triggered installation redeploy via MCP" + ); + + Ok(ToolContent::Text { + text: response.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "trigger_redeploy".to_string(), + description: "Trigger redeploy for an existing installation".to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "installation_id": { + "type": "number", + "description": "Installation ID to redeploy" + } + }, + "required": ["installation_id"] + }), + } + } +} + +/// Add a new app to an existing installation +pub struct AddAppToDeploymentTool; + +#[async_trait] +impl ToolHandler for AddAppToDeploymentTool { + async fn execute(&self, args: Value, context: &ToolContext) -> Result { + #[derive(Deserialize)] + struct Args { + installation_id: i64, + app_code: String, + #[serde(default)] + app_config: Option, + } + + let params: Args = + serde_json::from_value(args).map_err(|e| format!("Invalid arguments: {}", e))?; + + let client = UserServiceClient::new_public(&context.settings.user_service_url); + let token = context.user.access_token.as_deref().unwrap_or(""); + + let response = client + .add_app_to_installation( + token, + params.installation_id, + ¶ms.app_code, + params.app_config, + ) + .await + .map_err(|e| format!("Failed to add app to installation: {}", e))?; + + tracing::info!( + user_id = %context.user.id, + installation_id = params.installation_id, + app_code = %params.app_code, + "Added app to installation via MCP" + ); + + Ok(ToolContent::Text { + text: response.to_string(), + }) + } + + fn schema(&self) -> Tool { + Tool { + name: "add_app_to_deployment".to_string(), + description: "Add an app to an existing installation/deployment".to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "installation_id": { + "type": "number", + "description": "Installation ID" + }, + "app_code": { + "type": "string", + "description": "Application code to add" + }, + "app_config": { + "type": "object", + "description": "Optional app-specific config payload" + } + }, + "required": ["installation_id", "app_code"] + }), + } + } +} diff --git a/src/models/chat.rs b/src/models/chat.rs new file mode 100644 index 00000000..4243973e --- /dev/null +++ b/src/models/chat.rs @@ -0,0 +1,13 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] +pub struct ChatConversation { + pub id: Uuid, + pub user_id: String, + pub project_id: Option, + pub messages: serde_json::Value, + pub created_at: DateTime, + pub updated_at: DateTime, +} diff --git a/src/models/mod.rs b/src/models/mod.rs index a08d33d5..d9bafa60 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,5 +1,6 @@ mod agent; mod agreement; +mod chat; mod client; mod cloud; mod command; @@ -16,6 +17,7 @@ pub mod user; pub use agent::*; pub use agreement::*; +pub use chat::*; pub use client::*; pub use cloud::*; pub use command::*; diff --git a/src/routes/chat/delete.rs b/src/routes/chat/delete.rs new file mode 100644 index 00000000..2112f2f9 --- /dev/null +++ b/src/routes/chat/delete.rs @@ -0,0 +1,27 @@ +use crate::db; +use crate::helpers::JsonResponse; +use crate::models; +use actix_web::{delete, web, Responder, Result}; +use serde::Deserialize; +use sqlx::PgPool; +use std::sync::Arc; + +#[derive(Debug, Deserialize)] +pub struct Query { + pub project_id: Option, +} + +/// DELETE /chat/history?project_id={id} +/// Clears the stored chat conversation for the logged-in user. +#[tracing::instrument(name = "Delete chat history.")] +#[delete("/history")] +pub async fn item( + user: web::ReqData>, + query: web::Query, + pg_pool: web::Data, +) -> Result { + db::chat::delete(pg_pool.get_ref(), &user.id, query.project_id) + .await + .map_err(|err| JsonResponse::internal_server_error(err.to_string())) + .map(|_| JsonResponse::::build().ok("OK")) +} diff --git a/src/routes/chat/get.rs b/src/routes/chat/get.rs new file mode 100644 index 00000000..29b412c3 --- /dev/null +++ b/src/routes/chat/get.rs @@ -0,0 +1,31 @@ +use crate::db; +use crate::helpers::JsonResponse; +use crate::models; +use actix_web::{get, web, Responder, Result}; +use serde::Deserialize; +use sqlx::PgPool; +use std::sync::Arc; + +#[derive(Debug, Deserialize)] +pub struct Query { + pub project_id: Option, +} + +/// GET /chat/history?project_id={id} +/// Returns the saved chat conversation for the logged-in user. +/// project_id is optional; omit for canvas/onboarding mode. +#[tracing::instrument(name = "Get chat history.")] +#[get("/history")] +pub async fn item( + user: web::ReqData>, + query: web::Query, + pg_pool: web::Data, +) -> Result { + db::chat::fetch(pg_pool.get_ref(), &user.id, query.project_id) + .await + .map_err(|err| JsonResponse::internal_server_error(err.to_string())) + .and_then(|conv| match conv { + Some(c) => Ok(JsonResponse::build().set_item(Some(c)).ok("OK")), + None => Err(JsonResponse::not_found("No chat history found")), + }) +} diff --git a/src/routes/chat/mod.rs b/src/routes/chat/mod.rs new file mode 100644 index 00000000..b99105e5 --- /dev/null +++ b/src/routes/chat/mod.rs @@ -0,0 +1,3 @@ +pub mod delete; +pub mod get; +pub mod upsert; diff --git a/src/routes/chat/upsert.rs b/src/routes/chat/upsert.rs new file mode 100644 index 00000000..51c7111a --- /dev/null +++ b/src/routes/chat/upsert.rs @@ -0,0 +1,29 @@ +use crate::db; +use crate::helpers::JsonResponse; +use crate::models; +use actix_web::{put, web, Responder, Result}; +use serde::Deserialize; +use serde_json::Value; +use sqlx::PgPool; +use std::sync::Arc; + +#[derive(Debug, Deserialize)] +pub struct ChatHistoryRequest { + pub project_id: Option, + pub messages: Value, +} + +/// PUT /chat/history +/// Upserts the chat conversation for the logged-in user. +#[tracing::instrument(name = "Upsert chat history.")] +#[put("/history")] +pub async fn item( + user: web::ReqData>, + web::Json(body): web::Json, + pg_pool: web::Data, +) -> Result { + db::chat::upsert(pg_pool.get_ref(), &user.id, body.project_id, body.messages) + .await + .map(|conv| JsonResponse::build().set_item(conv).ok("OK")) + .map_err(|err| JsonResponse::internal_server_error(err.to_string())) +} diff --git a/src/routes/deployment/mod.rs b/src/routes/deployment/mod.rs index 2f30b66e..d47e2ae1 100644 --- a/src/routes/deployment/mod.rs +++ b/src/routes/deployment/mod.rs @@ -1,3 +1,5 @@ pub mod capabilities; +pub mod status; pub use capabilities::*; +pub use status::*; diff --git a/src/routes/deployment/status.rs b/src/routes/deployment/status.rs new file mode 100644 index 00000000..2551f734 --- /dev/null +++ b/src/routes/deployment/status.rs @@ -0,0 +1,128 @@ +use actix_web::{get, web, Responder, Result}; +use chrono::{DateTime, Utc}; +use serde::Serialize; +use sqlx::PgPool; +use std::sync::Arc; + +use crate::{db, helpers::JsonResponse, models}; + +/// Public-facing deployment status response (hides internal metadata). +#[derive(Debug, Clone, Serialize, Default)] +pub struct DeploymentStatusResponse { + pub id: i32, + pub project_id: i32, + pub deployment_hash: String, + pub status: String, + /// Human-readable status/error message from the deployment pipeline. + #[serde(skip_serializing_if = "Option::is_none")] + pub status_message: Option, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +impl From for DeploymentStatusResponse { + fn from(d: models::Deployment) -> Self { + let status_message = d + .metadata + .get("status_message") + .and_then(|v| v.as_str()) + .map(String::from); + + Self { + id: d.id, + project_id: d.project_id, + deployment_hash: d.deployment_hash, + status: d.status, + status_message, + created_at: d.created_at, + updated_at: d.updated_at, + } + } +} + +/// `GET /api/v1/deployments/{id}` +/// +/// Fetch deployment status by deployment ID. +/// Requires authentication (inherited from the `/api` scope middleware). +#[tracing::instrument(name = "Get deployment status by ID", skip(pg_pool))] +#[get("/{id}")] +pub async fn status_handler( + path: web::Path, + user: web::ReqData>, + pg_pool: web::Data, +) -> Result { + let deployment_id = path.into_inner(); + + let deployment = db::deployment::fetch(pg_pool.get_ref(), deployment_id) + .await + .map_err(|err| JsonResponse::::build().internal_server_error(err))?; + + match deployment { + Some(d) => { + // Verify the deployment belongs to the requesting user + if d.user_id.as_deref() != Some(&user.id) { + return Err(JsonResponse::::build() + .not_found("Deployment not found")); + } + let resp: DeploymentStatusResponse = d.into(); + Ok(JsonResponse::build() + .set_item(resp) + .ok("Deployment status fetched")) + } + None => Err(JsonResponse::::build() + .not_found("Deployment not found")), + } +} + +/// `GET /api/v1/deployments/project/{project_id}` +/// +/// Fetch the latest deployment status for a project. +/// Returns the most recent (non-deleted) deployment. +#[tracing::instrument(name = "Get deployment status by project ID", skip(pg_pool))] +#[get("/project/{project_id}")] +pub async fn status_by_project_handler( + path: web::Path, + user: web::ReqData>, + pg_pool: web::Data, +) -> Result { + let project_id = path.into_inner(); + + let deployment = db::deployment::fetch_by_project_id(pg_pool.get_ref(), project_id) + .await + .map_err(|err| JsonResponse::::build().internal_server_error(err))?; + + match deployment { + Some(d) => { + if d.user_id.as_deref() != Some(&user.id) { + return Err(JsonResponse::::build() + .not_found("No deployment found for this project")); + } + let resp: DeploymentStatusResponse = d.into(); + Ok(JsonResponse::build() + .set_item(resp) + .ok("Deployment status fetched")) + } + None => Err(JsonResponse::::build() + .not_found("No deployment found for this project")), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn deployment_to_status_response() { + let d = models::Deployment::new( + 42, + Some("user123".to_string()), + "deployment_abc".to_string(), + "in_progress".to_string(), + serde_json::json!({}), + ); + let resp: DeploymentStatusResponse = d.into(); + assert_eq!(resp.project_id, 42); + assert_eq!(resp.deployment_hash, "deployment_abc"); + assert_eq!(resp.status, "in_progress"); + } +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 27c48022..a8f824cf 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -13,6 +13,7 @@ pub(crate) mod project; pub(crate) mod server; pub(crate) mod agreement; +pub(crate) mod chat; pub(crate) mod marketplace; pub use project::*; diff --git a/src/services/config_renderer.rs b/src/services/config_renderer.rs index 7da98cb7..977319ff 100644 --- a/src/services/config_renderer.rs +++ b/src/services/config_renderer.rs @@ -14,7 +14,6 @@ use crate::services::vault_service::{AppConfig, VaultError, VaultService}; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use serde_json::Value; -use serde_json::json; use std::collections::HashMap; use tera::{Context as TeraContext, Tera}; @@ -779,6 +778,7 @@ const SERVICE_TEMPLATE: &str = r#" #[cfg(test)] mod tests { use super::*; + use serde_json::json; #[test] fn test_parse_environment_object() { diff --git a/src/startup.rs b/src/startup.rs index 534e2855..cf0ab71b 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -210,7 +210,9 @@ pub async fn run( ) .service( web::scope("/v1/deployments") - .service(routes::deployment::capabilities_handler), + .service(routes::deployment::capabilities_handler) + .service(routes::deployment::status_handler) + .service(routes::deployment::status_by_project_handler), ) .service( web::scope("/v1/commands") @@ -266,6 +268,12 @@ pub async fn run( .service(crate::routes::agreement::get_handler) .service(crate::routes::agreement::accept_handler), ) + .service( + web::scope("/chat") + .service(crate::routes::chat::get::item) + .service(crate::routes::chat::upsert::item) + .service(crate::routes::chat::delete::item), + ) .service(web::resource("/mcp").route(web::get().to(mcp::mcp_websocket))) .app_data(json_config.clone()) .app_data(api_pool.clone()) diff --git a/tests/cli_ai.rs b/tests/cli_ai.rs new file mode 100644 index 00000000..d6e88314 --- /dev/null +++ b/tests/cli_ai.rs @@ -0,0 +1,56 @@ +//! Integration tests for `stacker ai`. + +use assert_cmd::Command; +use predicates::prelude::*; +use std::fs; +use tempfile::TempDir; + +fn stacker_cmd() -> Command { + Command::cargo_bin("stacker-cli").expect("stacker-cli binary not found") +} + +#[test] +fn test_ai_ask_no_config_returns_error() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["ai", "ask", "How to optimize my Dockerfile?"]) + .assert() + .failure() + .stderr(predicate::str::contains("ConfigNotFound")); +} + +#[test] +fn test_ai_ask_disabled_returns_error() { + let dir = TempDir::new().unwrap(); + let config = r#" +name: test-app +version: "1.0" +app: + type: static + path: "." +deploy: + target: local +ai: + enabled: false + provider: openai +"#; + fs::write(dir.path().join("stacker.yml"), config).unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["ai", "ask", "Question?"]) + .assert() + .failure() + .stderr(predicate::str::contains("AiNotConfigured")); +} + +#[test] +fn test_ai_help_shows_usage() { + stacker_cmd() + .args(["ai", "ask", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--context")); +} diff --git a/tests/cli_config.rs b/tests/cli_config.rs new file mode 100644 index 00000000..f3a47e3c --- /dev/null +++ b/tests/cli_config.rs @@ -0,0 +1,112 @@ +//! Integration tests for `stacker config`. + +use assert_cmd::Command; +use predicates::prelude::*; +use std::fs; +use tempfile::TempDir; + +fn stacker_cmd() -> Command { + Command::cargo_bin("stacker-cli").expect("stacker-cli binary not found") +} + +#[test] +fn test_config_validate_valid_returns_success() { + let dir = TempDir::new().unwrap(); + let config = r#" +name: test-app +version: "1.0" +app: + type: static + path: "." +deploy: + target: local +"#; + fs::write(dir.path().join("stacker.yml"), config).unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["config", "validate"]) + .assert() + .success(); +} + +#[test] +fn test_config_validate_missing_file_returns_error() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["config", "validate"]) + .assert() + .failure() + .stderr(predicate::str::contains("ConfigNotFound")); +} + +#[test] +fn test_config_validate_custom_file() { + let dir = TempDir::new().unwrap(); + let config = r#" +name: custom +version: "1.0" +app: + type: node + path: "." +deploy: + target: local +"#; + fs::write(dir.path().join("my-config.yml"), config).unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["config", "validate", "--file", "my-config.yml"]) + .assert() + .success(); +} + +#[test] +fn test_config_show_displays_config() { + let dir = TempDir::new().unwrap(); + let config = r#" +name: show-test +version: "1.0" +app: + type: static + path: "." +deploy: + target: local +"#; + fs::write(dir.path().join("stacker.yml"), config).unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["config", "show"]) + .assert() + .success() + .stdout(predicate::str::contains("show-test")); +} + +#[test] +fn test_config_show_missing_file_returns_error() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["config", "show"]) + .assert() + .failure(); +} + + #[test] + fn test_config_example_prints_full_reference() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["config", "example"]) + .assert() + .success() + .stdout(predicate::str::contains("FULL COMMENTED REFERENCE")) + .stdout(predicate::str::contains("monitoring:")) + .stdout(predicate::str::contains("hooks:")) + .stdout(predicate::str::contains("deploy:")); + } diff --git a/tests/cli_deploy.rs b/tests/cli_deploy.rs new file mode 100644 index 00000000..322edf0a --- /dev/null +++ b/tests/cli_deploy.rs @@ -0,0 +1,143 @@ +//! Integration tests for `stacker deploy`. + +use assert_cmd::Command; +use predicates::prelude::*; +use std::fs; +use tempfile::TempDir; + +fn stacker_cmd() -> Command { + Command::cargo_bin("stacker-cli").expect("stacker-cli binary not found") +} + +fn setup_project(dir: &TempDir) { + let config = r#" +name: test-app +version: "1.0" +app: + type: static + path: "." +deploy: + target: local +"#; + fs::write(dir.path().join("stacker.yml"), config).unwrap(); + fs::write(dir.path().join("index.html"), "

Hello

").unwrap(); +} + +#[test] +fn test_deploy_dry_run_generates_artifacts() { + let dir = TempDir::new().unwrap(); + setup_project(&dir); + + stacker_cmd() + .current_dir(dir.path()) + .args(["deploy", "--target", "local", "--dry-run"]) + .assert() + .success(); + + // Dry-run should generate the Dockerfile and compose file + assert!(dir.path().join(".stacker").exists()); + assert!(dir.path().join(".stacker/Dockerfile").exists()); + assert!(dir.path().join(".stacker/docker-compose.yml").exists()); +} + +#[test] +fn test_deploy_dry_run_dockerfile_content() { + let dir = TempDir::new().unwrap(); + setup_project(&dir); + + stacker_cmd() + .current_dir(dir.path()) + .args(["deploy", "--target", "local", "--dry-run"]) + .assert() + .success(); + + let dockerfile = fs::read_to_string(dir.path().join(".stacker/Dockerfile")).unwrap(); + assert!(dockerfile.contains("FROM")); +} + +#[test] +fn test_deploy_dry_run_compose_content() { + let dir = TempDir::new().unwrap(); + setup_project(&dir); + + stacker_cmd() + .current_dir(dir.path()) + .args(["deploy", "--target", "local", "--dry-run"]) + .assert() + .success(); + + let compose = fs::read_to_string(dir.path().join(".stacker/docker-compose.yml")).unwrap(); + assert!(compose.contains("services")); +} + +#[test] +fn test_deploy_no_config_returns_error() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["deploy", "--target", "local"]) + .assert() + .failure(); +} + +#[test] +fn test_deploy_custom_file_flag() { + let dir = TempDir::new().unwrap(); + let config = r#" +name: custom-app +version: "1.0" +app: + type: static + path: "." +deploy: + target: local +"#; + fs::write(dir.path().join("custom.yml"), config).unwrap(); + fs::write(dir.path().join("index.html"), "

Hi

").unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["deploy", "--target", "local", "--file", "custom.yml", "--dry-run"]) + .assert() + .success(); +} + +#[test] +fn test_deploy_cloud_without_credentials_fails() { + let dir = TempDir::new().unwrap(); + let config = r#" +name: cloud-app +version: "1.0" +app: + type: static + path: "." +deploy: + target: cloud + cloud: + provider: hetzner + region: fsn1 + size: cx21 + ssh_key: ~/.ssh/id_ed25519 +"#; + fs::write(dir.path().join("stacker.yml"), config).unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["deploy", "--target", "cloud"]) + .assert() + .failure() + .stderr(predicate::str::contains("login").or(predicate::str::contains("credential").or(predicate::str::contains("Login")))); +} + +#[test] +fn test_deploy_invalid_target_fails() { + let dir = TempDir::new().unwrap(); + setup_project(&dir); + + stacker_cmd() + .current_dir(dir.path()) + .args(["deploy", "--target", "mars"]) + .assert() + .failure(); +} diff --git a/tests/cli_destroy.rs b/tests/cli_destroy.rs new file mode 100644 index 00000000..b2d82f0f --- /dev/null +++ b/tests/cli_destroy.rs @@ -0,0 +1,47 @@ +//! Integration tests for `stacker destroy`. + +use assert_cmd::Command; +use predicates::prelude::*; +use std::fs; +use tempfile::TempDir; + +fn stacker_cmd() -> Command { + Command::cargo_bin("stacker-cli").expect("stacker-cli binary not found") +} + +#[test] +fn test_destroy_requires_confirmation() { + let dir = TempDir::new().unwrap(); + let stacker_dir = dir.path().join(".stacker"); + fs::create_dir_all(&stacker_dir).unwrap(); + fs::write(stacker_dir.join("docker-compose.yml"), "version: '3.8'\n").unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .arg("destroy") + .assert() + .failure() + .stderr(predicate::str::contains("confirm").or(predicate::str::contains("Destroy"))); +} + +#[test] +fn test_destroy_no_deployment_returns_error() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["destroy", "--confirm"]) + .assert() + .failure() + .stderr(predicate::str::contains("No deployment").or(predicate::str::contains("Nothing to destroy"))); +} + +#[test] +fn test_destroy_help_shows_options() { + stacker_cmd() + .args(["destroy", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--volumes")) + .stdout(predicate::str::contains("--confirm")); +} diff --git a/tests/cli_init.rs b/tests/cli_init.rs new file mode 100644 index 00000000..6d4898f6 --- /dev/null +++ b/tests/cli_init.rs @@ -0,0 +1,182 @@ +//! Integration tests for `stacker init`. +//! +//! Uses `assert_cmd` to invoke the stacker-cli binary. + +use assert_cmd::Command; +use predicates::prelude::*; +use std::fs; +use tempfile::TempDir; + +fn stacker_cmd() -> Command { + Command::cargo_bin("stacker-cli").expect("stacker-cli binary not found") +} + +#[test] +fn test_init_creates_stacker_yml() { + let dir = TempDir::new().unwrap(); + // Create an index.html so detector picks up "static" + fs::write(dir.path().join("index.html"), "

Hello

").unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .arg("init") + .assert() + .success(); + + assert!(dir.path().join("stacker.yml").exists()); + let content = fs::read_to_string(dir.path().join("stacker.yml")).unwrap(); + assert!(content.contains("name:")); + assert!(content.contains("app:")); +} + +#[test] +fn test_init_with_app_type_flag() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["init", "--app-type", "node"]) + .assert() + .success(); + + let content = fs::read_to_string(dir.path().join("stacker.yml")).unwrap(); + assert!(content.contains("node")); +} + +#[test] +fn test_init_with_proxy_flag() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["init", "--with-proxy"]) + .assert() + .success(); + + let content = fs::read_to_string(dir.path().join("stacker.yml")).unwrap(); + assert!(content.contains("proxy")); +} + +#[test] +fn test_init_with_ai_flag() { + let dir = TempDir::new().unwrap(); + + // Use custom provider with fake key so it fails fast (avoids hitting + // a real running Ollama which would take minutes to generate). + stacker_cmd() + .current_dir(dir.path()) + .args(["init", "--with-ai", "--ai-provider", "custom", "--ai-api-key", "fake"]) + .assert() + .success(); + + let content = fs::read_to_string(dir.path().join("stacker.yml")).unwrap(); + assert!(content.contains("ai")); +} + +#[test] +fn test_init_refuses_overwrite() { + let dir = TempDir::new().unwrap(); + fs::write(dir.path().join("stacker.yml"), "existing").unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .arg("init") + .assert() + .failure() + .stderr(predicate::str::contains("already exists")); +} + +#[test] +fn test_init_detects_static_project() { + let dir = TempDir::new().unwrap(); + fs::write(dir.path().join("index.html"), "

Test

").unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .arg("init") + .assert() + .success(); + + let content = fs::read_to_string(dir.path().join("stacker.yml")).unwrap(); + assert!(content.contains("static")); +} + +#[test] +fn test_init_detects_node_project() { + let dir = TempDir::new().unwrap(); + fs::write( + dir.path().join("package.json"), + r#"{"name": "test", "version": "1.0.0"}"#, + ) + .unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .arg("init") + .assert() + .success(); + + let content = fs::read_to_string(dir.path().join("stacker.yml")).unwrap(); + assert!(content.contains("node")); +} + +#[test] +fn test_init_detects_python_project() { + let dir = TempDir::new().unwrap(); + fs::write(dir.path().join("requirements.txt"), "flask\n").unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .arg("init") + .assert() + .success(); + + let content = fs::read_to_string(dir.path().join("stacker.yml")).unwrap(); + assert!(content.contains("python")); +} + +// ── AI-powered init integration tests ─────────────── + +#[test] +fn test_init_with_ai_help_shows_provider_flags() { + stacker_cmd() + .args(["init", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--ai-provider")) + .stdout(predicate::str::contains("--ai-model")) + .stdout(predicate::str::contains("--ai-api-key")); +} + +#[test] +fn test_init_with_ai_and_provider_flags() { + let dir = TempDir::new().unwrap(); + fs::write(dir.path().join("package.json"), r#"{"name":"test"}"#).unwrap(); + + // Use custom provider with fake key — triggers AI path, fails fast, + // then falls back to template-based generation. + stacker_cmd() + .current_dir(dir.path()) + .args(["init", "--with-ai", "--ai-provider", "custom", "--ai-api-key", "fake"]) + .assert() + .success() + .stderr(predicate::str::contains("AI").or(predicate::str::contains("Created"))); + + assert!(dir.path().join("stacker.yml").exists()); + let content = fs::read_to_string(dir.path().join("stacker.yml")).unwrap(); + // Should still produce a valid config (fallback to template) + assert!(content.contains("name:")); + assert!(content.contains("ai")); +} + +#[test] +fn test_init_with_ai_invalid_provider_fails() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .args(["init", "--with-ai", "--ai-provider", "grok"]) + .assert() + .failure() + .stderr(predicate::str::contains("Unknown AI provider")); +} diff --git a/tests/cli_logs.rs b/tests/cli_logs.rs new file mode 100644 index 00000000..56b4244c --- /dev/null +++ b/tests/cli_logs.rs @@ -0,0 +1,34 @@ +//! Integration tests for `stacker logs`. + +use assert_cmd::Command; +use predicates::prelude::*; +use std::fs; +use tempfile::TempDir; + +fn stacker_cmd() -> Command { + Command::cargo_bin("stacker-cli").expect("stacker-cli binary not found") +} + +#[test] +fn test_logs_no_deployment_returns_error() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .arg("logs") + .assert() + .failure() + .stderr(predicate::str::contains("No deployment found").or(predicate::str::contains("docker-compose"))); +} + +#[test] +fn test_logs_help_shows_options() { + stacker_cmd() + .args(["logs", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--follow")) + .stdout(predicate::str::contains("--service")) + .stdout(predicate::str::contains("--tail")) + .stdout(predicate::str::contains("--since")); +} diff --git a/tests/cli_proxy.rs b/tests/cli_proxy.rs new file mode 100644 index 00000000..27cd03ee --- /dev/null +++ b/tests/cli_proxy.rs @@ -0,0 +1,48 @@ +//! Integration tests for `stacker proxy`. + +use assert_cmd::Command; +use predicates::prelude::*; + +fn stacker_cmd() -> Command { + Command::cargo_bin("stacker-cli").expect("stacker-cli binary not found") +} + +#[test] +fn test_proxy_add_generates_nginx_block() { + stacker_cmd() + .args(["proxy", "add", "example.com", "--upstream", "http://app:3000"]) + .assert() + .success() + .stdout(predicate::str::contains("server_name").or(predicate::str::contains("example.com"))); +} + +#[test] +fn test_proxy_add_with_ssl() { + stacker_cmd() + .args([ + "proxy", "add", "secure.example.com", + "--upstream", "http://app:3000", + "--ssl", "auto", + ]) + .assert() + .success(); +} + +#[test] +fn test_proxy_detect_help() { + stacker_cmd() + .args(["proxy", "detect", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("Detect")); +} + +#[test] +fn test_proxy_add_help_shows_options() { + stacker_cmd() + .args(["proxy", "add", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--upstream")) + .stdout(predicate::str::contains("--ssl")); +} diff --git a/tests/cli_status.rs b/tests/cli_status.rs new file mode 100644 index 00000000..2883eec3 --- /dev/null +++ b/tests/cli_status.rs @@ -0,0 +1,30 @@ +//! Integration tests for `stacker status`. + +use assert_cmd::Command; +use predicates::prelude::*; +use tempfile::TempDir; + +fn stacker_cmd() -> Command { + Command::cargo_bin("stacker-cli").expect("stacker-cli binary not found") +} + +#[test] +fn test_status_no_deployment_returns_error() { + let dir = TempDir::new().unwrap(); + + stacker_cmd() + .current_dir(dir.path()) + .arg("status") + .assert() + .failure() + .stderr(predicate::str::contains("No deployment").or(predicate::str::contains("docker-compose"))); +} + +#[test] +fn test_status_help_shows_json_flag() { + stacker_cmd() + .args(["status", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--json")); +} diff --git a/tests/cli_update.rs b/tests/cli_update.rs new file mode 100644 index 00000000..59c9ff4f --- /dev/null +++ b/tests/cli_update.rs @@ -0,0 +1,44 @@ +//! Integration tests for `stacker update`. + +use assert_cmd::Command; +use predicates::prelude::*; + +fn stacker_cmd() -> Command { + Command::cargo_bin("stacker-cli").expect("stacker-cli binary not found") +} + +#[test] +fn test_update_default_channel() { + stacker_cmd() + .arg("update") + .assert() + .success() + .stderr(predicate::str::contains("stable")); +} + +#[test] +fn test_update_beta_channel() { + stacker_cmd() + .args(["update", "--channel", "beta"]) + .assert() + .success() + .stderr(predicate::str::contains("beta")); +} + +#[test] +fn test_update_invalid_channel_fails() { + stacker_cmd() + .args(["update", "--channel", "nightly"]) + .assert() + .failure() + .stderr(predicate::str::contains("Unknown channel").or(predicate::str::contains("nightly"))); +} + +#[test] +fn test_update_help() { + stacker_cmd() + .args(["update", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--channel")); +} diff --git a/tests/mock_data/stacker_cloud.yml b/tests/mock_data/stacker_cloud.yml new file mode 100644 index 00000000..32c18e41 --- /dev/null +++ b/tests/mock_data/stacker_cloud.yml @@ -0,0 +1,12 @@ +name: cloud-test +version: "1.0" +app: + type: static + path: ./public +deploy: + target: cloud + cloud: + provider: hetzner + region: fsn1 + size: cx21 + ssh_key: ~/.ssh/id_ed25519 diff --git a/tests/mock_data/stacker_full.yml b/tests/mock_data/stacker_full.yml new file mode 100644 index 00000000..dfff01ea --- /dev/null +++ b/tests/mock_data/stacker_full.yml @@ -0,0 +1,44 @@ +name: full-test-app +version: "2.0" +organization: test-org +app: + type: node + path: ./src + build: + context: . + args: + NODE_ENV: production +services: + - name: postgres + image: postgres:16 + ports: ["5432:5432"] + environment: + POSTGRES_DB: testdb + POSTGRES_PASSWORD: testpass + volumes: + - pgdata:/var/lib/postgresql/data + - name: redis + image: redis:7-alpine + ports: ["6379:6379"] +proxy: + type: nginx + domains: + - domain: test.example.com + ssl: auto + upstream: app:3000 +deploy: + target: local +ai: + enabled: true + provider: ollama + model: llama3 + endpoint: http://localhost:11434 + tasks: [dockerfile, troubleshoot] +monitoring: + status_panel: true + healthcheck: + endpoint: /health + interval: 30s +env: + APP_PORT: "3000" + LOG_LEVEL: debug diff --git a/tests/mock_data/stacker_invalid.yml b/tests/mock_data/stacker_invalid.yml new file mode 100644 index 00000000..a633c641 --- /dev/null +++ b/tests/mock_data/stacker_invalid.yml @@ -0,0 +1,3 @@ +# Missing required name field +app: + type: cobol diff --git a/tests/mock_data/stacker_minimal.yml b/tests/mock_data/stacker_minimal.yml new file mode 100644 index 00000000..e091211d --- /dev/null +++ b/tests/mock_data/stacker_minimal.yml @@ -0,0 +1,7 @@ +name: test-app +version: "1.0" +app: + type: static + source: "./public" +deploy: + target: local diff --git a/website/assets/stacker-logo.png b/website/assets/stacker-logo.png new file mode 100644 index 00000000..c10321e5 Binary files /dev/null and b/website/assets/stacker-logo.png differ diff --git a/website/dist/assets/stacker-logo.png b/website/dist/assets/stacker-logo.png new file mode 100644 index 00000000..c10321e5 Binary files /dev/null and b/website/dist/assets/stacker-logo.png differ diff --git a/website/dist/index.html b/website/dist/index.html new file mode 100644 index 00000000..da303198 --- /dev/null +++ b/website/dist/index.html @@ -0,0 +1,546 @@ + + + + + + Stacker — AI-Powered Infrastructure CLI by TryDirect + + + + + + + + + + + + + + + + +
+ + +
+
+
+ + Open Source · AI-Powered · Multi-Cloud +
+

+ Deploy infrastructure
+ with a single command. +

+

+ Stacker scans your project, leverages AI to generate perfect Docker Compose + configurations, and deploys to any cloud provider — all from your terminal. +

+ +
+
+
+
+ + + +
+ terminal +
+
+
+ $ + + | +
+
+
+
+
+
+
+
+ + +
+
+
+ +

Everything you need to deploy.
Nothing you don't.

+

From project scanning to cloud deployment, Stacker handles the entire infrastructure lifecycle.

+
+
+
+
+ +
+

AI-Powered Configuration

+

Run stacker init --with-ai and watch as AI deeply scans your project structure, detects frameworks, databases, and services — then generates a production-ready stacker.yml.

+
Flagship Feature
+
+
+
+ +
+

Deep Project Scanning

+

Automatically detects Dockerfile, docker-compose.yml, package.json, Cargo.toml, requirements.txt, and 20+ other project files to build a complete picture.

+
+
+
+ +
+

Multi-Provider AI

+

Supports OpenAI, Anthropic, Ollama (local), and custom endpoints. Auto-detects available Ollama models — no manual configuration needed.

+
+
+
+ +
+

Multi-Cloud Deploy

+

Deploy to DigitalOcean, Hetzner, AWS, Linode, UpCloud, or your own server. One config, any cloud.

+
+
+
+ +
+

Docker Compose Generation

+

Generates production-ready docker-compose.yml with proper networking, volumes, healthchecks, and environment variables.

+
+
+
+ +
+

Proxy & SSL

+

Auto-configures Nginx or Traefik reverse proxy with Let's Encrypt SSL certificates. Zero-touch HTTPS for all your services.

+
+
+
+
+ + +
+
+
+ +

Your infrastructure,
understood by AI.

+

Stacker's AI engine doesn't just guess — it deeply analyzes your project to generate the perfect deployment configuration.

+
+
+
+
1
+
+

Project Scanning

+

Detects languages, frameworks, databases, message queues, and services from your codebase.

+
+
+ Detected: Rust + PostgreSQL + Redis + RabbitMQ +
+
+ +
+
2
+
+

Prompt Building

+

Constructs a rich context prompt with all discovered files, dependencies, and port mappings.

+
+
+ Context: 47 files analyzed, 12 dependencies mapped +
+
+ +
+
3
+
+

Config Generation

+

AI generates a complete stacker.yml with services, networking, volumes, and deployment targets.

+
+
+ Generated: stacker.yml (14 services, 3 networks) +
+
+ +
+
4
+
+

Deploy

+

One command deployes your entire stack to any cloud provider with Terraform + Ansible.

+
+
+ $ stacker deploy --cloud hetzner --region eu-central +
+
+
+
+

Supported AI Providers

+
+
+
+ +
+ OpenAI + GPT-4o, GPT-4, GPT-3.5 +
+
+
+ +
+ Anthropic + Claude 4, Sonnet, Haiku +
+
+
+ +
+ Ollama + Local models, auto-detect + Free & Private +
+
+
+ +
+ Custom + Any OpenAI-compatible API +
+
+
+
+
+ + +
+
+
+ +

Up and running
in seconds.

+
+
+
+
1
+
+

Install

+
+
+
+
+
+
+
+ $ + curl -fsSL https://raw.githubusercontent.com/trydirect/stacker/main/install.sh | bash +
+
+
+
+
+
+
+
2
+
+

Initialize with AI

+
+
+
+
+
+
+
+ $ + cd your-project +
+
+ $ + stacker init --with-ai +
+
+ ✓ Scanning project structure... +
+
+ ✓ Detected: Node.js + PostgreSQL + Redis +
+
+ ✓ Generated stacker.yml with 5 services +
+
+
+
+
+
+
+
3
+
+

Deploy

+
+
+
+
+
+
+
+ $ + stacker deploy +
+
+ ▸ Provisioning infrastructure on Hetzner... +
+
+ ✓ Stack deployed! https://your-app.example.com +
+
+
+
+
+
+
+
+
+ + +
+
+
+ +

10 commands.
Complete control.

+

Every stage of your infrastructure lifecycle, covered.

+
+
+
+
+ stacker login +
+

Authenticate with TryDirect platform using API key or OAuth.

+
+
+
+ stacker init + --with-ai +
+

Scan your project and generate stacker.yml — with optional AI assistance.

+
+
+
+ stacker deploy +
+

Deploy your stack to the configured cloud provider via Terraform + Ansible.

+
+
+
+ stacker logs + --follow +
+

Stream real-time logs from your deployed services with color-coded output.

+
+
+
+ stacker status +
+

View current deployment status, service health, and resource usage.

+
+
+
+ stacker destroy +
+

Tear down infrastructure cleanly. All cloud resources removed.

+
+
+
+ stacker config +
+

View and edit your stacker.yml configuration interactively.

+
+
+
+ stacker ai + ask +
+

Ask questions about your infrastructure or get deployment advice from AI.

+
+
+
+ stacker proxy +
+

Auto-configure Nginx/Traefik reverse proxy with SSL certificates.

+
+
+
+ stacker update +
+

Update Stacker CLI to the latest version with a single command.

+
+
+
+
+ + +
+
+
+ +

Deploy anywhere.
Your cloud, your choice.

+
+
+ + + + + + +
+
+
+ + +
+
+
+ +

Ready to deploy
smarter?

+

Join developers who ship infrastructure with AI. Open source, free forever.

+ +
+
+ 0 + CLI Commands +
+
+ 0 + AI Providers +
+
+ 0 + Cloud Platforms +
+
+ 0 + Tests Passing +
+
+
+
+
+ + + + + + + diff --git a/website/dist/main.d.ts b/website/dist/main.d.ts new file mode 100644 index 00000000..682cd2b0 --- /dev/null +++ b/website/dist/main.d.ts @@ -0,0 +1,69 @@ +interface Particle { + x: number; + y: number; + vx: number; + vy: number; + size: number; + opacity: number; + hue: number; + life: number; + maxLife: number; +} +declare class ParticleSystem { + private canvas; + private ctx; + private particles; + private mouse; + private animId; + private readonly MAX_PARTICLES; + private readonly CONNECTION_DIST; + constructor(canvas: HTMLCanvasElement); + private resize; + private init; + private createParticle; + private bindEvents; + private animate; + destroy(): void; +} +declare class TypeWriter { + private element; + private commands; + private outputs; + private outputEl; + private currentCmd; + private charIndex; + private typing; + private speed; + constructor(element: HTMLElement, outputEl: HTMLElement, commands: string[], outputs: string[][], speed?: number); + private type; + private showOutput; + private nextCommand; +} +declare class ScrollAnimator { + private observer; + constructor(); +} +declare class CounterAnimator { + private observer; + constructor(); + private animateCounter; +} +declare class StickyNav { + private nav; + private lastScrollY; + constructor(nav: HTMLElement); + private onScroll; +} +declare class MobileMenu { + private toggle; + private menu; + private isOpen; + constructor(toggle: HTMLElement, menu: HTMLElement); + private toggleMenu; + private close; +} +declare class CommandCardEffects { + constructor(); +} +declare function initSmoothScroll(): void; +declare function initCardGlow(): void; diff --git a/website/dist/main.js b/website/dist/main.js new file mode 100644 index 00000000..9ce418b3 --- /dev/null +++ b/website/dist/main.js @@ -0,0 +1,390 @@ +"use strict"; +// ============================================================ +// Stacker Website — TypeScript Interactions +// Particle system, typing effect, scroll animations, counters +// ============================================================ +class ParticleSystem { + constructor(canvas) { + this.particles = []; + this.mouse = { x: -1000, y: -1000 }; + this.animId = 0; + this.MAX_PARTICLES = 80; + this.CONNECTION_DIST = 120; + this.animate = () => { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.particles.forEach((p, i) => { + // Update position + p.x += p.vx; + p.y += p.vy; + p.life++; + // Mouse repulsion + const dx = p.x - this.mouse.x; + const dy = p.y - this.mouse.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < 150) { + const force = (150 - dist) / 150; + p.vx += (dx / dist) * force * 0.02; + p.vy += (dy / dist) * force * 0.02; + } + // Damping + p.vx *= 0.99; + p.vy *= 0.99; + // Wrap around + if (p.x < 0) + p.x = this.canvas.width; + if (p.x > this.canvas.width) + p.x = 0; + if (p.y < 0) + p.y = this.canvas.height; + if (p.y > this.canvas.height) + p.y = 0; + // Fade lifecycle + const lifeRatio = p.life / p.maxLife; + const alpha = lifeRatio < 0.1 + ? lifeRatio * 10 * p.opacity + : lifeRatio > 0.9 + ? (1 - lifeRatio) * 10 * p.opacity + : p.opacity; + // Draw particle + this.ctx.beginPath(); + this.ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); + this.ctx.fillStyle = `hsla(${p.hue}, 60%, 60%, ${alpha})`; + this.ctx.fill(); + // Respawn + if (p.life >= p.maxLife) { + this.particles[i] = this.createParticle(); + } + }); + // Draw connections + for (let i = 0; i < this.particles.length; i++) { + for (let j = i + 1; j < this.particles.length; j++) { + const a = this.particles[i]; + const b = this.particles[j]; + const dx = a.x - b.x; + const dy = a.y - b.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < this.CONNECTION_DIST) { + const opacity = (1 - dist / this.CONNECTION_DIST) * 0.15; + this.ctx.beginPath(); + this.ctx.moveTo(a.x, a.y); + this.ctx.lineTo(b.x, b.y); + this.ctx.strokeStyle = `hsla(270, 50%, 60%, ${opacity})`; + this.ctx.lineWidth = 0.5; + this.ctx.stroke(); + } + } + } + this.animId = requestAnimationFrame(this.animate); + }; + this.canvas = canvas; + this.ctx = canvas.getContext('2d'); + this.resize(); + this.init(); + this.bindEvents(); + this.animate(); + } + resize() { + this.canvas.width = window.innerWidth; + this.canvas.height = window.innerHeight; + } + init() { + for (let i = 0; i < this.MAX_PARTICLES; i++) { + this.particles.push(this.createParticle()); + } + } + createParticle() { + const hueOptions = [270, 200, 340]; // purple, blue, pink + return { + x: Math.random() * this.canvas.width, + y: Math.random() * this.canvas.height, + vx: (Math.random() - 0.5) * 0.4, + vy: (Math.random() - 0.5) * 0.4, + size: Math.random() * 2 + 0.5, + opacity: Math.random() * 0.5 + 0.1, + hue: hueOptions[Math.floor(Math.random() * hueOptions.length)], + life: 0, + maxLife: Math.random() * 600 + 200, + }; + } + bindEvents() { + window.addEventListener('resize', () => this.resize()); + window.addEventListener('mousemove', (e) => { + this.mouse.x = e.clientX; + this.mouse.y = e.clientY; + }); + } + destroy() { + cancelAnimationFrame(this.animId); + } +} +// --- Typing Effect --- +class TypeWriter { + constructor(element, outputEl, commands, outputs, speed = 50) { + this.currentCmd = 0; + this.charIndex = 0; + this.typing = true; + this.element = element; + this.outputEl = outputEl; + this.commands = commands; + this.outputs = outputs; + this.speed = speed; + this.type(); + } + type() { + const cmd = this.commands[this.currentCmd]; + if (this.charIndex < cmd.length) { + this.element.textContent += cmd[this.charIndex]; + this.charIndex++; + setTimeout(() => this.type(), this.speed + Math.random() * 40); + } + else { + // Show output after command + setTimeout(() => this.showOutput(), 400); + } + } + showOutput() { + const lines = this.outputs[this.currentCmd]; + let lineIndex = 0; + const showLine = () => { + if (lineIndex < lines.length) { + const div = document.createElement('div'); + div.className = 'terminal__line terminal__line--output'; + const span = document.createElement('span'); + span.className = 'terminal__text'; + const line = lines[lineIndex]; + if (line.startsWith('✓')) { + span.classList.add('terminal__text--success'); + } + else if (line.startsWith('▸') || line.startsWith('⟩')) { + span.classList.add('terminal__text--info'); + } + span.textContent = line; + div.appendChild(span); + this.outputEl.appendChild(div); + lineIndex++; + setTimeout(showLine, 200); + } + else { + // Move to next command + setTimeout(() => this.nextCommand(), 2500); + } + }; + showLine(); + } + nextCommand() { + this.currentCmd = (this.currentCmd + 1) % this.commands.length; + this.charIndex = 0; + this.element.textContent = ''; + this.outputEl.innerHTML = ''; + this.type(); + } +} +// --- Scroll Animations --- +class ScrollAnimator { + constructor() { + this.observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const el = entry.target; + const delay = parseInt(el.dataset.delay || '0', 10); + setTimeout(() => { + el.classList.add('is-visible'); + }, delay); + this.observer.unobserve(el); + } + }); + }, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }); + document.querySelectorAll('[data-animate]').forEach((el) => { + this.observer.observe(el); + }); + } +} +// --- Counter Animation --- +class CounterAnimator { + constructor() { + this.observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + this.animateCounter(entry.target); + this.observer.unobserve(entry.target); + } + }); + }, { threshold: 0.5 }); + document.querySelectorAll('[data-count]').forEach((el) => { + this.observer.observe(el); + }); + } + animateCounter(el) { + const target = parseInt(el.dataset.count || '0', 10); + const duration = 2000; + const start = performance.now(); + const tick = (now) => { + const elapsed = now - start; + const progress = Math.min(elapsed / duration, 1); + // Ease out cubic + const eased = 1 - Math.pow(1 - progress, 3); + const current = Math.round(eased * target); + el.textContent = current.toString(); + if (progress < 1) { + requestAnimationFrame(tick); + } + else { + el.textContent = target.toString(); + } + }; + requestAnimationFrame(tick); + } +} +// --- Smooth Nav --- +class StickyNav { + constructor(nav) { + this.lastScrollY = 0; + this.nav = nav; + window.addEventListener('scroll', () => this.onScroll(), { passive: true }); + this.onScroll(); + } + onScroll() { + const scrollY = window.scrollY; + if (scrollY > 80) { + this.nav.classList.add('nav--scrolled'); + } + else { + this.nav.classList.remove('nav--scrolled'); + } + this.lastScrollY = scrollY; + } +} +// --- Mobile Menu --- +class MobileMenu { + constructor(toggle, menu) { + this.isOpen = false; + this.toggle = toggle; + this.menu = menu; + this.toggle.addEventListener('click', () => this.toggleMenu()); + // Close on link click + menu.querySelectorAll('.nav__link').forEach((link) => { + link.addEventListener('click', () => this.close()); + }); + } + toggleMenu() { + this.isOpen = !this.isOpen; + this.menu.classList.toggle('is-open', this.isOpen); + } + close() { + this.isOpen = false; + this.menu.classList.remove('is-open'); + } +} +// --- Command Card Hover Effects --- +class CommandCardEffects { + constructor() { + document.querySelectorAll('.command-card').forEach((card) => { + card.addEventListener('mouseenter', (e) => { + const el = e.currentTarget; + el.style.setProperty('--glow-opacity', '1'); + }); + card.addEventListener('mouseleave', (e) => { + const el = e.currentTarget; + el.style.setProperty('--glow-opacity', '0'); + }); + }); + } +} +// --- Smooth Scroll for Anchor Links --- +function initSmoothScroll() { + document.querySelectorAll('a[href^="#"]').forEach((anchor) => { + anchor.addEventListener('click', (e) => { + e.preventDefault(); + const href = anchor.getAttribute('href'); + if (!href || href === '#') + return; + const target = document.querySelector(href); + if (target) { + const navHeight = document.querySelector('.nav')?.getBoundingClientRect().height || 0; + const top = target.getBoundingClientRect().top + window.scrollY - navHeight - 20; + window.scrollTo({ top, behavior: 'smooth' }); + } + }); + }); +} +// --- Mouse Glow Effect on Feature Cards --- +function initCardGlow() { + document.querySelectorAll('.feature-card, .provider-card, .cloud-logo').forEach((card) => { + card.addEventListener('mousemove', (e) => { + const mouseEvent = e; + const rect = card.getBoundingClientRect(); + const x = mouseEvent.clientX - rect.left; + const y = mouseEvent.clientY - rect.top; + card.style.setProperty('--mouse-x', `${x}px`); + card.style.setProperty('--mouse-y', `${y}px`); + }); + }); +} +// ============================================================ +// Initialize Everything +// ============================================================ +document.addEventListener('DOMContentLoaded', () => { + // Particles + const canvas = document.getElementById('particles'); + if (canvas) { + new ParticleSystem(canvas); + } + // Typing effect in hero terminal + const heroCommand = document.getElementById('heroCommand'); + const heroOutput = document.getElementById('heroOutput'); + if (heroCommand && heroOutput) { + new TypeWriter(heroCommand, heroOutput, [ + 'stacker init --with-ai', + 'stacker deploy --cloud hetzner', + 'stacker status', + 'stacker ai ask "optimize my config"', + ], [ + [ + '▸ Scanning project structure...', + '✓ Detected: Python + FastAPI + PostgreSQL + Redis', + '✓ AI generating configuration via Ollama (deepseek-r1)', + '✓ stacker.yml created with 6 services', + ], + [ + '▸ Provisioning infrastructure on Hetzner Cloud...', + '▸ Running Terraform plan (3 resources)...', + '▸ Configuring with Ansible...', + '✓ Stack deployed! https://my-app.example.com', + ], + [ + '⟩ Stack: my-project (running)', + '⟩ Services: 6/6 healthy', + '⟩ Uptime: 14d 6h 32m', + '✓ All systems operational', + ], + [ + '▸ Analyzing your stacker.yml...', + '✓ Suggestion: Add health checks to redis service', + '✓ Suggestion: Set memory limits for postgres (512MB → 1GB)', + '✓ Config optimized. Run "stacker deploy" to apply.', + ], + ], 45); + } + // Scroll animations + new ScrollAnimator(); + // Counter animations + new CounterAnimator(); + // Sticky nav + const nav = document.getElementById('nav'); + if (nav) { + new StickyNav(nav); + } + // Mobile menu + const mobileToggle = document.getElementById('mobileToggle'); + const navLinks = document.getElementById('navLinks'); + if (mobileToggle && navLinks) { + new MobileMenu(mobileToggle, navLinks); + } + // Card effects + new CommandCardEffects(); + // Smooth scroll + initSmoothScroll(); + // Card glow + initCardGlow(); +}); +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/website/dist/main.js.map b/website/dist/main.js.map new file mode 100644 index 00000000..daed7b9b --- /dev/null +++ b/website/dist/main.js.map @@ -0,0 +1 @@ +{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/dist/styles.css b/website/dist/styles.css new file mode 100644 index 00000000..b187f9e6 --- /dev/null +++ b/website/dist/styles.css @@ -0,0 +1,1380 @@ +/* ============================================================ + Stacker Website — Styles + TryDirect brand colors + modern visual effects + ============================================================ */ + +/* --- CSS Custom Properties --- */ +:root { + /* TryDirect brand */ + --color-accent: #7e57c2; + --color-accent-light: #9575cd; + --color-accent-soft: #d1c4e9; + --color-accent-deep: #673ab7; + --color-accent-dark: #512da8; + + /* Stacker logo colors */ + --color-cloud: #5DADE2; + --color-stack: #C2185B; + + /* Neutrals */ + --color-bg: #0a0a0f; + --color-bg-card: #12121a; + --color-bg-card-hover: #1a1a2e; + --color-bg-secondary: #16161f; + --color-border: #1e1e2e; + --color-border-hover: #2a2a3e; + --color-text: #e5e7eb; + --color-text-secondary: #9ca3af; + --color-text-dim: #6b7280; + + /* Status */ + --color-success: #4caf50; + --color-info: #2196f3; + --color-warning: #ff9800; + + /* Typography */ + --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; + + /* Spacing */ + --space-xs: 4px; + --space-sm: 8px; + --space-md: 16px; + --space-lg: 24px; + --space-xl: 32px; + --space-2xl: 48px; + --space-3xl: 64px; + --space-4xl: 96px; + + /* Borders */ + --radius-sm: 6px; + --radius-md: 10px; + --radius-lg: 16px; + --radius-xl: 24px; + + /* Transitions */ + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); +} + +/* --- Reset & Base --- */ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + scroll-behavior: smooth; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font-family: var(--font-sans); + background: var(--color-bg); + color: var(--color-text); + line-height: 1.6; + overflow-x: hidden; +} + +a { + color: inherit; + text-decoration: none; +} + +img { + max-width: 100%; + height: auto; +} + +code { + font-family: var(--font-mono); + font-size: 0.875em; + background: rgba(126, 87, 194, 0.15); + color: var(--color-accent-light); + padding: 2px 6px; + border-radius: 4px; +} + +/* --- Particle Canvas --- */ +#particles { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + pointer-events: none; + opacity: 0.4; +} + +/* --- Container --- */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 var(--space-lg); + position: relative; + z-index: 1; +} + +/* --- Gradient Text --- */ +.gradient-text { + background: linear-gradient(135deg, var(--color-accent), var(--color-cloud), var(--color-stack)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* --- Buttons --- */ +.btn { + display: inline-flex; + align-items: center; + gap: var(--space-sm); + font-family: var(--font-sans); + font-weight: 600; + border: none; + border-radius: var(--radius-md); + cursor: pointer; + transition: all 0.3s var(--ease-out); + text-decoration: none; + white-space: nowrap; +} + +.btn--primary { + background: linear-gradient(135deg, var(--color-accent), var(--color-accent-deep)); + color: #fff; + box-shadow: 0 4px 20px rgba(126, 87, 194, 0.3); +} + +.btn--primary:hover { + transform: translateY(-2px); + box-shadow: 0 8px 30px rgba(126, 87, 194, 0.5); +} + +.btn--ghost { + background: transparent; + color: var(--color-text); + border: 1px solid var(--color-border); +} + +.btn--ghost:hover { + border-color: var(--color-accent); + color: var(--color-accent-light); + background: rgba(126, 87, 194, 0.05); +} + +.btn--accent { + background: var(--color-accent); + color: #fff; +} + +.btn--accent:hover { + background: var(--color-accent-deep); +} + +.btn--sm { + padding: 8px 16px; + font-size: 0.85rem; +} + +.btn--lg { + padding: 14px 28px; + font-size: 1rem; +} + +/* ============================================================ + NAVIGATION + ============================================================ */ +.nav { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + padding: var(--space-md) 0; + transition: all 0.3s var(--ease-out); + background: transparent; +} + +.nav--scrolled { + background: rgba(10, 10, 15, 0.85); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border-bottom: 1px solid var(--color-border); + padding: var(--space-sm) 0; +} + +.nav__inner { + max-width: 1200px; + margin: 0 auto; + padding: 0 var(--space-lg); + display: flex; + align-items: center; + justify-content: space-between; +} + +.nav__logo { + display: flex; + align-items: center; + gap: var(--space-sm); +} + +.nav__logo-img { + width: 36px; + height: 36px; + object-fit: contain; +} + +.nav__logo-text { + font-size: 1.35rem; + font-weight: 800; + letter-spacing: -0.02em; +} + +.nav__links { + display: flex; + gap: var(--space-xl); +} + +.nav__link { + color: var(--color-text-secondary); + font-size: 0.9rem; + font-weight: 500; + transition: color 0.2s; + position: relative; +} + +.nav__link::after { + content: ''; + position: absolute; + bottom: -4px; + left: 0; + width: 0; + height: 2px; + background: var(--color-accent); + transition: width 0.3s var(--ease-out); +} + +.nav__link:hover { + color: var(--color-text); +} + +.nav__link:hover::after { + width: 100%; +} + +.nav__actions { + display: flex; + align-items: center; + gap: var(--space-md); +} + +.nav__github { + display: flex; + align-items: center; + gap: 6px; + color: var(--color-text-secondary); + font-size: 0.85rem; + font-weight: 500; + transition: color 0.2s; +} + +.nav__github:hover { + color: var(--color-text); +} + +.nav__mobile-toggle { + display: none; + flex-direction: column; + gap: 5px; + background: none; + border: none; + cursor: pointer; + padding: 4px; +} + +.nav__mobile-toggle span { + display: block; + width: 24px; + height: 2px; + background: var(--color-text); + transition: all 0.3s; +} + +/* ============================================================ + HERO + ============================================================ */ +.hero { + position: relative; + min-height: 100vh; + display: flex; + align-items: center; + padding: var(--space-4xl) 0 var(--space-3xl); + overflow: hidden; +} + +.hero__bg-grid { + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(126, 87, 194, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(126, 87, 194, 0.03) 1px, transparent 1px); + background-size: 60px 60px; + mask-image: radial-gradient(ellipse at center, black 30%, transparent 70%); + -webkit-mask-image: radial-gradient(ellipse at center, black 30%, transparent 70%); +} + +.hero__glow { + position: absolute; + top: -200px; + left: 50%; + transform: translateX(-50%); + width: 800px; + height: 600px; + background: radial-gradient( + ellipse at center, + rgba(126, 87, 194, 0.15) 0%, + rgba(93, 173, 226, 0.08) 40%, + transparent 70% + ); + filter: blur(80px); + pointer-events: none; + animation: glowPulse 8s ease-in-out infinite; +} + +@keyframes glowPulse { + 0%, 100% { opacity: 1; transform: translateX(-50%) scale(1); } + 50% { opacity: 0.7; transform: translateX(-50%) scale(1.1); } +} + +.hero__content { + text-align: center; + max-width: 800px; + margin: 0 auto; +} + +.hero__badge { + display: inline-flex; + align-items: center; + gap: var(--space-sm); + padding: 6px 16px; + border-radius: 100px; + border: 1px solid var(--color-border); + background: rgba(126, 87, 194, 0.05); + color: var(--color-text-secondary); + font-size: 0.8rem; + font-weight: 500; + margin-bottom: var(--space-xl); +} + +.hero__badge-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--color-success); + animation: dotPulse 2s ease-in-out infinite; +} + +@keyframes dotPulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.4; } +} + +.hero__title { + font-size: clamp(2.5rem, 6vw, 4.5rem); + font-weight: 900; + line-height: 1.1; + letter-spacing: -0.03em; + margin-bottom: var(--space-lg); +} + +.hero__subtitle { + font-size: clamp(1rem, 2vw, 1.2rem); + color: var(--color-text-secondary); + max-width: 600px; + margin: 0 auto var(--space-xl); + line-height: 1.7; +} + +.hero__actions { + display: flex; + justify-content: center; + gap: var(--space-md); + margin-bottom: var(--space-3xl); + flex-wrap: wrap; +} + +/* --- Terminal --- */ +.terminal { + background: #0d0d14; + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + overflow: hidden; + text-align: left; + box-shadow: + 0 20px 60px rgba(0, 0, 0, 0.5), + 0 0 0 1px rgba(126, 87, 194, 0.1), + inset 0 1px 0 rgba(255, 255, 255, 0.03); +} + +.terminal--compact { + border-radius: var(--radius-md); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); +} + +.terminal__header { + display: flex; + align-items: center; + padding: 12px 16px; + background: rgba(255, 255, 255, 0.02); + border-bottom: 1px solid var(--color-border); +} + +.terminal__dots { + display: flex; + gap: 6px; +} + +.terminal__dot { + width: 10px; + height: 10px; + border-radius: 50%; +} + +.terminal__dot--red { background: #ff5f57; } +.terminal__dot--yellow { background: #febc2e; } +.terminal__dot--green { background: #28c840; } + +.terminal__title { + margin-left: auto; + font-family: var(--font-mono); + font-size: 0.75rem; + color: var(--color-text-dim); +} + +.terminal__body { + padding: 20px; + font-family: var(--font-mono); + font-size: 0.875rem; + line-height: 1.8; +} + +.terminal--compact .terminal__body { + padding: 14px 16px; + font-size: 0.8rem; +} + +.terminal__line { + display: flex; + align-items: center; + gap: var(--space-sm); +} + +.terminal__line--output { + padding-left: 20px; +} + +.terminal__prompt { + color: var(--color-accent-light); + font-weight: 600; + user-select: none; +} + +.terminal__command, +.terminal__text { + color: var(--color-text); +} + +.terminal__text--success { + color: var(--color-success); +} + +.terminal__text--info { + color: var(--color-info); +} + +.terminal__cursor { + color: var(--color-accent); + animation: blink 1s step-end infinite; +} + +@keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} + +.terminal__output { + margin-top: var(--space-sm); +} + +.hero__terminal { + max-width: 650px; + margin: 0 auto; +} + +/* ============================================================ + SECTIONS + ============================================================ */ +.section { + padding: var(--space-4xl) 0; + position: relative; +} + +.section__header { + text-align: center; + max-width: 650px; + margin: 0 auto var(--space-3xl); +} + +.section__label { + display: inline-block; + font-family: var(--font-mono); + font-size: 0.8rem; + font-weight: 600; + color: var(--color-accent); + text-transform: uppercase; + letter-spacing: 0.1em; + margin-bottom: var(--space-md); +} + +.section__title { + font-size: clamp(2rem, 4vw, 3rem); + font-weight: 800; + line-height: 1.15; + letter-spacing: -0.02em; + margin-bottom: var(--space-md); +} + +.section__desc { + font-size: 1.1rem; + color: var(--color-text-secondary); + line-height: 1.7; +} + +/* ============================================================ + FEATURE CARDS + ============================================================ */ +.features__grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--space-lg); +} + +.feature-card { + position: relative; + padding: var(--space-xl); + background: var(--color-bg-card); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + transition: all 0.4s var(--ease-out); + overflow: hidden; +} + +.feature-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 1px; + background: linear-gradient(90deg, transparent, var(--color-accent), transparent); + opacity: 0; + transition: opacity 0.4s; +} + +.feature-card:hover { + transform: translateY(-4px); + border-color: var(--color-border-hover); + background: var(--color-bg-card-hover); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); +} + +.feature-card:hover::before { + opacity: 1; +} + +.feature-card--highlight { + grid-column: 1 / -1; + display: grid; + grid-template-columns: auto 1fr auto; + align-items: start; + gap: var(--space-lg); + border-color: rgba(126, 87, 194, 0.2); + background: linear-gradient(135deg, rgba(126, 87, 194, 0.05), rgba(93, 173, 226, 0.03)); +} + +.feature-card__icon { + width: 56px; + height: 56px; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-md); + margin-bottom: var(--space-md); + transition: transform 0.3s var(--ease-spring); +} + +.feature-card:hover .feature-card__icon { + transform: scale(1.1); +} + +.feature-card__icon--ai { + background: linear-gradient(135deg, rgba(126, 87, 194, 0.2), rgba(126, 87, 194, 0.05)); + color: var(--color-accent-light); +} + +.feature-card__icon--scan { + background: linear-gradient(135deg, rgba(93, 173, 226, 0.2), rgba(93, 173, 226, 0.05)); + color: var(--color-cloud); +} + +.feature-card__icon--providers { + background: linear-gradient(135deg, rgba(194, 24, 91, 0.2), rgba(194, 24, 91, 0.05)); + color: var(--color-stack); +} + +.feature-card__icon--cloud { + background: linear-gradient(135deg, rgba(33, 150, 243, 0.2), rgba(33, 150, 243, 0.05)); + color: var(--color-info); +} + +.feature-card__icon--docker { + background: linear-gradient(135deg, rgba(76, 175, 80, 0.2), rgba(76, 175, 80, 0.05)); + color: var(--color-success); +} + +.feature-card__icon--proxy { + background: linear-gradient(135deg, rgba(255, 152, 0, 0.2), rgba(255, 152, 0, 0.05)); + color: var(--color-warning); +} + +.feature-card__title { + font-size: 1.15rem; + font-weight: 700; + margin-bottom: var(--space-sm); +} + +.feature-card__desc { + color: var(--color-text-secondary); + font-size: 0.9rem; + line-height: 1.7; +} + +.feature-card__tag { + display: inline-flex; + padding: 4px 12px; + border-radius: 100px; + border: 1px solid rgba(126, 87, 194, 0.3); + background: rgba(126, 87, 194, 0.1); + color: var(--color-accent-light); + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.02em; + align-self: start; +} + +/* ============================================================ + AI SECTION + ============================================================ */ +.ai-section { + background: var(--color-bg-secondary); +} + +.ai-section__flow { + max-width: 700px; + margin: 0 auto var(--space-3xl); +} + +.ai-step { + display: grid; + grid-template-columns: 48px 1fr auto; + gap: var(--space-md); + align-items: start; + padding: var(--space-lg); + background: var(--color-bg-card); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + transition: all 0.3s var(--ease-out); +} + +.ai-step:hover { + border-color: var(--color-accent); + transform: translateX(4px); + box-shadow: -4px 0 0 var(--color-accent); +} + +.ai-step__number { + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-md); + background: linear-gradient(135deg, var(--color-accent), var(--color-accent-deep)); + color: #fff; + font-size: 1.2rem; + font-weight: 800; + flex-shrink: 0; +} + +.ai-step__number--final { + background: linear-gradient(135deg, var(--color-success), #2e7d32); +} + +.ai-step__content h3 { + font-size: 1.05rem; + font-weight: 700; + margin-bottom: 4px; +} + +.ai-step__content p { + color: var(--color-text-secondary); + font-size: 0.85rem; + line-height: 1.6; +} + +.ai-step__code { + background: rgba(126, 87, 194, 0.08); + border: 1px solid rgba(126, 87, 194, 0.15); + border-radius: var(--radius-sm); + padding: 8px 12px; + align-self: center; +} + +.ai-step__code code { + background: none; + padding: 0; + font-size: 0.75rem; + color: var(--color-accent-light); +} + +.ai-step__connector { + display: flex; + justify-content: center; + padding: 4px 0; + color: var(--color-text-dim); +} + +/* --- Providers Grid --- */ +.ai-section__providers-title { + text-align: center; + font-size: 1.1rem; + font-weight: 600; + margin-bottom: var(--space-lg); + color: var(--color-text-secondary); +} + +.providers__grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: var(--space-md); +} + +.provider-card { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-sm); + padding: var(--space-lg) var(--space-md); + background: var(--color-bg-card); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + text-align: center; + transition: all 0.3s var(--ease-out); + position: relative; +} + +.provider-card:hover { + transform: translateY(-4px); + border-color: var(--color-border-hover); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); +} + +.provider-card--highlight { + border-color: rgba(76, 175, 80, 0.3); + background: linear-gradient(135deg, rgba(76, 175, 80, 0.05), transparent); +} + +.provider-card__icon { + color: var(--color-accent-light); +} + +.provider-card--highlight .provider-card__icon { + color: var(--color-success); +} + +.provider-card__name { + font-weight: 700; + font-size: 0.95rem; +} + +.provider-card__detail { + font-size: 0.75rem; + color: var(--color-text-dim); +} + +.provider-card__badge { + position: absolute; + top: -10px; + right: -10px; + padding: 3px 10px; + border-radius: 100px; + background: var(--color-success); + color: #fff; + font-size: 0.65rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +/* ============================================================ + QUICK START + ============================================================ */ +.quickstart { + background: var(--color-bg); +} + +.quickstart__steps { + display: grid; + gap: var(--space-xl); + max-width: 750px; + margin: 0 auto; +} + +.quickstart__step { + display: grid; + grid-template-columns: 48px 1fr; + gap: var(--space-lg); + align-items: start; +} + +.quickstart__step-num { + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + border: 2px solid var(--color-accent); + color: var(--color-accent); + font-size: 1.2rem; + font-weight: 800; + position: relative; +} + +.quickstart__step:not(:last-child) .quickstart__step-num::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + width: 2px; + height: calc(var(--space-xl) + 48px); + background: linear-gradient(to bottom, var(--color-accent), transparent); + transform: translateX(-50%); +} + +.quickstart__step-content h3 { + font-size: 1.15rem; + font-weight: 700; + margin-bottom: var(--space-md); +} + +.quickstart__code { + margin-top: var(--space-sm); +} + +/* ============================================================ + COMMANDS + ============================================================ */ +.commands { + background: var(--color-bg-secondary); +} + +.commands__grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: var(--space-md); +} + +.command-card { + padding: var(--space-lg); + background: var(--color-bg-card); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + transition: all 0.3s var(--ease-out); + cursor: default; +} + +.command-card:hover { + border-color: var(--color-accent); + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); +} + +.command-card__header { + display: flex; + align-items: center; + gap: var(--space-sm); + margin-bottom: var(--space-sm); +} + +.command-card__cmd { + font-family: var(--font-mono); + font-size: 0.95rem; + font-weight: 700; + color: var(--color-accent-light); + background: none; + padding: 0; +} + +.command-card__flag { + font-family: var(--font-mono); + font-size: 0.75rem; + padding: 2px 8px; + border-radius: 4px; + background: rgba(76, 175, 80, 0.1); + border: 1px solid rgba(76, 175, 80, 0.2); + color: var(--color-success); +} + +.command-card__desc { + font-size: 0.85rem; + color: var(--color-text-secondary); + line-height: 1.6; +} + +/* ============================================================ + CLOUD PROVIDERS + ============================================================ */ +.providers { + background: var(--color-bg); +} + +.providers__logos { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: var(--space-xl); +} + +.cloud-logo { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-sm); + padding: var(--space-xl) var(--space-2xl); + background: var(--color-bg-card); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + transition: all 0.3s var(--ease-out); + min-width: 140px; +} + +.cloud-logo:hover { + border-color: var(--color-accent); + transform: translateY(-4px); + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3); +} + +.cloud-logo__icon { + color: var(--color-text-secondary); + transition: color 0.3s; +} + +.cloud-logo:hover .cloud-logo__icon { + color: var(--color-accent-light); +} + +.cloud-logo span { + font-size: 0.85rem; + font-weight: 600; + color: var(--color-text-secondary); +} + +/* ============================================================ + CTA + ============================================================ */ +.cta { + padding: var(--space-3xl) 0; +} + +.cta__inner { + position: relative; + text-align: center; + padding: var(--space-4xl) var(--space-2xl); + background: var(--color-bg-card); + border: 1px solid var(--color-border); + border-radius: var(--radius-xl); + overflow: hidden; +} + +.cta__glow { + position: absolute; + top: -100px; + left: 50%; + transform: translateX(-50%); + width: 600px; + height: 400px; + background: radial-gradient( + ellipse at center, + rgba(126, 87, 194, 0.12) 0%, + rgba(93, 173, 226, 0.06) 50%, + transparent 70% + ); + filter: blur(60px); + pointer-events: none; +} + +.cta__title { + font-size: clamp(2rem, 4vw, 3rem); + font-weight: 900; + line-height: 1.15; + letter-spacing: -0.02em; + margin-bottom: var(--space-md); + position: relative; +} + +.cta__desc { + font-size: 1.1rem; + color: var(--color-text-secondary); + margin-bottom: var(--space-xl); + position: relative; +} + +.cta__actions { + display: flex; + justify-content: center; + gap: var(--space-md); + margin-bottom: var(--space-3xl); + position: relative; + flex-wrap: wrap; +} + +.cta__stats { + display: flex; + justify-content: center; + gap: var(--space-3xl); + position: relative; + flex-wrap: wrap; +} + +.cta__stat { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; +} + +.cta__stat-value { + font-size: 2.5rem; + font-weight: 900; + background: linear-gradient(135deg, var(--color-accent), var(--color-cloud)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.cta__stat-label { + font-size: 0.8rem; + color: var(--color-text-dim); + font-weight: 500; +} + +/* ============================================================ + FOOTER + ============================================================ */ +.footer { + padding: var(--space-3xl) 0 var(--space-xl); + border-top: 1px solid var(--color-border); +} + +.footer__inner { + display: grid; + grid-template-columns: 1fr 2fr; + gap: var(--space-3xl); + margin-bottom: var(--space-2xl); +} + +.footer__logo { + width: 48px; + height: 48px; + object-fit: contain; + margin-bottom: var(--space-md); +} + +.footer__tagline { + color: var(--color-text-secondary); + font-size: 0.9rem; +} + +.footer__tagline a { + color: var(--color-accent-light); + transition: color 0.2s; +} + +.footer__tagline a:hover { + color: var(--color-accent); +} + +.footer__links { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: var(--space-xl); +} + +.footer__col h4 { + font-size: 0.85rem; + font-weight: 700; + color: var(--color-text); + margin-bottom: var(--space-md); +} + +.footer__col a { + display: block; + color: var(--color-text-secondary); + font-size: 0.85rem; + padding: 4px 0; + transition: color 0.2s; +} + +.footer__col a:hover { + color: var(--color-accent-light); +} + +.footer__bottom { + padding-top: var(--space-xl); + border-top: 1px solid var(--color-border); + text-align: center; +} + +.footer__bottom p { + font-size: 0.8rem; + color: var(--color-text-dim); +} + +.footer__bottom a { + color: var(--color-text-secondary); + transition: color 0.2s; +} + +.footer__bottom a:hover { + color: var(--color-accent-light); +} + +/* ============================================================ + ANIMATIONS + ============================================================ */ +[data-animate] { + opacity: 0; + transform: translateY(30px); + transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out); +} + +[data-animate].is-visible { + opacity: 1; + transform: translateY(0); +} + +/* Staggered entrance for grid items */ +.features__grid [data-animate], +.commands__grid [data-animate], +.providers__logos [data-animate] { + transition-delay: calc(var(--stagger, 0) * 100ms); +} + +/* Floating animation for decorative elements */ +@keyframes float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-10px); } +} + +/* Shimmer effect */ +@keyframes shimmer { + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } +} + +.feature-card--highlight::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(126, 87, 194, 0.03) 50%, + transparent 100% + ); + background-size: 200% 100%; + animation: shimmer 8s linear infinite; + pointer-events: none; +} + +/* --- Scrollbar --- */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: var(--color-bg); +} + +::-webkit-scrollbar-thumb { + background: var(--color-border); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--color-accent); +} + +/* ============================================================ + RESPONSIVE + ============================================================ */ +@media (max-width: 1024px) { + .features__grid { + grid-template-columns: repeat(2, 1fr); + } + + .feature-card--highlight { + grid-column: 1 / -1; + grid-template-columns: auto 1fr; + } + + .feature-card__tag { + grid-column: 2; + } + + .providers__grid { + grid-template-columns: repeat(2, 1fr); + } + + .footer__inner { + grid-template-columns: 1fr; + } +} + +@media (max-width: 768px) { + .nav__links { + display: none; + position: absolute; + top: 100%; + left: 0; + right: 0; + flex-direction: column; + padding: var(--space-lg); + background: rgba(10, 10, 15, 0.95); + backdrop-filter: blur(20px); + border-bottom: 1px solid var(--color-border); + gap: var(--space-md); + } + + .nav__links.is-open { + display: flex; + } + + .nav__mobile-toggle { + display: flex; + } + + .nav__actions .btn--accent { + display: none; + } + + .hero__title { + font-size: 2.2rem; + } + + .features__grid { + grid-template-columns: 1fr; + } + + .feature-card--highlight { + grid-template-columns: 1fr; + } + + .commands__grid { + grid-template-columns: 1fr; + } + + .providers__grid { + grid-template-columns: repeat(2, 1fr); + } + + .providers__logos { + gap: var(--space-md); + } + + .cloud-logo { + padding: var(--space-lg); + min-width: 100px; + } + + .cta__stats { + gap: var(--space-xl); + } + + .cta__stat-value { + font-size: 2rem; + } + + .footer__links { + grid-template-columns: repeat(2, 1fr); + } + + .ai-step { + grid-template-columns: 40px 1fr; + } + + .ai-step__code { + grid-column: 2; + } + + .ai-step__number { + width: 40px; + height: 40px; + font-size: 1rem; + } +} + +@media (max-width: 480px) { + .hero__actions { + flex-direction: column; + align-items: center; + } + + .quickstart__step { + grid-template-columns: 1fr; + } + + .quickstart__step-num { + margin-bottom: var(--space-sm); + } + + .quickstart__step:not(:last-child) .quickstart__step-num::after { + display: none; + } + + .cta__actions { + flex-direction: column; + align-items: center; + } + + .cta__stats { + flex-direction: column; + gap: var(--space-lg); + } + + .providers__grid { + grid-template-columns: 1fr; + } + + .footer__links { + grid-template-columns: 1fr; + } +} diff --git a/website/node_modules/.bin/is-docker b/website/node_modules/.bin/is-docker new file mode 120000 index 00000000..9896ba57 --- /dev/null +++ b/website/node_modules/.bin/is-docker @@ -0,0 +1 @@ +../is-docker/cli.js \ No newline at end of file diff --git a/website/node_modules/.bin/node-which b/website/node_modules/.bin/node-which new file mode 120000 index 00000000..6f8415ec --- /dev/null +++ b/website/node_modules/.bin/node-which @@ -0,0 +1 @@ +../which/bin/node-which \ No newline at end of file diff --git a/website/node_modules/.bin/rc b/website/node_modules/.bin/rc new file mode 120000 index 00000000..48b3cda7 --- /dev/null +++ b/website/node_modules/.bin/rc @@ -0,0 +1 @@ +../rc/cli.js \ No newline at end of file diff --git a/website/node_modules/.bin/serve b/website/node_modules/.bin/serve new file mode 120000 index 00000000..f3deda9a --- /dev/null +++ b/website/node_modules/.bin/serve @@ -0,0 +1 @@ +../serve/build/main.js \ No newline at end of file diff --git a/website/node_modules/.bin/tsc b/website/node_modules/.bin/tsc new file mode 120000 index 00000000..0863208a --- /dev/null +++ b/website/node_modules/.bin/tsc @@ -0,0 +1 @@ +../typescript/bin/tsc \ No newline at end of file diff --git a/website/node_modules/.bin/tsserver b/website/node_modules/.bin/tsserver new file mode 120000 index 00000000..f8f8f1a0 --- /dev/null +++ b/website/node_modules/.bin/tsserver @@ -0,0 +1 @@ +../typescript/bin/tsserver \ No newline at end of file diff --git a/website/node_modules/.package-lock.json b/website/node_modules/.package-lock.json new file mode 100644 index 00000000..2e164363 --- /dev/null +++ b/website/node_modules/.package-lock.json @@ -0,0 +1,1067 @@ +{ + "name": "stacker-website", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/@zeit/schemas": { + "version": "2.36.0", + "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz", + "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/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-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/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/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "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/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "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/boxen": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", + "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.0", + "chalk": "^5.0.1", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/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/chalk-template/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/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arch": "^2.2.0", + "execa": "^5.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "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/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-port-reachable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz", + "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "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/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "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/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "dev": true, + "license": "MIT" + }, + "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/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/serve": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.5.tgz", + "integrity": "sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zeit/schemas": "2.36.0", + "ajv": "8.12.0", + "arg": "5.0.2", + "boxen": "7.0.0", + "chalk": "5.0.1", + "chalk-template": "0.4.0", + "clipboardy": "3.0.0", + "compression": "1.8.1", + "is-port-reachable": "4.0.0", + "serve-handler": "6.1.6", + "update-check": "1.5.4" + }, + "bin": { + "serve": "build/main.js" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/serve-handler": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "3.3.0", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-check": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", + "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0" + } + }, + "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/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + } + } +} diff --git a/website/node_modules/@zeit/schemas/.editorconfig b/website/node_modules/@zeit/schemas/.editorconfig new file mode 100644 index 00000000..a2be8159 --- /dev/null +++ b/website/node_modules/@zeit/schemas/.editorconfig @@ -0,0 +1,37 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 +tab_width = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[{*.json,*.json.example,*.gyp,*.yml,*.yaml}] +indent_style = space +indent_size = 2 + +[{*.py,*.asm}] +indent_style = space + +[*.py] +indent_size = 4 + +[*.asm] +indent_size = 8 + +[*.md] +trim_trailing_whitespace = false + +# Ideal settings - some plugins might support these. +[*.js] +quote_type = single + +[{*.c,*.cc,*.h,*.hh,*.cpp,*.hpp,*.m,*.mm,*.mpp,*.js,*.java,*.go,*.rs,*.php,*.ng,*.jsx,*.ts,*.d,*.cs,*.swift}] +curly_bracket_next_line = false +spaces_around_operators = true +spaces_around_brackets = outside +# close enough to 1TB +indent_brace_style = K&R diff --git a/website/node_modules/@zeit/schemas/.github/workflows/ci.yaml b/website/node_modules/@zeit/schemas/.github/workflows/ci.yaml new file mode 100644 index 00000000..81ea42ad --- /dev/null +++ b/website/node_modules/@zeit/schemas/.github/workflows/ci.yaml @@ -0,0 +1,23 @@ +name: Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + name: Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 2 + - uses: actions/setup-node@v3 + timeout-minutes: 5 # See https://github.com/actions/cache/issues/810 + with: + cache: 'yarn' + + - run: yarn install --network-timeout 1000000 --frozen-lockfile + - run: yarn test diff --git a/website/node_modules/@zeit/schemas/.github/workflows/publish.yaml b/website/node_modules/@zeit/schemas/.github/workflows/publish.yaml new file mode 100644 index 00000000..b22bf1e3 --- /dev/null +++ b/website/node_modules/@zeit/schemas/.github/workflows/publish.yaml @@ -0,0 +1,24 @@ +name: Publish Package + +on: + workflow_dispatch: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + timeout-minutes: 5 # See https://github.com/actions/cache/issues/810 + with: + cache: 'yarn' + node-version: '18.x' + registry-url: 'https://registry.npmjs.org' + + - run: yarn install --network-timeout 1000000 --frozen-lockfile + - run: yarn test + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }} diff --git a/website/node_modules/@zeit/schemas/.yarnrc b/website/node_modules/@zeit/schemas/.yarnrc new file mode 100644 index 00000000..fdd705c6 --- /dev/null +++ b/website/node_modules/@zeit/schemas/.yarnrc @@ -0,0 +1 @@ +save-prefix "" diff --git a/website/node_modules/@zeit/schemas/LICENSE b/website/node_modules/@zeit/schemas/LICENSE new file mode 100644 index 00000000..36459199 --- /dev/null +++ b/website/node_modules/@zeit/schemas/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 ZEIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/website/node_modules/@zeit/schemas/README.md b/website/node_modules/@zeit/schemas/README.md new file mode 100644 index 00000000..a53b93dd --- /dev/null +++ b/website/node_modules/@zeit/schemas/README.md @@ -0,0 +1,35 @@ +# Vercel Schemas + +Schemas used across many Vercel packages to validating config files, requests to APIs, and more. + +## Why? + +- Keep schemas used across Vercel projects in sync +- We use `.js` instead of `.json` because parsing JSON takes longer + +## Usage + +To get started, pick one of the schemas in this repository and load it: + +```js +const schema = require('@zeit/schemas/deployment/config'); +``` + +Next, set up [AJV](https://github.com/epoberezkin/ajv) (the validator) and run the schema through it: + +```js +const AJV = require('ajv'); + +const ajv = new AJV({ allErrors: true }); +const isValid = ajv.validate(schema, ); + +if (!isValid) { + console.error(`The following entries are wrong: ${JSON.stringify(ajv.errors)}`); +} +``` + +That is all! :tada: + +## Contributing + +We are currently not accepting external contributions for this repository. diff --git a/website/node_modules/@zeit/schemas/deployment/config-env.js b/website/node_modules/@zeit/schemas/deployment/config-env.js new file mode 100644 index 00000000..324bf247 --- /dev/null +++ b/website/node_modules/@zeit/schemas/deployment/config-env.js @@ -0,0 +1,41 @@ +const maxEnvLength = 100; + +const EnvKey = { + type: 'string', + pattern: '^[A-z0-9_]+$', + minLength: 1, + maxLength: 256 +}; + +const EnvKeys = { + type: 'array', + minItems: 0, + maxItems: maxEnvLength, + uniqueItems: true, + items: EnvKey, + additionalProperties: false +}; + +const EnvValue = { + type: 'string', + minLength: 0, + maxLength: 65536 +}; + +// { 'FOO': 'BAR' } +const EnvObject = { + type: 'object', + minProperties: 0, + maxProperties: maxEnvLength, + patternProperties: { + '.+': EnvValue + }, + additionalProperties: false +}; + +module.exports = { + EnvKey, + EnvKeys, + EnvValue, + EnvObject +}; diff --git a/website/node_modules/@zeit/schemas/deployment/config-static.js b/website/node_modules/@zeit/schemas/deployment/config-static.js new file mode 100644 index 00000000..247083af --- /dev/null +++ b/website/node_modules/@zeit/schemas/deployment/config-static.js @@ -0,0 +1,82 @@ +module.exports = { + type: 'object', + properties: { + 'public': { + type: 'string' + }, + 'cleanUrls': { + type: [ + 'boolean', + 'array' + ] + }, + 'rewrites': { + type: 'array' + }, + 'redirects': { + type: 'array' + }, + 'headers': { + type: 'array', + maxItems: 50, + minItems: 1, + uniqueItems: true, + items: { + type: 'object', + required: ['source', 'headers'], + properties: { + source: { + type: 'string', + maxLength: 100, + minLength: 1 + }, + headers: { + type: 'array', + maxItems: 50, + minItems: 1, + uniqueItems: true, + items: { + type: 'object', + required: ['key', 'value'], + properties: { + key: { + type: 'string', + minLength: 1, + maxLength: 128, + pattern: "^[a-zA-Z0-9_!#$%&'*+.^`|~-]+$" + }, + value: { + type: 'string', + minLength: 1, + maxLength: 2048, + pattern: '^[\u0020-\u007e\u00a0-\u00ff]+$' + } + }, + additionalProperties: false + } + } + }, + additionalProperties: false + } + }, + 'directoryListing': { + type: [ + 'boolean', + 'array' + ] + }, + 'unlisted': { + type: 'array' + }, + 'trailingSlash': { + type: 'boolean' + }, + 'renderSingle': { + type: 'boolean' + }, + 'symlinks': { + type: 'boolean' + } + }, + additionalProperties: false +}; diff --git a/website/node_modules/@zeit/schemas/deployment/config.js b/website/node_modules/@zeit/schemas/deployment/config.js new file mode 100644 index 00000000..d9831e74 --- /dev/null +++ b/website/node_modules/@zeit/schemas/deployment/config.js @@ -0,0 +1,170 @@ +const {Service} = require('./service'); +const {EnvKeys, EnvObject} = require('./config-env'); +const staticSchema = require('./config-static'); + +module.exports = { + type: 'object', + additionalProperties: false, + dependencies: { + slot: { + type: 'object', + required: ['features'], + properties: { + features: { + type: 'object', + required: ['cloud'], + properties: { + cloud: { + 'const': 'v2' + } + } + } + } + } + }, + properties: { + 'name': { + type: 'string', + minLength: 1 + }, + 'project': { + type: 'string', + minLength: 1 + }, + 'alias': { + type: [ + 'string', + 'array' + ] + }, + 'env': { anyOf: [EnvObject, EnvKeys] }, + 'build': { + type: 'object', + additionalProperties: false, + properties: { + env: EnvObject + } + }, + 'scale': { + type: 'object', + patternProperties: { + '.+': { + 'type': 'object', + 'required': ['max', 'min'], + 'properties': { + max: { + anyOf: [ + { + type: 'number', + minimum: 1 + }, + {'const': 'auto'} + ] + }, + min: { + type: 'number', + minimum: 0 + } + }, + 'if': { + properties: { + max: { + type: 'number' + } + } + }, + 'then': { + properties: { + min: { + maximum: { + $data: '1/max' + } + } + } + } + } + }, + additionalProperties: false + }, + 'regions': { + type: 'array' + }, + 'dotenv': { + type: [ + 'boolean', + 'string' + ] + }, + 'files': { + type: 'array' + }, + 'type': { + type: 'string' + }, + 'forwardNpm': { + type: 'boolean' + }, + 'public': { + type: 'boolean' + }, + 'engines': { + type: 'object' + }, + 'api': { + type: 'string' + }, + 'static': staticSchema, + 'limits': { + type: 'object', + properties: { + duration: { + type: 'number', + minimum: 60000, + maximum: 60000 * 15 // max 15m runtime + }, + maxConcurrentReqs: { + type: 'number', + minimum: 1, + maximum: 256 + }, + timeout: { + type: 'number', + minimum: 60000, + maximum: 60000 * 15 // max duration + } + }, + additionalProperties: false + }, + 'features': { + type: 'object', + patternProperties: { + '.*': { + type: ['string', 'number', 'boolean'] + } + } + }, + 'github': { + type: 'object', + properties: { + enabled: { + type: 'boolean' + }, + autoAlias: { + type: 'boolean' + }, + autoJobCancelation: { + type: 'boolean' + }, + silent: { + type: 'boolean' + } + }, + additionalProperties: false + }, + 'slot': { + type: 'string', + pattern: 'c.125-m512|c1-m4096|staging-*' + }, + 'service': Service + } +}; diff --git a/website/node_modules/@zeit/schemas/deployment/service.js b/website/node_modules/@zeit/schemas/deployment/service.js new file mode 100644 index 00000000..66a792b1 --- /dev/null +++ b/website/node_modules/@zeit/schemas/deployment/service.js @@ -0,0 +1,15 @@ +const Service = { + type: 'object', + additionalProperties: false, + properties: { + port: { + type: 'number', + minimum: 1, + maximum: 32767 + } + } +}; + +module.exports = { + Service +}; diff --git a/website/node_modules/@zeit/schemas/package.json b/website/node_modules/@zeit/schemas/package.json new file mode 100644 index 00000000..a7155ebb --- /dev/null +++ b/website/node_modules/@zeit/schemas/package.json @@ -0,0 +1,28 @@ +{ + "name": "@zeit/schemas", + "version": "2.36.0", + "description": "All schemas used for validation that are shared between our projects", + "scripts": { + "test": "yarn run lint && best --verbose", + "lint": "zeit-eslint --ext .jsx,.js .", + "lint-staged": "git diff --diff-filter=ACMRT --cached --name-only '*.js' '*.jsx' | xargs zeit-eslint" + }, + "repository": "zeit/schemas", + "author": "leo", + "license": "MIT", + "devDependencies": { + "@zeit/best": "0.4.3", + "@zeit/eslint-config-node": "0.3.0", + "@zeit/git-hooks": "0.1.4", + "ajv": "6.5.1", + "eslint": "4.19.1" + }, + "eslintConfig": { + "extends": [ + "@zeit/eslint-config-node" + ] + }, + "git": { + "pre-commit": "lint-staged" + } +} diff --git a/website/node_modules/@zeit/schemas/test/deployment-env.js b/website/node_modules/@zeit/schemas/test/deployment-env.js new file mode 100644 index 00000000..9d3ed7b7 --- /dev/null +++ b/website/node_modules/@zeit/schemas/test/deployment-env.js @@ -0,0 +1,88 @@ +/* eslint camelcase: 0 */ +const AJV = require('ajv'); +const assert = require('assert'); +const { + EnvKeys, + EnvObject +} = require('../deployment/config-env'); + +const ajv = new AJV({allErrors: true}); + +// EnvKeys +exports.test_env_keys_valid = () => { + const isValid = ajv.validate(EnvKeys, [ + 'FOO', + 'BAR' + ]); + assert.equal(isValid, true); +}; + +exports.test_env_keys_too_short = () => { + const isValid = ajv.validate(EnvKeys, [ + 'FOO', + '' + ]); + assert.equal(isValid, false); + assert.equal(ajv.errors[0].keyword, 'minLength'); +}; + +exports.test_env_keys_too_long = () => { + const isValid = ajv.validate(EnvKeys, [ + 'FOO', + 'A'.repeat(257) + ]); + assert.equal(isValid, false); + assert.equal(ajv.errors[0].keyword, 'maxLength'); +}; + +exports.test_env_keys_invalid_chars = () => { + const isValid = ajv.validate(EnvKeys, [ + 'FOO', + 'BA,D' + ]); + assert.equal(isValid, false); + assert.equal(ajv.errors[0].keyword, 'pattern'); +}; + +exports.test_env_keys_invalid_type = () => { + const isValid = ajv.validate(EnvKeys, [ + 'FOO', + true + ]); + assert.equal(isValid, false); + assert.equal(ajv.errors[0].keyword, 'type'); +}; + +exports.test_env_keys_non_unique = () => { + const isValid = ajv.validate(EnvKeys, [ + 'FOO', + 'FOO' + ]); + assert.equal(isValid, false); + assert.equal(ajv.errors[0].keyword, 'uniqueItems'); +}; + +// EnvObject +exports.test_env_object_valid = () => { + const isValid = ajv.validate(EnvObject, { + FOO: 'BAR', + BAZ: '@secret' + }); + assert.equal(isValid, true); +}; + +exports.test_env_object_bad_type = () => { + const isValid = ajv.validate(EnvObject, { + FOO: true + }); + assert.equal(isValid, false); + assert.equal(ajv.errors[0].keyword, 'type'); +}; + +exports.test_env_object_too_long = () => { + const isValid = ajv.validate(EnvObject, { + FOO: 'a'.repeat(70000) + }); + assert.equal(isValid, false); + assert.equal(ajv.errors[0].keyword, 'maxLength'); +}; diff --git a/website/node_modules/@zeit/schemas/test/deployment.js b/website/node_modules/@zeit/schemas/test/deployment.js new file mode 100644 index 00000000..baec34b2 --- /dev/null +++ b/website/node_modules/@zeit/schemas/test/deployment.js @@ -0,0 +1,404 @@ +/* eslint camelcase: 0 */ +const AJV = require('ajv'); +const assert = require('assert'); +const deploymentConfigSchema = require('../deployment/config'); + +const ajv = new AJV({allErrors: true, $data: true}); + +exports.test_unknown_keys = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + foo: 1, + bar: 2 + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 2); + ['foo', 'bar'].forEach((prop, i) => { + const error = ajv.errors[i]; + assert.equal(error.keyword, 'additionalProperties'); + assert.equal(error.params.additionalProperty, prop); + }); +}; + +exports.test_features_object = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + features: { + foo: 'v2', + bar: 2 + } + }); + assert.equal(isValid, true); +}; + +exports.test_slot_key = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + features: { + cloud: 'v2' + }, + slot: 'c.125-m512' + }); + assert.equal(isValid, true); +}; + +exports.test_staging_slot_key = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + features: { + cloud: 'v2' + }, + slot: 'staging-c.5-t1-w-m1024' + }); + assert.equal(isValid, true); +}; + +exports.test_invalid_slot_key = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + features: { + cloud: 'v2' + }, + slot: 'invalid-key' + }); + assert.equal(isValid, false); +}; + +exports.test_slot_key_without_cloud_v2 = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + slot: 'c.125-m512' + }); + assert.equal(isValid, false); +}; + +exports.test_invalid_features_object = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + features: { + foo: [] + } + }); + assert.equal(isValid, false); +}; + +exports.test_features_object = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + limits: { + duration: 60000, + maxConcurrentReqs: 2, + timeout: 60000 * 2 + } + }); + assert.equal(isValid, true); +}; + +exports.test_invalid_limits_object = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + limits: { + foo: [] + } + }); + assert.equal(!isValid, true); +}; + +exports.test_valid_env_types = () => { + let isValid = ajv.validate(deploymentConfigSchema, { + env: { + VALID: '1' + } + }); + assert.equal(isValid, true); + + isValid = ajv.validate(deploymentConfigSchema, { + env: [ + 'VALID' + ] + }); + assert.equal(isValid, true); +}; + +exports.test_invalid_env_types = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + env: { + INVALID: true + } + }); + assert.equal(!isValid, true); +}; + +exports.test_valid_build_env_types = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + build: { + env: { + VALID: '1' + } + } + }); + assert.equal(isValid, true); +}; + +exports.test_invalid_build_env_types = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + build: { + env: { + INVALID: true + } + } + }); + assert.equal(!isValid, true); +}; + +exports.test_invalid_static_object = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + 'static': { + foo: [] + } + }); + assert.equal(isValid, false); +}; + +exports.test_valid_static_headers_object = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + 'static': { + headers: [ + { + source: '/_next/webpack/chunks/*', + headers: [{ + key: 'Cache-Control', + value: 'adssds' + }] + }, + { + source: '/_next/static/commons/**', + headers: [{ + key: 'Cache-Control', + value: 'public, max-age=31536000, immutable' + }] + }, + { + source: '/_next/*/page/**/*.js', + headers: [{ + key: 'Cache-Control', + value: 'public, max-age=31536000, immutable' + }] + } + ] + } + }); + + assert.equal(isValid, true); + + for (let i = 0x20; i <= 0xff; i++) { + if (i > 0x7e && i < 0xa0) { + continue; + } + + const result = ajv.validate(deploymentConfigSchema, { + 'static': { + headers: [ + { + source: '/', + headers: [{ + key: 'X-Test', + value: `value ${String.fromCharCode(i)}` + }] + } + ] + } + }); + + assert.equal(result, true, `Failed to validate for char: 0x${i.toString(16)}`); + } +}; + +exports.test_invalid_static_headers_object = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + 'static': { + headers: [ + { + source: '/_next/webpack/chunks/*', + headers: [{ + key: ':alternate-protocol', + value: 'foo\x00bar' + }] + }, + { + source: '/_next/static/commons/**', + headers: [{ + key: 'Cache-Control', + value: 'public, max-age=31536000, immutable' + }] + } + ] + } + }); + + assert.equal(isValid, false); + + // Use 256 to go above 0xff + for (let i = 0; i <= 256; i++) { + if ((i >= 0x20 && i <= 0x7e) || (i >= 0xa0 && i <= 0xff)) { + continue; + } + + const result = ajv.validate(deploymentConfigSchema, { + 'static': { + headers: { + source: '/', + headers: [{ + key: 'X-Test', + value: `value ${String.fromCharCode(i)}` + }] + } + } + }); + + assert.equal(result, false, `Failed to error for char: 0x${i.toString(16)}`); + } +}; + +exports.test_valid_static_object_trailing_slash = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + 'static': { + trailingSlash: true + } + }); + assert.equal(isValid, true); +}; + +exports.test_valid_static_object_invalid_prop = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + 'static': { + trailingSlash: [] + } + }); + assert.equal(isValid, false); +}; + +exports.test_project = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + project: 'cool-project' + }); + assert.equal(isValid, true); +}; + +exports.test_github_enabled = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + github: { + enabled: false + } + }); + assert.equal(isValid, true); +}; + +exports.test_github_silent = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + github: { + silent: true + } + }); + assert.equal(isValid, true); +}; + +exports.test_github_auto_alias = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + github: { + autoAlias: false + } + }); + assert.equal(isValid, true); +}; + +exports.test_github_auto_job_cancelation = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + github: { + autoJobCancelation: false + } + }); + assert.equal(isValid, true); +}; + +exports.test_github_additional_field = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + github: { + abc: 'bbc' + } + }); + assert.equal(isValid, false); +}; + +exports.test_scale_sfo1 = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + scale: { + sfo1: { + min: 0, + max: 1 + } + } + }); + assert.equal(isValid, true); +}; + +exports.test_scale_all = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + scale: { + all: { + min: 0, + max: 'auto' + } + } + }); + assert.equal(isValid, true); +}; + +exports.test_scale_invalid = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + scale: { + foo: { + min: -1, + max: 'auto' + } + } + }); + assert.equal(isValid, false); +}; + +exports.test_scale_invalid_min = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + scale: { + foo: { + min: 2, + max: 1 + } + } + }); + assert.equal(isValid, false); +}; + +exports.test_service_invalid = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + service: 'foo' + }); + assert.equal(isValid, false); +}; + +exports.test_service_port_valid = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + service: { + port: 80 + } + }); + assert.equal(isValid, true); +}; + +exports.test_service_port_invalid = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + service: { + port: 0 + } + }); + assert.equal(isValid, false); +}; + +exports.test_service_port_invalid_type = () => { + const isValid = ajv.validate(deploymentConfigSchema, { + service: { + port: '3000' + } + }); + assert.equal(isValid, false); +}; diff --git a/website/node_modules/@zeit/schemas/test/user.js b/website/node_modules/@zeit/schemas/test/user.js new file mode 100644 index 00000000..f6eacadc --- /dev/null +++ b/website/node_modules/@zeit/schemas/test/user.js @@ -0,0 +1,418 @@ +/* eslint camelcase: 0 */ +const AJV = require('ajv'); +const assert = require('assert'); +const { User } = require('../user'); + +const ajv = new AJV({ allErrors: true }); + +// Username +exports.test_username_null = () => { + const isValid = ajv.validate(User, { + username: null + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 1); + assert.equal(ajv.errors[0].dataPath, '.username'); + assert.equal(ajv.errors[0].message, 'should be string'); +}; + +exports.test_username_invalid_pattern = () => { + const isValid = ajv.validate(User, { + username: '!!!' + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 1); + assert.equal(ajv.errors[0].dataPath, '.username'); + assert.equal( + ajv.errors[0].message, + 'should match pattern "^[a-z0-9][a-z0-9-]*[a-z0-9]$"' + ); +}; + +exports.test_username_too_short = () => { + const isValid = ajv.validate(User, { + username: '' + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 2); + assert.equal(ajv.errors[0].dataPath, '.username'); + assert.equal( + ajv.errors[0].message, + 'should NOT be shorter than 1 characters' + ); + assert.equal(ajv.errors[1].dataPath, '.username'); + assert.equal( + ajv.errors[1].message, + 'should match pattern "^[a-z0-9][a-z0-9-]*[a-z0-9]$"' + ); +}; + +exports.test_username_too_long = () => { + const isValid = ajv.validate(User, { + username: 'a'.repeat(50) + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 1); + assert.equal(ajv.errors[0].dataPath, '.username'); + assert.equal( + ajv.errors[0].message, + 'should NOT be longer than 48 characters' + ); +}; + +exports.test_username_valid = () => { + assert(ajv.validate(User, { username: 'n8' })); + assert(ajv.validate(User, { username: 'rauchg' })); +}; + +// Name +exports.test_name_too_short = () => { + const isValid = ajv.validate(User, { + name: '' + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 1); + assert.equal(ajv.errors[0].dataPath, '.name'); + assert.equal( + ajv.errors[0].message, + 'should NOT be shorter than 1 characters' + ); +}; + +exports.test_name_too_long = () => { + const isValid = ajv.validate(User, { + name: 'a'.repeat(50) + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 1); + assert.equal(ajv.errors[0].dataPath, '.name'); + assert.equal( + ajv.errors[0].message, + 'should NOT be longer than 32 characters' + ); +}; + +exports.test_name_valid = () => { + assert(ajv.validate(User, { name: 'Nate' })); +}; + +// BillingChecked +exports.test_billing_checked_null = () => { + const isValid = ajv.validate(User, { + billingChecked: null + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 1); + assert.equal(ajv.errors[0].dataPath, '.billingChecked'); + assert.equal(ajv.errors[0].message, 'should be boolean'); +}; + +exports.test_billing_checked_valid = () => { + assert(ajv.validate(User, { billingChecked: true })); +}; + +// Avatar +exports.test_avatar_too_short = () => { + const isValid = ajv.validate(User, { + avatar: 'abc' + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 1); + assert.equal(ajv.errors[0].dataPath, '.avatar'); + assert.equal( + ajv.errors[0].message, + 'should NOT be shorter than 40 characters' + ); +}; + +exports.test_avatar_too_long = () => { + const isValid = ajv.validate(User, { + avatar: 'a'.repeat(50) + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 1); + assert.equal(ajv.errors[0].dataPath, '.avatar'); + assert.equal( + ajv.errors[0].message, + 'should NOT be longer than 40 characters' + ); +}; + +exports.test_avatar_invalid = () => { + const isValid = ajv.validate(User, { + avatar: 'n'.repeat(40) + }); + assert.equal(isValid, false); + assert.equal(ajv.errors.length, 1); + assert.equal(ajv.errors[0].dataPath, '.avatar'); + assert.equal(ajv.errors[0].message, 'should match pattern "^[0-9a-f]+$"'); +}; + +exports.test_avatar_valid = () => { + assert(ajv.validate(User, { avatar: 'a'.repeat(40) })); +}; + +exports.test_email_valid = () => { + assert(ajv.validate(User, { email: 'nate@zeit.co' })); +}; + +exports.test_email_invalid = () => { + const isValid = ajv.validate(User, { + email: `${'n'.repeat(256)}@zeit.co` + }); + assert.equal(isValid, false); +}; + +exports.test_avatar_invalid_length = () => { + assert(ajv.validate(User, { avatar: 'a'.repeat(40) })); +}; + +exports.test_platformVersion_null_valid = () => { + assert(ajv.validate(User, { platformVersion: null })); +}; + +exports.test_platformVersion_zero_invalid = () => { + const isValid = ajv.validate(User, { + platformVersion: 0 + }); + assert.equal(isValid, false); +}; + +exports.test_platformVersion_one_valid = () => { + assert(ajv.validate(User, { platformVersion: 1 })); +}; + +exports.test_platformVersion_two_valid = () => { + assert(ajv.validate(User, { platformVersion: 2 })); +}; + +exports.test_platformVersion_three_invalid = () => { + const isValid = ajv.validate(User, { + platformVersion: 3 + }); + assert.equal(isValid, false); +}; + +exports.test_importFlowGitProvider_github_valid = () => { + assert(ajv.validate(User, { importFlowGitProvider: 'github' })); +}; + +exports.test_importFlowGitProvider_gitlab_valid = () => { + assert(ajv.validate(User, { importFlowGitProvider: 'gitlab' })); +}; + +exports.test_importFlowGitProvider_bitbucket_valid = () => { + assert(ajv.validate(User, { importFlowGitProvider: 'bitbucket' })); +}; + +exports.test_importFlowGitProvider_null_valid = () => { + assert(ajv.validate(User, { importFlowGitProvider: null })); +}; + +exports.test_importFlowGitProvider_invalid_value = () => { + const isValid = ajv.validate(User, { + importFlowGitProvider: 'test' + }); + assert.equal(isValid, false); +}; + +exports.test_importFlowGitProvider_number_invalid = () => { + const isValid = ajv.validate(User, { + importFlowGitProvider: 10 + }); + assert.equal(isValid, false); +}; + +exports.test_importFlowGitNamespace_string_valid = () => { + assert(ajv.validate(User, { importFlowGitNamespace: 'test' })); +}; + +exports.test_importFlowGitNamespace_null_valid = () => { + assert(ajv.validate(User, { importFlowGitNamespace: null })); +}; + +exports.test_importFlowGitNamespace_number_invalid = () => { + const isValid = ajv.validate(User, { + importFlowGitNamespace: 10 + }); + assert.strictEqual(isValid, false); +}; + +exports.test_importFlowGitNamespace_boolean_invalid = () => { + const isValid = ajv.validate(User, { + importFlowGitNamespace: true + }); + assert.strictEqual(isValid, false); +}; + +exports.test_importFlowGitNamespaceId_string_valid = () => { + assert(ajv.validate(User, { importFlowGitNamespaceId: 'test' })); +}; + +exports.test_importFlowGitNamespaceId_number_valid = () => { + assert(ajv.validate(User, { importFlowGitNamespaceId: 10 })); +}; + +exports.test_importFlowGitNamespaceId_null_valid = () => { + assert(ajv.validate(User, { importFlowGitNamespaceId: null })); +}; + +exports.test_importFlowGitNamespaceId_boolean_invalid = () => { + const isValid = ajv.validate(User, { + importFlowGitNamespaceId: true + }); + assert.strictEqual(isValid, false); +}; + +exports.test_scopeId_valid = () => { + assert(ajv.validate(User, { scopeId: '123test' })); +}; + +exports.test_scopeId_invalid = () => { + const isValid = ajv.validate(User, { + scopeId: null + }); + assert.strictEqual(isValid, false); +}; + +exports.test_gitNamespaceId_string_valid = () => { + assert(ajv.validate(User, { gitNamespaceId: 'test' })); +}; + +exports.test_gitNamespaceId_number_valid = () => { + assert(ajv.validate(User, { gitNamespaceId: 123 })); +}; + +exports.test_gitNamespaceId_null_valid = () => { + assert(ajv.validate(User, { gitNamespaceId: null })); +}; + +exports.test_gitNamespaceId_boolean_invalid = () => { + const isValid = ajv.validate(User, { + gitNamespaceId: true + }); + assert.strictEqual(isValid, false); +}; + +exports.test_viewPreference_cards_valid = () => { + assert(ajv.validate(User, { viewPreference: 'cards' })); +}; + +exports.test_viewPreference_list_valid = () => { + assert(ajv.validate(User, { viewPreference: 'list' })); +}; + +exports.test_viewPreference_null_valid = () => { + assert(ajv.validate(User, { viewPreference: null })); +}; + +exports.test_viewPreference_invalid_value = () => { + const isValid = ajv.validate(User, { + viewPreference: 'test' + }); + assert.equal(isValid, false); +}; + +exports.test_viewPreference_number_invalid = () => { + const isValid = ajv.validate(User, { + viewPreference: 10 + }); + assert.equal(isValid, false); +}; + +exports.test_favoritesViewPreference_open_valid = () => { + assert(ajv.validate(User, { favoritesViewPreference: 'open' })); +}; + +exports.test_favoritesViewPreference_closed_valid = () => { + assert(ajv.validate(User, { favoritesViewPreference: 'closed' })); +}; + +exports.test_favoritesViewPreference_null_valid = () => { + assert(ajv.validate(User, { favoritesViewPreference: null })); +}; + +exports.test_favoritesViewPreference_invalid_value = () => { + const isValid = ajv.validate(User, { + favoritesViewPreference: 'test' + }); + assert.equal(isValid, false); +}; + +exports.test_favoritesViewPreference_number_invalid = () => { + const isValid = ajv.validate(User, { + favoritesViewPreference: 10 + }); + assert.equal(isValid, false); +}; + +exports.test_recentsViewPreference_open_valid = () => { + assert(ajv.validate(User, { recentsViewPreference: 'open' })); +}; + +exports.test_recentsViewPreference_closed_valid = () => { + assert(ajv.validate(User, { recentsViewPreference: 'closed' })); +}; + +exports.test_recentsViewPreference_null_valid = () => { + assert(ajv.validate(User, { recentsViewPreference: null })); +}; + +exports.test_recentsViewPreference_invalid_value = () => { + const isValid = ajv.validate(User, { + recentsViewPreference: 'test' + }); + assert.equal(isValid, false); +}; + +exports.test_recentsViewPreference_number_invalid = () => { + const isValid = ajv.validate(User, { + recentsViewPreference: 10 + }); + assert.equal(isValid, false); +}; + +exports.test_remoteCaching_valid = () => { + assert(ajv.validate(User, { remoteCaching: { enabled: true } })); +}; + +exports.test_remoteCaching_valid = () => { + const isValid = ajv.validate(User, { remoteCaching: { enabled: 'yes' } }); + assert.strictEqual(isValid, false); +}; + +exports.test_dismissedToasts_valid = () => { + assert(ajv.validate(User, { dismissedToasts: [] })); +}; + +exports.test_dismissedToasts_valid = () => { + assert(ajv.validate(User, { dismissedToasts: [{ name: ' exampleToast', dismissals: [{ scopeId: 'exampleScopeId', createdAt: 1656442351576 }] }] })); +}; + +exports.test_dismissedToasts_invalid = () => { + const isValid = ajv.validate(User, { dismissedToasts: [{ name: ' exampleToast', otherProp: 'abc' }] }); + assert.strictEqual(isValid, false); +}; + +exports.test_favoriteProjectsAndSpaces_valid = () => { + assert(ajv.validate(User, { favoriteProjectsAndSpaces: [] })); +}; + +exports.test_favoriteProjectsAndSpaces_valid = () => { + assert( + ajv.validate(User, { + favoriteProjectsAndSpaces: [ + { projectId: '123', scopeId: '123', scopeSlug: 'A Slug' }, + { projectId: '123', scopeId: '123', scopeSlug: 'A Slug' }, + { spaceId: '123', scopeId: '123', scopeSlug: 'A Slug' } + ] + }) + ); +}; + +exports.test_favoriteProjectsAndSpaces_invalid = () => { + const isValid = ajv.validate(User, { + favoriteProjectsAndSpaces: [{ projectId: '123', missing: '123', unknownProp: 'A Slug' }] + }); + assert.strictEqual(isValid, false); +}; diff --git a/website/node_modules/@zeit/schemas/user/index.js b/website/node_modules/@zeit/schemas/user/index.js new file mode 100644 index 00000000..714e62d7 --- /dev/null +++ b/website/node_modules/@zeit/schemas/user/index.js @@ -0,0 +1,296 @@ +const Username = { + type: 'string', + minLength: 1, + maxLength: 48, + pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$' +}; + +const Name = { + type: 'string', + minLength: 1, + maxLength: 32 +}; + +const Email = { + type: 'string', + minLength: 5, + maxLength: 256 +}; + +const ImportFlowGitProvider = { + oneOf: [ + { + 'enum': ['github', 'gitlab', 'bitbucket'] + }, + { + type: 'null' + } + ] +}; + +const ImportFlowGitNamespace = { + oneOf: [ + { + type: 'string' + }, + { + type: 'null' + } + ] +}; + +const ImportFlowGitNamespaceId = { + oneOf: [ + { + type: 'string' + }, + { + type: 'number' + }, + { + type: 'null' + } + ] +}; + +const ScopeId = { + type: 'string' +}; + +const GitNamespaceId = { + oneOf: [ + { + type: 'string' + }, + { + type: 'number' + }, + { + type: 'null' + } + ] +}; + +const ViewPreference = { + oneOf: [ + { + 'enum': ['cards', 'list'] + }, + { + type: 'null' + } + ] +}; + +const ToggleViewPreference = { + oneOf: [ + { + 'enum': ['open', 'closed'] + }, + { + type: 'null' + } + ] +}; + +const PlatformVersion = { + oneOf: [ + { + // A `null` platform version means to always use the latest + type: 'null' + }, + { + type: 'integer', + minimum: 1, + maximum: 2 + } + ] +}; + +const Avatar = { + type: 'string', + minLength: 40, + maxLength: 40, + pattern: '^[0-9a-f]+$' +}; + +const Bio = { + type: 'string' +}; + +const Website = { + type: 'string', + minLength: 4, + maxLength: 40 +}; + +const Profile = { + type: 'object', + properties: { + service: { + type: 'string' + }, + link: { + type: 'string' + } + }, + additionalProperties: false +}; + +const Profiles = { + type: 'array', + minItems: 0, + maxItems: 100, + uniqueItems: true, + items: Profile, + additionalProperties: false +}; + +const RemoteCaching = { + type: 'object', + properties: { + enabled: { + type: 'boolean' + } + }, + additionalProperties: false +}; + +const ToastDismissal = { + type: 'object', + properties: { + scopeId: { + type: 'string' + }, + createdAt: { + type: 'number' + } + }, + additionalProperties: false +}; + +const DismissedToast = { + type: 'object', + properties: { + name: { + type: 'string' + }, + dismissals: { + type: 'array', + minItems: 0, + maxItems: 50, + items: ToastDismissal + } + }, + additionalProperties: false +}; + +const DismissedToasts = { + type: 'array', + minItems: 0, + maxItems: 50, + items: DismissedToast, + additionalProperties: false +}; + +const FavoriteProjectOrSpace = { + type: 'object', + properties: { + projectId: { + type: 'string' + }, + spaceId: { + type: 'string' + }, + scopeId: { + type: 'string' + }, + scopeSlug: { + type: 'string' + } + }, + additionalProperties: false +}; + +const FavoriteProjectsAndSpaces = { + type: 'array', + minItems: 0, + items: FavoriteProjectOrSpace, + additionalProperties: false +}; + +const EnablePreviewFeedback = { + oneOf: [ + { + 'enum': [ + 'on', + 'off', + 'default', + 'on-force', + 'off-force', + 'default-force' + ] + }, + { + type: 'null' + } + ] +}; + +const DefaultTeamId = { + oneOf: [ + { + type: 'string', + maxLength: 29 + }, + { + type: 'null' + } + ] +}; + +const User = { + type: 'object', + additionalProperties: false, + properties: { + username: Username, + name: Name, + email: Email, + billingChecked: { type: 'boolean' }, + avatar: Avatar, + platformVersion: PlatformVersion, + bio: Bio, + website: Website, + profiles: Profiles, + importFlowGitProvider: ImportFlowGitProvider, + importFlowGitNamespace: ImportFlowGitNamespace, + importFlowGitNamespaceId: ImportFlowGitNamespaceId, + scopeId: ScopeId, + gitNamespaceId: GitNamespaceId, + viewPreference: ViewPreference, + favoritesViewPreference: ToggleViewPreference, + recentsViewPreference: ToggleViewPreference, + remoteCaching: RemoteCaching, + dismissedToasts: DismissedToasts, + enablePreviewFeedback: EnablePreviewFeedback, + favoriteProjectsAndSpaces: FavoriteProjectsAndSpaces, + defaultTeamId: DefaultTeamId + } +}; + +module.exports = { + User, + Username, + Name, + Email, + Avatar, + PlatformVersion, + ImportFlowGitProvider, + ImportFlowGitNamespace, + ImportFlowGitNamespaceId, + ScopeId, + GitNamespaceId, + ViewPreference, + ToggleViewPreference, + DismissedToasts +}; diff --git a/website/node_modules/ajv/.runkit_example.js b/website/node_modules/ajv/.runkit_example.js new file mode 100644 index 00000000..0d578d5d --- /dev/null +++ b/website/node_modules/ajv/.runkit_example.js @@ -0,0 +1,23 @@ +const Ajv = require("ajv") +const ajv = new Ajv({allErrors: true}) + +const schema = { + type: "object", + properties: { + foo: {type: "string"}, + bar: {type: "number", maximum: 3}, + }, + required: ["foo", "bar"], + additionalProperties: false, +} + +const validate = ajv.compile(schema) + +test({foo: "abc", bar: 2}) +test({foo: 2, bar: 4}) + +function test(data) { + const valid = validate(data) + if (valid) console.log("Valid!") + else console.log("Invalid: " + ajv.errorsText(validate.errors)) +} diff --git a/website/node_modules/ajv/LICENSE b/website/node_modules/ajv/LICENSE new file mode 100644 index 00000000..139162ad --- /dev/null +++ b/website/node_modules/ajv/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015-2021 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/website/node_modules/ajv/README.md b/website/node_modules/ajv/README.md new file mode 100644 index 00000000..ab3f774a --- /dev/null +++ b/website/node_modules/ajv/README.md @@ -0,0 +1,207 @@ +Ajv logo + +  + +# Ajv JSON schema validator + +The fastest JSON validator for Node.js and browser. + +Supports JSON Schema draft-04/06/07/2019-09/2020-12 ([draft-04 support](https://ajv.js.org/json-schema.html#draft-04) requires ajv-draft-04 package) and JSON Type Definition [RFC8927](https://datatracker.ietf.org/doc/rfc8927/). + +[![build](https://github.com/ajv-validator/ajv/workflows/build/badge.svg)](https://github.com/ajv-validator/ajv/actions?query=workflow%3Abuild) +[![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) +[![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv) +[![Coverage Status](https://coveralls.io/repos/github/ajv-validator/ajv/badge.svg?branch=master)](https://coveralls.io/github/ajv-validator/ajv?branch=master) +[![SimpleX](https://img.shields.io/badge/chat-on%20SimpleX-%2307b4b9)](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FV-6t4hoy_SsvKMi9KekdGX-VKQOhDeAe%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAm98gjwvrAEiiz_YgBoaQB9dtKTl5Om1pborUyevQwzg%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22wYrTFafovkymjUtc2vUjCQ%3D%3D%22%7D) +[![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) +[![GitHub Sponsors](https://img.shields.io/badge/$-sponsors-brightgreen)](https://github.com/sponsors/epoberezkin) + +## Ajv sponsors + +[Mozilla](https://www.mozilla.org)[](https://opencollective.com/ajv) + +[Microsoft](https://opensource.microsoft.com)[](https://opencollective.com/ajv)[](https://opencollective.com/ajv) + +[Retool](https://retool.com/?utm_source=sponsor&utm_campaign=ajv)[Tidelift](https://tidelift.com/subscription/pkg/npm-ajv?utm_source=npm-ajv&utm_medium=referral&utm_campaign=enterprise)[SimpleX](https://github.com/simplex-chat/simplex-chat)[](https://opencollective.com/ajv) + +## Contributing + +More than 100 people contributed to Ajv, and we would love to have you join the development. We welcome implementing new features that will benefit many users and ideas to improve our documentation. + +Please review [Contributing guidelines](./CONTRIBUTING.md) and [Code components](https://ajv.js.org/components.html). + +## Documentation + +All documentation is available on the [Ajv website](https://ajv.js.org). + +Some useful site links: + +- [Getting started](https://ajv.js.org/guide/getting-started.html) +- [JSON Schema vs JSON Type Definition](https://ajv.js.org/guide/schema-language.html) +- [API reference](https://ajv.js.org/api.html) +- [Strict mode](https://ajv.js.org/strict-mode.html) +- [Standalone validation code](https://ajv.js.org/standalone.html) +- [Security considerations](https://ajv.js.org/security.html) +- [Command line interface](https://ajv.js.org/packages/ajv-cli.html) +- [Frequently Asked Questions](https://ajv.js.org/faq.html) + +## Please [sponsor Ajv development](https://github.com/sponsors/epoberezkin) + +Since I asked to support Ajv development 40 people and 6 organizations contributed via GitHub and OpenCollective - this support helped receiving the MOSS grant! + +Your continuing support is very important - the funds will be used to develop and maintain Ajv once the next major version is released. + +Please sponsor Ajv via: + +- [GitHub sponsors page](https://github.com/sponsors/epoberezkin) (GitHub will match it) +- [Ajv Open Collective](https://opencollective.com/ajv) + +Thank you. + +#### Open Collective sponsors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +## Performance + +Ajv generates code to turn JSON Schemas into super-fast validation functions that are efficient for v8 optimization. + +Currently Ajv is the fastest and the most standard compliant validator according to these benchmarks: + +- [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark) - 50% faster than the second place +- [jsck benchmark](https://github.com/pandastrike/jsck#benchmarks) - 20-190% faster +- [z-schema benchmark](https://rawgit.com/zaggino/z-schema/master/benchmark/results.html) +- [themis benchmark](https://cdn.rawgit.com/playlyfe/themis/master/benchmark/results.html) + +Performance of different validators by [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark): + +[![performance](https://chart.googleapis.com/chart?chxt=x,y&cht=bhs&chco=76A4FB&chls=2.0&chbh=62,4,1&chs=600x416&chxl=-1:|ajv|@exodus/schemasafe|is-my-json-valid|djv|@cfworker/json-schema|jsonschema/=t:100,69.2,51.5,13.1,5.1,1.2)](https://github.com/ebdrup/json-schema-benchmark/blob/master/README.md#performance) + +## Features + +- Ajv implements JSON Schema [draft-06/07/2019-09/2020-12](http://json-schema.org/) standards (draft-04 is supported in v6): + - all validation keywords (see [JSON Schema validation keywords](https://ajv.js.org/json-schema.html)) + - [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md) extensions: + - NEW: keyword [discriminator](https://ajv.js.org/json-schema.html#discriminator). + - keyword [nullable](https://ajv.js.org/json-schema.html#nullable). + - full support of remote references (remote schemas have to be added with `addSchema` or compiled to be available) + - support of recursive references between schemas + - correct string lengths for strings with unicode pairs + - JSON Schema [formats](https://ajv.js.org/guide/formats.html) (with [ajv-formats](https://github.com/ajv-validator/ajv-formats) plugin). + - [validates schemas against meta-schema](https://ajv.js.org/api.html#api-validateschema) +- NEW: supports [JSON Type Definition](https://datatracker.ietf.org/doc/rfc8927/): + - all keywords (see [JSON Type Definition schema forms](https://ajv.js.org/json-type-definition.html)) + - meta-schema for JTD schemas + - "union" keyword and user-defined keywords (can be used inside "metadata" member of the schema) +- supports [browsers](https://ajv.js.org/guide/environments.html#browsers) and Node.js 10.x - current +- [asynchronous loading](https://ajv.js.org/guide/managing-schemas.html#asynchronous-schema-loading) of referenced schemas during compilation +- "All errors" validation mode with [option allErrors](https://ajv.js.org/options.html#allerrors) +- [error messages with parameters](https://ajv.js.org/api.html#validation-errors) describing error reasons to allow error message generation +- i18n error messages support with [ajv-i18n](https://github.com/ajv-validator/ajv-i18n) package +- [removing-additional-properties](https://ajv.js.org/guide/modifying-data.html#removing-additional-properties) +- [assigning defaults](https://ajv.js.org/guide/modifying-data.html#assigning-defaults) to missing properties and items +- [coercing data](https://ajv.js.org/guide/modifying-data.html#coercing-data-types) to the types specified in `type` keywords +- [user-defined keywords](https://ajv.js.org/guide/user-keywords.html) +- additional extension keywords with [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package +- [\$data reference](https://ajv.js.org/guide/combining-schemas.html#data-reference) to use values from the validated data as values for the schema keywords +- [asynchronous validation](https://ajv.js.org/guide/async-validation.html) of user-defined formats and keywords + +## Install + +To install version 8: + +``` +npm install ajv +``` + +## Getting started + +Try it in the Node.js REPL: https://runkit.com/npm/ajv + +In JavaScript: + +```javascript +// or ESM/TypeScript import +import Ajv from "ajv" +// Node.js require: +const Ajv = require("ajv") + +const ajv = new Ajv() // options can be passed, e.g. {allErrors: true} + +const schema = { + type: "object", + properties: { + foo: {type: "integer"}, + bar: {type: "string"}, + }, + required: ["foo"], + additionalProperties: false, +} + +const data = { + foo: 1, + bar: "abc", +} + +const validate = ajv.compile(schema) +const valid = validate(data) +if (!valid) console.log(validate.errors) +``` + +Learn how to use Ajv and see more examples in the [Guide: getting started](https://ajv.js.org/guide/getting-started.html) + +## Changes history + +See [https://github.com/ajv-validator/ajv/releases](https://github.com/ajv-validator/ajv/releases) + +**Please note**: [Changes in version 8.0.0](https://github.com/ajv-validator/ajv/releases/tag/v8.0.0) + +[Version 7.0.0](https://github.com/ajv-validator/ajv/releases/tag/v7.0.0) + +[Version 6.0.0](https://github.com/ajv-validator/ajv/releases/tag/v6.0.0). + +## Code of conduct + +Please review and follow the [Code of conduct](./CODE_OF_CONDUCT.md). + +Please report any unacceptable behaviour to ajv.validator@gmail.com - it will be reviewed by the project team. + +## Security contact + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. Please do NOT report security vulnerabilities via GitHub issues. + +## Open-source software support + +Ajv is a part of [Tidelift subscription](https://tidelift.com/subscription/pkg/npm-ajv?utm_source=npm-ajv&utm_medium=referral&utm_campaign=readme) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers. + +## License + +[MIT](./LICENSE) diff --git a/website/node_modules/ajv/dist/2019.d.ts b/website/node_modules/ajv/dist/2019.d.ts new file mode 100644 index 00000000..0dd0bcb0 --- /dev/null +++ b/website/node_modules/ajv/dist/2019.d.ts @@ -0,0 +1,19 @@ +import type { AnySchemaObject } from "./types"; +import AjvCore, { Options } from "./core"; +declare class Ajv2019 extends AjvCore { + constructor(opts?: Options); + _addVocabularies(): void; + _addDefaultMetaSchema(): void; + defaultMeta(): string | AnySchemaObject | undefined; +} +export default Ajv2019; +export { Format, FormatDefinition, AsyncFormatDefinition, KeywordDefinition, KeywordErrorDefinition, CodeKeywordDefinition, MacroKeywordDefinition, FuncKeywordDefinition, Vocabulary, Schema, SchemaObject, AnySchemaObject, AsyncSchema, AnySchema, ValidateFunction, AsyncValidateFunction, ErrorObject, ErrorNoParams, } from "./types"; +export { Plugin, Options, CodeOptions, InstanceOptions, Logger, ErrorsTextOptions } from "./core"; +export { SchemaCxt, SchemaObjCxt } from "./compile"; +export { KeywordCxt } from "./compile/validate"; +export { DefinedError } from "./vocabularies/errors"; +export { JSONType } from "./compile/rules"; +export { JSONSchemaType } from "./types/json-schema"; +export { _, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions } from "./compile/codegen"; +export { default as ValidationError } from "./runtime/validation_error"; +export { default as MissingRefError } from "./compile/ref_error"; diff --git a/website/node_modules/ajv/dist/2019.js b/website/node_modules/ajv/dist/2019.js new file mode 100644 index 00000000..f30eb9e1 --- /dev/null +++ b/website/node_modules/ajv/dist/2019.js @@ -0,0 +1,59 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MissingRefError = exports.ValidationError = exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = void 0; +const core_1 = require("./core"); +const draft7_1 = require("./vocabularies/draft7"); +const dynamic_1 = require("./vocabularies/dynamic"); +const next_1 = require("./vocabularies/next"); +const unevaluated_1 = require("./vocabularies/unevaluated"); +const discriminator_1 = require("./vocabularies/discriminator"); +const json_schema_2019_09_1 = require("./refs/json-schema-2019-09"); +const META_SCHEMA_ID = "https://json-schema.org/draft/2019-09/schema"; +class Ajv2019 extends core_1.default { + constructor(opts = {}) { + super({ + ...opts, + dynamicRef: true, + next: true, + unevaluated: true, + }); + } + _addVocabularies() { + super._addVocabularies(); + this.addVocabulary(dynamic_1.default); + draft7_1.default.forEach((v) => this.addVocabulary(v)); + this.addVocabulary(next_1.default); + this.addVocabulary(unevaluated_1.default); + if (this.opts.discriminator) + this.addKeyword(discriminator_1.default); + } + _addDefaultMetaSchema() { + super._addDefaultMetaSchema(); + const { $data, meta } = this.opts; + if (!meta) + return; + json_schema_2019_09_1.default.call(this, $data); + this.refs["http://json-schema.org/schema"] = META_SCHEMA_ID; + } + defaultMeta() { + return (this.opts.defaultMeta = + super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined)); + } +} +module.exports = exports = Ajv2019; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = Ajv2019; +var validate_1 = require("./compile/validate"); +Object.defineProperty(exports, "KeywordCxt", { enumerable: true, get: function () { return validate_1.KeywordCxt; } }); +var codegen_1 = require("./compile/codegen"); +Object.defineProperty(exports, "_", { enumerable: true, get: function () { return codegen_1._; } }); +Object.defineProperty(exports, "str", { enumerable: true, get: function () { return codegen_1.str; } }); +Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return codegen_1.stringify; } }); +Object.defineProperty(exports, "nil", { enumerable: true, get: function () { return codegen_1.nil; } }); +Object.defineProperty(exports, "Name", { enumerable: true, get: function () { return codegen_1.Name; } }); +Object.defineProperty(exports, "CodeGen", { enumerable: true, get: function () { return codegen_1.CodeGen; } }); +var validation_error_1 = require("./runtime/validation_error"); +Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return validation_error_1.default; } }); +var ref_error_1 = require("./compile/ref_error"); +Object.defineProperty(exports, "MissingRefError", { enumerable: true, get: function () { return ref_error_1.default; } }); +//# sourceMappingURL=2019.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/2019.js.map b/website/node_modules/ajv/dist/2019.js.map new file mode 100644 index 00000000..204e730d --- /dev/null +++ b/website/node_modules/ajv/dist/2019.js.map @@ -0,0 +1 @@ +{"version":3,"file":"2019.js","sourceRoot":"","sources":["../lib/2019.ts"],"names":[],"mappings":";;;AACA,iCAAuC;AAEvC,kDAAsD;AACtD,oDAAsD;AACtD,8CAAgD;AAChD,4DAA8D;AAC9D,gEAAwD;AACxD,oEAA0D;AAE1D,MAAM,cAAc,GAAG,8CAA8C,CAAA;AAErE,MAAM,OAAQ,SAAQ,cAAO;IAC3B,YAAY,OAAgB,EAAE;QAC5B,KAAK,CAAC;YACJ,GAAG,IAAI;YACP,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,IAAI;YACV,WAAW,EAAE,IAAI;SAClB,CAAC,CAAA;IACJ,CAAC;IAED,gBAAgB;QACd,KAAK,CAAC,gBAAgB,EAAE,CAAA;QACxB,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAA;QACrC,gBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;QACxD,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,CAAA;QAClC,IAAI,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAA;QACzC,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa;YAAE,IAAI,CAAC,UAAU,CAAC,uBAAa,CAAC,CAAA;IAC7D,CAAC;IAED,qBAAqB;QACnB,KAAK,CAAC,qBAAqB,EAAE,CAAA;QAC7B,MAAM,EAAC,KAAK,EAAE,IAAI,EAAC,GAAG,IAAI,CAAC,IAAI,CAAA;QAC/B,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,6BAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI,CAAC,+BAA+B,CAAC,GAAG,cAAc,CAAA;IAC7D,CAAC;IAED,WAAW;QACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW;YAC3B,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;IACzF,CAAC;CACF;AAED,MAAM,CAAC,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;AAClC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC,CAAA;AAE3D,kBAAe,OAAO,CAAA;AAyBtB,+CAA6C;AAArC,sGAAA,UAAU,OAAA;AAIlB,6CAA6F;AAArF,4FAAA,CAAC,OAAA;AAAE,8FAAA,GAAG,OAAA;AAAE,oGAAA,SAAS,OAAA;AAAE,8FAAA,GAAG,OAAA;AAAE,+FAAA,IAAI,OAAA;AAAQ,kGAAA,OAAO,OAAA;AACnD,+DAAqE;AAA7D,mHAAA,OAAO,OAAmB;AAClC,iDAA8D;AAAtD,4GAAA,OAAO,OAAmB"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/2020.d.ts b/website/node_modules/ajv/dist/2020.d.ts new file mode 100644 index 00000000..77b4e016 --- /dev/null +++ b/website/node_modules/ajv/dist/2020.d.ts @@ -0,0 +1,19 @@ +import type { AnySchemaObject } from "./types"; +import AjvCore, { Options } from "./core"; +declare class Ajv2020 extends AjvCore { + constructor(opts?: Options); + _addVocabularies(): void; + _addDefaultMetaSchema(): void; + defaultMeta(): string | AnySchemaObject | undefined; +} +export default Ajv2020; +export { Format, FormatDefinition, AsyncFormatDefinition, KeywordDefinition, KeywordErrorDefinition, CodeKeywordDefinition, MacroKeywordDefinition, FuncKeywordDefinition, Vocabulary, Schema, SchemaObject, AnySchemaObject, AsyncSchema, AnySchema, ValidateFunction, AsyncValidateFunction, ErrorObject, ErrorNoParams, } from "./types"; +export { Plugin, Options, CodeOptions, InstanceOptions, Logger, ErrorsTextOptions } from "./core"; +export { SchemaCxt, SchemaObjCxt } from "./compile"; +export { KeywordCxt } from "./compile/validate"; +export { DefinedError } from "./vocabularies/errors"; +export { JSONType } from "./compile/rules"; +export { JSONSchemaType } from "./types/json-schema"; +export { _, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions } from "./compile/codegen"; +export { default as ValidationError } from "./runtime/validation_error"; +export { default as MissingRefError } from "./compile/ref_error"; diff --git a/website/node_modules/ajv/dist/2020.js b/website/node_modules/ajv/dist/2020.js new file mode 100644 index 00000000..50a532ed --- /dev/null +++ b/website/node_modules/ajv/dist/2020.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MissingRefError = exports.ValidationError = exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = void 0; +const core_1 = require("./core"); +const draft2020_1 = require("./vocabularies/draft2020"); +const discriminator_1 = require("./vocabularies/discriminator"); +const json_schema_2020_12_1 = require("./refs/json-schema-2020-12"); +const META_SCHEMA_ID = "https://json-schema.org/draft/2020-12/schema"; +class Ajv2020 extends core_1.default { + constructor(opts = {}) { + super({ + ...opts, + dynamicRef: true, + next: true, + unevaluated: true, + }); + } + _addVocabularies() { + super._addVocabularies(); + draft2020_1.default.forEach((v) => this.addVocabulary(v)); + if (this.opts.discriminator) + this.addKeyword(discriminator_1.default); + } + _addDefaultMetaSchema() { + super._addDefaultMetaSchema(); + const { $data, meta } = this.opts; + if (!meta) + return; + json_schema_2020_12_1.default.call(this, $data); + this.refs["http://json-schema.org/schema"] = META_SCHEMA_ID; + } + defaultMeta() { + return (this.opts.defaultMeta = + super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined)); + } +} +module.exports = exports = Ajv2020; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = Ajv2020; +var validate_1 = require("./compile/validate"); +Object.defineProperty(exports, "KeywordCxt", { enumerable: true, get: function () { return validate_1.KeywordCxt; } }); +var codegen_1 = require("./compile/codegen"); +Object.defineProperty(exports, "_", { enumerable: true, get: function () { return codegen_1._; } }); +Object.defineProperty(exports, "str", { enumerable: true, get: function () { return codegen_1.str; } }); +Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return codegen_1.stringify; } }); +Object.defineProperty(exports, "nil", { enumerable: true, get: function () { return codegen_1.nil; } }); +Object.defineProperty(exports, "Name", { enumerable: true, get: function () { return codegen_1.Name; } }); +Object.defineProperty(exports, "CodeGen", { enumerable: true, get: function () { return codegen_1.CodeGen; } }); +var validation_error_1 = require("./runtime/validation_error"); +Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return validation_error_1.default; } }); +var ref_error_1 = require("./compile/ref_error"); +Object.defineProperty(exports, "MissingRefError", { enumerable: true, get: function () { return ref_error_1.default; } }); +//# sourceMappingURL=2020.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/2020.js.map b/website/node_modules/ajv/dist/2020.js.map new file mode 100644 index 00000000..1db6ae44 --- /dev/null +++ b/website/node_modules/ajv/dist/2020.js.map @@ -0,0 +1 @@ +{"version":3,"file":"2020.js","sourceRoot":"","sources":["../lib/2020.ts"],"names":[],"mappings":";;;AACA,iCAAuC;AAEvC,wDAA4D;AAC5D,gEAAwD;AACxD,oEAA0D;AAE1D,MAAM,cAAc,GAAG,8CAA8C,CAAA;AAErE,MAAM,OAAQ,SAAQ,cAAO;IAC3B,YAAY,OAAgB,EAAE;QAC5B,KAAK,CAAC;YACJ,GAAG,IAAI;YACP,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,IAAI;YACV,WAAW,EAAE,IAAI;SAClB,CAAC,CAAA;IACJ,CAAC;IAED,gBAAgB;QACd,KAAK,CAAC,gBAAgB,EAAE,CAAA;QACxB,mBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;QAC3D,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa;YAAE,IAAI,CAAC,UAAU,CAAC,uBAAa,CAAC,CAAA;IAC7D,CAAC;IAED,qBAAqB;QACnB,KAAK,CAAC,qBAAqB,EAAE,CAAA;QAC7B,MAAM,EAAC,KAAK,EAAE,IAAI,EAAC,GAAG,IAAI,CAAC,IAAI,CAAA;QAC/B,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,6BAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI,CAAC,+BAA+B,CAAC,GAAG,cAAc,CAAA;IAC7D,CAAC;IAED,WAAW;QACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW;YAC3B,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;IACzF,CAAC;CACF;AAED,MAAM,CAAC,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;AAClC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC,CAAA;AAE3D,kBAAe,OAAO,CAAA;AAyBtB,+CAA6C;AAArC,sGAAA,UAAU,OAAA;AAIlB,6CAA6F;AAArF,4FAAA,CAAC,OAAA;AAAE,8FAAA,GAAG,OAAA;AAAE,oGAAA,SAAS,OAAA;AAAE,8FAAA,GAAG,OAAA;AAAE,+FAAA,IAAI,OAAA;AAAQ,kGAAA,OAAO,OAAA;AACnD,+DAAqE;AAA7D,mHAAA,OAAO,OAAmB;AAClC,iDAA8D;AAAtD,4GAAA,OAAO,OAAmB"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/ajv.d.ts b/website/node_modules/ajv/dist/ajv.d.ts new file mode 100644 index 00000000..bcc17c71 --- /dev/null +++ b/website/node_modules/ajv/dist/ajv.d.ts @@ -0,0 +1,18 @@ +import type { AnySchemaObject } from "./types"; +import AjvCore from "./core"; +declare class Ajv extends AjvCore { + _addVocabularies(): void; + _addDefaultMetaSchema(): void; + defaultMeta(): string | AnySchemaObject | undefined; +} +export default Ajv; +export { Format, FormatDefinition, AsyncFormatDefinition, KeywordDefinition, KeywordErrorDefinition, CodeKeywordDefinition, MacroKeywordDefinition, FuncKeywordDefinition, Vocabulary, Schema, SchemaObject, AnySchemaObject, AsyncSchema, AnySchema, ValidateFunction, AsyncValidateFunction, SchemaValidateFunction, ErrorObject, ErrorNoParams, } from "./types"; +export { Plugin, Options, CodeOptions, InstanceOptions, Logger, ErrorsTextOptions } from "./core"; +export { SchemaCxt, SchemaObjCxt } from "./compile"; +export { KeywordCxt } from "./compile/validate"; +export { DefinedError } from "./vocabularies/errors"; +export { JSONType } from "./compile/rules"; +export { JSONSchemaType } from "./types/json-schema"; +export { _, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions } from "./compile/codegen"; +export { default as ValidationError } from "./runtime/validation_error"; +export { default as MissingRefError } from "./compile/ref_error"; diff --git a/website/node_modules/ajv/dist/ajv.js b/website/node_modules/ajv/dist/ajv.js new file mode 100644 index 00000000..755256a7 --- /dev/null +++ b/website/node_modules/ajv/dist/ajv.js @@ -0,0 +1,48 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MissingRefError = exports.ValidationError = exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = void 0; +const core_1 = require("./core"); +const draft7_1 = require("./vocabularies/draft7"); +const discriminator_1 = require("./vocabularies/discriminator"); +const draft7MetaSchema = require("./refs/json-schema-draft-07.json"); +const META_SUPPORT_DATA = ["/properties"]; +const META_SCHEMA_ID = "http://json-schema.org/draft-07/schema"; +class Ajv extends core_1.default { + _addVocabularies() { + super._addVocabularies(); + draft7_1.default.forEach((v) => this.addVocabulary(v)); + if (this.opts.discriminator) + this.addKeyword(discriminator_1.default); + } + _addDefaultMetaSchema() { + super._addDefaultMetaSchema(); + if (!this.opts.meta) + return; + const metaSchema = this.opts.$data + ? this.$dataMetaSchema(draft7MetaSchema, META_SUPPORT_DATA) + : draft7MetaSchema; + this.addMetaSchema(metaSchema, META_SCHEMA_ID, false); + this.refs["http://json-schema.org/schema"] = META_SCHEMA_ID; + } + defaultMeta() { + return (this.opts.defaultMeta = + super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined)); + } +} +module.exports = exports = Ajv; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = Ajv; +var validate_1 = require("./compile/validate"); +Object.defineProperty(exports, "KeywordCxt", { enumerable: true, get: function () { return validate_1.KeywordCxt; } }); +var codegen_1 = require("./compile/codegen"); +Object.defineProperty(exports, "_", { enumerable: true, get: function () { return codegen_1._; } }); +Object.defineProperty(exports, "str", { enumerable: true, get: function () { return codegen_1.str; } }); +Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return codegen_1.stringify; } }); +Object.defineProperty(exports, "nil", { enumerable: true, get: function () { return codegen_1.nil; } }); +Object.defineProperty(exports, "Name", { enumerable: true, get: function () { return codegen_1.Name; } }); +Object.defineProperty(exports, "CodeGen", { enumerable: true, get: function () { return codegen_1.CodeGen; } }); +var validation_error_1 = require("./runtime/validation_error"); +Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return validation_error_1.default; } }); +var ref_error_1 = require("./compile/ref_error"); +Object.defineProperty(exports, "MissingRefError", { enumerable: true, get: function () { return ref_error_1.default; } }); +//# sourceMappingURL=ajv.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/ajv.js.map b/website/node_modules/ajv/dist/ajv.js.map new file mode 100644 index 00000000..a229a00b --- /dev/null +++ b/website/node_modules/ajv/dist/ajv.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ajv.js","sourceRoot":"","sources":["../lib/ajv.ts"],"names":[],"mappings":";;;AACA,iCAA4B;AAC5B,kDAAsD;AACtD,gEAAwD;AACxD,qEAAoE;AAEpE,MAAM,iBAAiB,GAAG,CAAC,aAAa,CAAC,CAAA;AAEzC,MAAM,cAAc,GAAG,wCAAwC,CAAA;AAE/D,MAAM,GAAI,SAAQ,cAAO;IACvB,gBAAgB;QACd,KAAK,CAAC,gBAAgB,EAAE,CAAA;QACxB,gBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;QACxD,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa;YAAE,IAAI,CAAC,UAAU,CAAC,uBAAa,CAAC,CAAA;IAC7D,CAAC;IAED,qBAAqB;QACnB,KAAK,CAAC,qBAAqB,EAAE,CAAA;QAC7B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAM;QAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK;YAChC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;YAC3D,CAAC,CAAC,gBAAgB,CAAA;QACpB,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,CAAC,CAAA;QACrD,IAAI,CAAC,IAAI,CAAC,+BAA+B,CAAC,GAAG,cAAc,CAAA;IAC7D,CAAC;IAED,WAAW;QACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW;YAC3B,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;IACzF,CAAC;CACF;AAED,MAAM,CAAC,OAAO,GAAG,OAAO,GAAG,GAAG,CAAA;AAC9B,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC,CAAA;AAE3D,kBAAe,GAAG,CAAA;AA0BlB,+CAA6C;AAArC,sGAAA,UAAU,OAAA;AAIlB,6CAA6F;AAArF,4FAAA,CAAC,OAAA;AAAE,8FAAA,GAAG,OAAA;AAAE,oGAAA,SAAS,OAAA;AAAE,8FAAA,GAAG,OAAA;AAAE,+FAAA,IAAI,OAAA;AAAQ,kGAAA,OAAO,OAAA;AACnD,+DAAqE;AAA7D,mHAAA,OAAO,OAAmB;AAClC,iDAA8D;AAAtD,4GAAA,OAAO,OAAmB"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/codegen/code.d.ts b/website/node_modules/ajv/dist/compile/codegen/code.d.ts new file mode 100644 index 00000000..a0220ad7 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/codegen/code.d.ts @@ -0,0 +1,40 @@ +export declare abstract class _CodeOrName { + abstract readonly str: string; + abstract readonly names: UsedNames; + abstract toString(): string; + abstract emptyStr(): boolean; +} +export declare const IDENTIFIER: RegExp; +export declare class Name extends _CodeOrName { + readonly str: string; + constructor(s: string); + toString(): string; + emptyStr(): boolean; + get names(): UsedNames; +} +export declare class _Code extends _CodeOrName { + readonly _items: readonly CodeItem[]; + private _str?; + private _names?; + constructor(code: string | readonly CodeItem[]); + toString(): string; + emptyStr(): boolean; + get str(): string; + get names(): UsedNames; +} +export type CodeItem = Name | string | number | boolean | null; +export type UsedNames = Record; +export type Code = _Code | Name; +export type SafeExpr = Code | number | boolean | null; +export declare const nil: _Code; +type CodeArg = SafeExpr | string | undefined; +export declare function _(strs: TemplateStringsArray, ...args: CodeArg[]): _Code; +export declare function str(strs: TemplateStringsArray, ...args: (CodeArg | string[])[]): _Code; +export declare function addCodeArg(code: CodeItem[], arg: CodeArg | string[]): void; +export declare function strConcat(c1: Code, c2: Code): Code; +export declare function stringify(x: unknown): Code; +export declare function safeStringify(x: unknown): string; +export declare function getProperty(key: Code | string | number): Code; +export declare function getEsmExportName(key: Code | string | number): Code; +export declare function regexpCode(rx: RegExp): Code; +export {}; diff --git a/website/node_modules/ajv/dist/compile/codegen/code.js b/website/node_modules/ajv/dist/compile/codegen/code.js new file mode 100644 index 00000000..f8268838 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/codegen/code.js @@ -0,0 +1,155 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = void 0; +class _CodeOrName { +} +exports._CodeOrName = _CodeOrName; +exports.IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i; +class Name extends _CodeOrName { + constructor(s) { + super(); + if (!exports.IDENTIFIER.test(s)) + throw new Error("CodeGen: name must be a valid identifier"); + this.str = s; + } + toString() { + return this.str; + } + emptyStr() { + return false; + } + get names() { + return { [this.str]: 1 }; + } +} +exports.Name = Name; +class _Code extends _CodeOrName { + constructor(code) { + super(); + this._items = typeof code === "string" ? [code] : code; + } + toString() { + return this.str; + } + emptyStr() { + if (this._items.length > 1) + return false; + const item = this._items[0]; + return item === "" || item === '""'; + } + get str() { + var _a; + return ((_a = this._str) !== null && _a !== void 0 ? _a : (this._str = this._items.reduce((s, c) => `${s}${c}`, ""))); + } + get names() { + var _a; + return ((_a = this._names) !== null && _a !== void 0 ? _a : (this._names = this._items.reduce((names, c) => { + if (c instanceof Name) + names[c.str] = (names[c.str] || 0) + 1; + return names; + }, {}))); + } +} +exports._Code = _Code; +exports.nil = new _Code(""); +function _(strs, ...args) { + const code = [strs[0]]; + let i = 0; + while (i < args.length) { + addCodeArg(code, args[i]); + code.push(strs[++i]); + } + return new _Code(code); +} +exports._ = _; +const plus = new _Code("+"); +function str(strs, ...args) { + const expr = [safeStringify(strs[0])]; + let i = 0; + while (i < args.length) { + expr.push(plus); + addCodeArg(expr, args[i]); + expr.push(plus, safeStringify(strs[++i])); + } + optimize(expr); + return new _Code(expr); +} +exports.str = str; +function addCodeArg(code, arg) { + if (arg instanceof _Code) + code.push(...arg._items); + else if (arg instanceof Name) + code.push(arg); + else + code.push(interpolate(arg)); +} +exports.addCodeArg = addCodeArg; +function optimize(expr) { + let i = 1; + while (i < expr.length - 1) { + if (expr[i] === plus) { + const res = mergeExprItems(expr[i - 1], expr[i + 1]); + if (res !== undefined) { + expr.splice(i - 1, 3, res); + continue; + } + expr[i++] = "+"; + } + i++; + } +} +function mergeExprItems(a, b) { + if (b === '""') + return a; + if (a === '""') + return b; + if (typeof a == "string") { + if (b instanceof Name || a[a.length - 1] !== '"') + return; + if (typeof b != "string") + return `${a.slice(0, -1)}${b}"`; + if (b[0] === '"') + return a.slice(0, -1) + b.slice(1); + return; + } + if (typeof b == "string" && b[0] === '"' && !(a instanceof Name)) + return `"${a}${b.slice(1)}`; + return; +} +function strConcat(c1, c2) { + return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str `${c1}${c2}`; +} +exports.strConcat = strConcat; +// TODO do not allow arrays here +function interpolate(x) { + return typeof x == "number" || typeof x == "boolean" || x === null + ? x + : safeStringify(Array.isArray(x) ? x.join(",") : x); +} +function stringify(x) { + return new _Code(safeStringify(x)); +} +exports.stringify = stringify; +function safeStringify(x) { + return JSON.stringify(x) + .replace(/\u2028/g, "\\u2028") + .replace(/\u2029/g, "\\u2029"); +} +exports.safeStringify = safeStringify; +function getProperty(key) { + return typeof key == "string" && exports.IDENTIFIER.test(key) ? new _Code(`.${key}`) : _ `[${key}]`; +} +exports.getProperty = getProperty; +//Does best effort to format the name properly +function getEsmExportName(key) { + if (typeof key == "string" && exports.IDENTIFIER.test(key)) { + return new _Code(`${key}`); + } + throw new Error(`CodeGen: invalid export name: ${key}, use explicit $id name mapping`); +} +exports.getEsmExportName = getEsmExportName; +function regexpCode(rx) { + return new _Code(rx.toString()); +} +exports.regexpCode = regexpCode; +//# sourceMappingURL=code.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/codegen/code.js.map b/website/node_modules/ajv/dist/compile/codegen/code.js.map new file mode 100644 index 00000000..c5c7a396 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/codegen/code.js.map @@ -0,0 +1 @@ +{"version":3,"file":"code.js","sourceRoot":"","sources":["../../../lib/compile/codegen/code.ts"],"names":[],"mappings":";;;AAAA,MAAsB,WAAW;CAKhC;AALD,kCAKC;AAEY,QAAA,UAAU,GAAG,uBAAuB,CAAA;AAEjD,MAAa,IAAK,SAAQ,WAAW;IAEnC,YAAY,CAAS;QACnB,KAAK,EAAE,CAAA;QACP,IAAI,CAAC,kBAAU,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;QACpF,IAAI,CAAC,GAAG,GAAG,CAAC,CAAA;IACd,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,GAAG,CAAA;IACjB,CAAC;IAED,QAAQ;QACN,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,KAAK;QACP,OAAO,EAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAC,CAAA;IACxB,CAAC;CACF;AAnBD,oBAmBC;AAED,MAAa,KAAM,SAAQ,WAAW;IAKpC,YAAY,IAAkC;QAC5C,KAAK,EAAE,CAAA;QACP,IAAI,CAAC,MAAM,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACxD,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,GAAG,CAAA;IACjB,CAAC;IAED,QAAQ;QACN,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAA;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QAC3B,OAAO,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,IAAI,CAAA;IACrC,CAAC;IAED,IAAI,GAAG;;QACL,OAAO,OAAC,IAAI,CAAC,IAAI,oCAAT,IAAI,CAAC,IAAI,GAAK,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,CAAW,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,EAAC,CAAA;IACvF,CAAC;IAED,IAAI,KAAK;;QACP,OAAO,OAAC,IAAI,CAAC,MAAM,oCAAX,IAAI,CAAC,MAAM,GAAK,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAgB,EAAE,CAAC,EAAE,EAAE;YACjE,IAAI,CAAC,YAAY,IAAI;gBAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;YAC7D,OAAO,KAAK,CAAA;QACd,CAAC,EAAE,EAAE,CAAC,EAAC,CAAA;IACT,CAAC;CACF;AA9BD,sBA8BC;AAUY,QAAA,GAAG,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAA;AAIhC,SAAgB,CAAC,CAAC,IAA0B,EAAE,GAAG,IAAe;IAC9D,MAAM,IAAI,GAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAClC,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE;QACtB,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QACzB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;KACrB;IACD,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,CAAA;AACxB,CAAC;AARD,cAQC;AAED,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,CAAA;AAE3B,SAAgB,GAAG,CAAC,IAA0B,EAAE,GAAG,IAA4B;IAC7E,MAAM,IAAI,GAAe,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACjD,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE;QACtB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACf,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QACzB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;KAC1C;IACD,QAAQ,CAAC,IAAI,CAAC,CAAA;IACd,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,CAAA;AACxB,CAAC;AAVD,kBAUC;AAED,SAAgB,UAAU,CAAC,IAAgB,EAAE,GAAuB;IAClE,IAAI,GAAG,YAAY,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAA;SAC7C,IAAI,GAAG,YAAY,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;;QACvC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;AAClC,CAAC;AAJD,gCAIC;AAED,SAAS,QAAQ,CAAC,IAAgB;IAChC,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;QAC1B,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE;YACpB,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;YACpD,IAAI,GAAG,KAAK,SAAS,EAAE;gBACrB,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAA;gBAC1B,SAAQ;aACT;YACD,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAA;SAChB;QACD,CAAC,EAAE,CAAA;KACJ;AACH,CAAC;AAED,SAAS,cAAc,CAAC,CAAW,EAAE,CAAW;IAC9C,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,CAAC,CAAA;IACxB,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,CAAC,CAAA;IACxB,IAAI,OAAO,CAAC,IAAI,QAAQ,EAAE;QACxB,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;YAAE,OAAM;QACxD,IAAI,OAAO,CAAC,IAAI,QAAQ;YAAE,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAA;QACzD,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACpD,OAAM;KACP;IACD,IAAI,OAAO,CAAC,IAAI,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;IAC7F,OAAM;AACR,CAAC;AAED,SAAgB,SAAS,CAAC,EAAQ,EAAE,EAAQ;IAC1C,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA,GAAG,EAAE,GAAG,EAAE,EAAE,CAAA;AAClE,CAAC;AAFD,8BAEC;AAED,gCAAgC;AAChC,SAAS,WAAW,CAAC,CAA+C;IAClE,OAAO,OAAO,CAAC,IAAI,QAAQ,IAAI,OAAO,CAAC,IAAI,SAAS,IAAI,CAAC,KAAK,IAAI;QAChE,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACvD,CAAC;AAED,SAAgB,SAAS,CAAC,CAAU;IAClC,OAAO,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;AACpC,CAAC;AAFD,8BAEC;AAED,SAAgB,aAAa,CAAC,CAAU;IACtC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC;SAC7B,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;AAClC,CAAC;AAJD,sCAIC;AAED,SAAgB,WAAW,CAAC,GAA2B;IACrD,OAAO,OAAO,GAAG,IAAI,QAAQ,IAAI,kBAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA,IAAI,GAAG,GAAG,CAAA;AAC5F,CAAC;AAFD,kCAEC;AAED,8CAA8C;AAC9C,SAAgB,gBAAgB,CAAC,GAA2B;IAC1D,IAAI,OAAO,GAAG,IAAI,QAAQ,IAAI,kBAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAClD,OAAO,IAAI,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,CAAA;KAC3B;IACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,iCAAiC,CAAC,CAAA;AACxF,CAAC;AALD,4CAKC;AAED,SAAgB,UAAU,CAAC,EAAU;IACnC,OAAO,IAAI,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAA;AACjC,CAAC;AAFD,gCAEC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/codegen/index.d.ts b/website/node_modules/ajv/dist/compile/codegen/index.d.ts new file mode 100644 index 00000000..d586a4b4 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/codegen/index.d.ts @@ -0,0 +1,79 @@ +import type { ScopeValueSets, NameValue, ValueScope, ValueScopeName } from "./scope"; +import { _Code, Code, Name } from "./code"; +import { Scope } from "./scope"; +export { _, str, strConcat, nil, getProperty, stringify, regexpCode, Name, Code } from "./code"; +export { Scope, ScopeStore, ValueScope, ValueScopeName, ScopeValueSets, varKinds } from "./scope"; +export type SafeExpr = Code | number | boolean | null; +export type Block = Code | (() => void); +export declare const operators: { + GT: _Code; + GTE: _Code; + LT: _Code; + LTE: _Code; + EQ: _Code; + NEQ: _Code; + NOT: _Code; + OR: _Code; + AND: _Code; + ADD: _Code; +}; +export interface CodeGenOptions { + es5?: boolean; + lines?: boolean; + ownProperties?: boolean; +} +export declare class CodeGen { + readonly _scope: Scope; + readonly _extScope: ValueScope; + readonly _values: ScopeValueSets; + private readonly _nodes; + private readonly _blockStarts; + private readonly _constants; + private readonly opts; + constructor(extScope: ValueScope, opts?: CodeGenOptions); + toString(): string; + name(prefix: string): Name; + scopeName(prefix: string): ValueScopeName; + scopeValue(prefixOrName: ValueScopeName | string, value: NameValue): Name; + getScopeValue(prefix: string, keyOrRef: unknown): ValueScopeName | undefined; + scopeRefs(scopeName: Name): Code; + scopeCode(): Code; + private _def; + const(nameOrPrefix: Name | string, rhs: SafeExpr, _constant?: boolean): Name; + let(nameOrPrefix: Name | string, rhs?: SafeExpr, _constant?: boolean): Name; + var(nameOrPrefix: Name | string, rhs?: SafeExpr, _constant?: boolean): Name; + assign(lhs: Code, rhs: SafeExpr, sideEffects?: boolean): CodeGen; + add(lhs: Code, rhs: SafeExpr): CodeGen; + code(c: Block | SafeExpr): CodeGen; + object(...keyValues: [Name | string, SafeExpr | string][]): _Code; + if(condition: Code | boolean, thenBody?: Block, elseBody?: Block): CodeGen; + elseIf(condition: Code | boolean): CodeGen; + else(): CodeGen; + endIf(): CodeGen; + private _for; + for(iteration: Code, forBody?: Block): CodeGen; + forRange(nameOrPrefix: Name | string, from: SafeExpr, to: SafeExpr, forBody: (index: Name) => void, varKind?: Code): CodeGen; + forOf(nameOrPrefix: Name | string, iterable: Code, forBody: (item: Name) => void, varKind?: Code): CodeGen; + forIn(nameOrPrefix: Name | string, obj: Code, forBody: (item: Name) => void, varKind?: Code): CodeGen; + endFor(): CodeGen; + label(label: Name): CodeGen; + break(label?: Code): CodeGen; + return(value: Block | SafeExpr): CodeGen; + try(tryBody: Block, catchCode?: (e: Name) => void, finallyCode?: Block): CodeGen; + throw(error: Code): CodeGen; + block(body?: Block, nodeCount?: number): CodeGen; + endBlock(nodeCount?: number): CodeGen; + func(name: Name, args?: Code, async?: boolean, funcBody?: Block): CodeGen; + endFunc(): CodeGen; + optimize(n?: number): void; + private _leafNode; + private _blockNode; + private _endBlockNode; + private _elseNode; + private get _root(); + private get _currNode(); + private set _currNode(value); +} +export declare function not(x: T): T; +export declare function and(...args: Code[]): Code; +export declare function or(...args: Code[]): Code; diff --git a/website/node_modules/ajv/dist/compile/codegen/index.js b/website/node_modules/ajv/dist/compile/codegen/index.js new file mode 100644 index 00000000..a6c805af --- /dev/null +++ b/website/node_modules/ajv/dist/compile/codegen/index.js @@ -0,0 +1,697 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.or = exports.and = exports.not = exports.CodeGen = exports.operators = exports.varKinds = exports.ValueScopeName = exports.ValueScope = exports.Scope = exports.Name = exports.regexpCode = exports.stringify = exports.getProperty = exports.nil = exports.strConcat = exports.str = exports._ = void 0; +const code_1 = require("./code"); +const scope_1 = require("./scope"); +var code_2 = require("./code"); +Object.defineProperty(exports, "_", { enumerable: true, get: function () { return code_2._; } }); +Object.defineProperty(exports, "str", { enumerable: true, get: function () { return code_2.str; } }); +Object.defineProperty(exports, "strConcat", { enumerable: true, get: function () { return code_2.strConcat; } }); +Object.defineProperty(exports, "nil", { enumerable: true, get: function () { return code_2.nil; } }); +Object.defineProperty(exports, "getProperty", { enumerable: true, get: function () { return code_2.getProperty; } }); +Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return code_2.stringify; } }); +Object.defineProperty(exports, "regexpCode", { enumerable: true, get: function () { return code_2.regexpCode; } }); +Object.defineProperty(exports, "Name", { enumerable: true, get: function () { return code_2.Name; } }); +var scope_2 = require("./scope"); +Object.defineProperty(exports, "Scope", { enumerable: true, get: function () { return scope_2.Scope; } }); +Object.defineProperty(exports, "ValueScope", { enumerable: true, get: function () { return scope_2.ValueScope; } }); +Object.defineProperty(exports, "ValueScopeName", { enumerable: true, get: function () { return scope_2.ValueScopeName; } }); +Object.defineProperty(exports, "varKinds", { enumerable: true, get: function () { return scope_2.varKinds; } }); +exports.operators = { + GT: new code_1._Code(">"), + GTE: new code_1._Code(">="), + LT: new code_1._Code("<"), + LTE: new code_1._Code("<="), + EQ: new code_1._Code("==="), + NEQ: new code_1._Code("!=="), + NOT: new code_1._Code("!"), + OR: new code_1._Code("||"), + AND: new code_1._Code("&&"), + ADD: new code_1._Code("+"), +}; +class Node { + optimizeNodes() { + return this; + } + optimizeNames(_names, _constants) { + return this; + } +} +class Def extends Node { + constructor(varKind, name, rhs) { + super(); + this.varKind = varKind; + this.name = name; + this.rhs = rhs; + } + render({ es5, _n }) { + const varKind = es5 ? scope_1.varKinds.var : this.varKind; + const rhs = this.rhs === undefined ? "" : ` = ${this.rhs}`; + return `${varKind} ${this.name}${rhs};` + _n; + } + optimizeNames(names, constants) { + if (!names[this.name.str]) + return; + if (this.rhs) + this.rhs = optimizeExpr(this.rhs, names, constants); + return this; + } + get names() { + return this.rhs instanceof code_1._CodeOrName ? this.rhs.names : {}; + } +} +class Assign extends Node { + constructor(lhs, rhs, sideEffects) { + super(); + this.lhs = lhs; + this.rhs = rhs; + this.sideEffects = sideEffects; + } + render({ _n }) { + return `${this.lhs} = ${this.rhs};` + _n; + } + optimizeNames(names, constants) { + if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects) + return; + this.rhs = optimizeExpr(this.rhs, names, constants); + return this; + } + get names() { + const names = this.lhs instanceof code_1.Name ? {} : { ...this.lhs.names }; + return addExprNames(names, this.rhs); + } +} +class AssignOp extends Assign { + constructor(lhs, op, rhs, sideEffects) { + super(lhs, rhs, sideEffects); + this.op = op; + } + render({ _n }) { + return `${this.lhs} ${this.op}= ${this.rhs};` + _n; + } +} +class Label extends Node { + constructor(label) { + super(); + this.label = label; + this.names = {}; + } + render({ _n }) { + return `${this.label}:` + _n; + } +} +class Break extends Node { + constructor(label) { + super(); + this.label = label; + this.names = {}; + } + render({ _n }) { + const label = this.label ? ` ${this.label}` : ""; + return `break${label};` + _n; + } +} +class Throw extends Node { + constructor(error) { + super(); + this.error = error; + } + render({ _n }) { + return `throw ${this.error};` + _n; + } + get names() { + return this.error.names; + } +} +class AnyCode extends Node { + constructor(code) { + super(); + this.code = code; + } + render({ _n }) { + return `${this.code};` + _n; + } + optimizeNodes() { + return `${this.code}` ? this : undefined; + } + optimizeNames(names, constants) { + this.code = optimizeExpr(this.code, names, constants); + return this; + } + get names() { + return this.code instanceof code_1._CodeOrName ? this.code.names : {}; + } +} +class ParentNode extends Node { + constructor(nodes = []) { + super(); + this.nodes = nodes; + } + render(opts) { + return this.nodes.reduce((code, n) => code + n.render(opts), ""); + } + optimizeNodes() { + const { nodes } = this; + let i = nodes.length; + while (i--) { + const n = nodes[i].optimizeNodes(); + if (Array.isArray(n)) + nodes.splice(i, 1, ...n); + else if (n) + nodes[i] = n; + else + nodes.splice(i, 1); + } + return nodes.length > 0 ? this : undefined; + } + optimizeNames(names, constants) { + const { nodes } = this; + let i = nodes.length; + while (i--) { + // iterating backwards improves 1-pass optimization + const n = nodes[i]; + if (n.optimizeNames(names, constants)) + continue; + subtractNames(names, n.names); + nodes.splice(i, 1); + } + return nodes.length > 0 ? this : undefined; + } + get names() { + return this.nodes.reduce((names, n) => addNames(names, n.names), {}); + } +} +class BlockNode extends ParentNode { + render(opts) { + return "{" + opts._n + super.render(opts) + "}" + opts._n; + } +} +class Root extends ParentNode { +} +class Else extends BlockNode { +} +Else.kind = "else"; +class If extends BlockNode { + constructor(condition, nodes) { + super(nodes); + this.condition = condition; + } + render(opts) { + let code = `if(${this.condition})` + super.render(opts); + if (this.else) + code += "else " + this.else.render(opts); + return code; + } + optimizeNodes() { + super.optimizeNodes(); + const cond = this.condition; + if (cond === true) + return this.nodes; // else is ignored here + let e = this.else; + if (e) { + const ns = e.optimizeNodes(); + e = this.else = Array.isArray(ns) ? new Else(ns) : ns; + } + if (e) { + if (cond === false) + return e instanceof If ? e : e.nodes; + if (this.nodes.length) + return this; + return new If(not(cond), e instanceof If ? [e] : e.nodes); + } + if (cond === false || !this.nodes.length) + return undefined; + return this; + } + optimizeNames(names, constants) { + var _a; + this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants); + if (!(super.optimizeNames(names, constants) || this.else)) + return; + this.condition = optimizeExpr(this.condition, names, constants); + return this; + } + get names() { + const names = super.names; + addExprNames(names, this.condition); + if (this.else) + addNames(names, this.else.names); + return names; + } +} +If.kind = "if"; +class For extends BlockNode { +} +For.kind = "for"; +class ForLoop extends For { + constructor(iteration) { + super(); + this.iteration = iteration; + } + render(opts) { + return `for(${this.iteration})` + super.render(opts); + } + optimizeNames(names, constants) { + if (!super.optimizeNames(names, constants)) + return; + this.iteration = optimizeExpr(this.iteration, names, constants); + return this; + } + get names() { + return addNames(super.names, this.iteration.names); + } +} +class ForRange extends For { + constructor(varKind, name, from, to) { + super(); + this.varKind = varKind; + this.name = name; + this.from = from; + this.to = to; + } + render(opts) { + const varKind = opts.es5 ? scope_1.varKinds.var : this.varKind; + const { name, from, to } = this; + return `for(${varKind} ${name}=${from}; ${name}<${to}; ${name}++)` + super.render(opts); + } + get names() { + const names = addExprNames(super.names, this.from); + return addExprNames(names, this.to); + } +} +class ForIter extends For { + constructor(loop, varKind, name, iterable) { + super(); + this.loop = loop; + this.varKind = varKind; + this.name = name; + this.iterable = iterable; + } + render(opts) { + return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts); + } + optimizeNames(names, constants) { + if (!super.optimizeNames(names, constants)) + return; + this.iterable = optimizeExpr(this.iterable, names, constants); + return this; + } + get names() { + return addNames(super.names, this.iterable.names); + } +} +class Func extends BlockNode { + constructor(name, args, async) { + super(); + this.name = name; + this.args = args; + this.async = async; + } + render(opts) { + const _async = this.async ? "async " : ""; + return `${_async}function ${this.name}(${this.args})` + super.render(opts); + } +} +Func.kind = "func"; +class Return extends ParentNode { + render(opts) { + return "return " + super.render(opts); + } +} +Return.kind = "return"; +class Try extends BlockNode { + render(opts) { + let code = "try" + super.render(opts); + if (this.catch) + code += this.catch.render(opts); + if (this.finally) + code += this.finally.render(opts); + return code; + } + optimizeNodes() { + var _a, _b; + super.optimizeNodes(); + (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNodes(); + (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes(); + return this; + } + optimizeNames(names, constants) { + var _a, _b; + super.optimizeNames(names, constants); + (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants); + (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants); + return this; + } + get names() { + const names = super.names; + if (this.catch) + addNames(names, this.catch.names); + if (this.finally) + addNames(names, this.finally.names); + return names; + } +} +class Catch extends BlockNode { + constructor(error) { + super(); + this.error = error; + } + render(opts) { + return `catch(${this.error})` + super.render(opts); + } +} +Catch.kind = "catch"; +class Finally extends BlockNode { + render(opts) { + return "finally" + super.render(opts); + } +} +Finally.kind = "finally"; +class CodeGen { + constructor(extScope, opts = {}) { + this._values = {}; + this._blockStarts = []; + this._constants = {}; + this.opts = { ...opts, _n: opts.lines ? "\n" : "" }; + this._extScope = extScope; + this._scope = new scope_1.Scope({ parent: extScope }); + this._nodes = [new Root()]; + } + toString() { + return this._root.render(this.opts); + } + // returns unique name in the internal scope + name(prefix) { + return this._scope.name(prefix); + } + // reserves unique name in the external scope + scopeName(prefix) { + return this._extScope.name(prefix); + } + // reserves unique name in the external scope and assigns value to it + scopeValue(prefixOrName, value) { + const name = this._extScope.value(prefixOrName, value); + const vs = this._values[name.prefix] || (this._values[name.prefix] = new Set()); + vs.add(name); + return name; + } + getScopeValue(prefix, keyOrRef) { + return this._extScope.getValue(prefix, keyOrRef); + } + // return code that assigns values in the external scope to the names that are used internally + // (same names that were returned by gen.scopeName or gen.scopeValue) + scopeRefs(scopeName) { + return this._extScope.scopeRefs(scopeName, this._values); + } + scopeCode() { + return this._extScope.scopeCode(this._values); + } + _def(varKind, nameOrPrefix, rhs, constant) { + const name = this._scope.toName(nameOrPrefix); + if (rhs !== undefined && constant) + this._constants[name.str] = rhs; + this._leafNode(new Def(varKind, name, rhs)); + return name; + } + // `const` declaration (`var` in es5 mode) + const(nameOrPrefix, rhs, _constant) { + return this._def(scope_1.varKinds.const, nameOrPrefix, rhs, _constant); + } + // `let` declaration with optional assignment (`var` in es5 mode) + let(nameOrPrefix, rhs, _constant) { + return this._def(scope_1.varKinds.let, nameOrPrefix, rhs, _constant); + } + // `var` declaration with optional assignment + var(nameOrPrefix, rhs, _constant) { + return this._def(scope_1.varKinds.var, nameOrPrefix, rhs, _constant); + } + // assignment code + assign(lhs, rhs, sideEffects) { + return this._leafNode(new Assign(lhs, rhs, sideEffects)); + } + // `+=` code + add(lhs, rhs) { + return this._leafNode(new AssignOp(lhs, exports.operators.ADD, rhs)); + } + // appends passed SafeExpr to code or executes Block + code(c) { + if (typeof c == "function") + c(); + else if (c !== code_1.nil) + this._leafNode(new AnyCode(c)); + return this; + } + // returns code for object literal for the passed argument list of key-value pairs + object(...keyValues) { + const code = ["{"]; + for (const [key, value] of keyValues) { + if (code.length > 1) + code.push(","); + code.push(key); + if (key !== value || this.opts.es5) { + code.push(":"); + (0, code_1.addCodeArg)(code, value); + } + } + code.push("}"); + return new code_1._Code(code); + } + // `if` clause (or statement if `thenBody` and, optionally, `elseBody` are passed) + if(condition, thenBody, elseBody) { + this._blockNode(new If(condition)); + if (thenBody && elseBody) { + this.code(thenBody).else().code(elseBody).endIf(); + } + else if (thenBody) { + this.code(thenBody).endIf(); + } + else if (elseBody) { + throw new Error('CodeGen: "else" body without "then" body'); + } + return this; + } + // `else if` clause - invalid without `if` or after `else` clauses + elseIf(condition) { + return this._elseNode(new If(condition)); + } + // `else` clause - only valid after `if` or `else if` clauses + else() { + return this._elseNode(new Else()); + } + // end `if` statement (needed if gen.if was used only with condition) + endIf() { + return this._endBlockNode(If, Else); + } + _for(node, forBody) { + this._blockNode(node); + if (forBody) + this.code(forBody).endFor(); + return this; + } + // a generic `for` clause (or statement if `forBody` is passed) + for(iteration, forBody) { + return this._for(new ForLoop(iteration), forBody); + } + // `for` statement for a range of values + forRange(nameOrPrefix, from, to, forBody, varKind = this.opts.es5 ? scope_1.varKinds.var : scope_1.varKinds.let) { + const name = this._scope.toName(nameOrPrefix); + return this._for(new ForRange(varKind, name, from, to), () => forBody(name)); + } + // `for-of` statement (in es5 mode replace with a normal for loop) + forOf(nameOrPrefix, iterable, forBody, varKind = scope_1.varKinds.const) { + const name = this._scope.toName(nameOrPrefix); + if (this.opts.es5) { + const arr = iterable instanceof code_1.Name ? iterable : this.var("_arr", iterable); + return this.forRange("_i", 0, (0, code_1._) `${arr}.length`, (i) => { + this.var(name, (0, code_1._) `${arr}[${i}]`); + forBody(name); + }); + } + return this._for(new ForIter("of", varKind, name, iterable), () => forBody(name)); + } + // `for-in` statement. + // With option `ownProperties` replaced with a `for-of` loop for object keys + forIn(nameOrPrefix, obj, forBody, varKind = this.opts.es5 ? scope_1.varKinds.var : scope_1.varKinds.const) { + if (this.opts.ownProperties) { + return this.forOf(nameOrPrefix, (0, code_1._) `Object.keys(${obj})`, forBody); + } + const name = this._scope.toName(nameOrPrefix); + return this._for(new ForIter("in", varKind, name, obj), () => forBody(name)); + } + // end `for` loop + endFor() { + return this._endBlockNode(For); + } + // `label` statement + label(label) { + return this._leafNode(new Label(label)); + } + // `break` statement + break(label) { + return this._leafNode(new Break(label)); + } + // `return` statement + return(value) { + const node = new Return(); + this._blockNode(node); + this.code(value); + if (node.nodes.length !== 1) + throw new Error('CodeGen: "return" should have one node'); + return this._endBlockNode(Return); + } + // `try` statement + try(tryBody, catchCode, finallyCode) { + if (!catchCode && !finallyCode) + throw new Error('CodeGen: "try" without "catch" and "finally"'); + const node = new Try(); + this._blockNode(node); + this.code(tryBody); + if (catchCode) { + const error = this.name("e"); + this._currNode = node.catch = new Catch(error); + catchCode(error); + } + if (finallyCode) { + this._currNode = node.finally = new Finally(); + this.code(finallyCode); + } + return this._endBlockNode(Catch, Finally); + } + // `throw` statement + throw(error) { + return this._leafNode(new Throw(error)); + } + // start self-balancing block + block(body, nodeCount) { + this._blockStarts.push(this._nodes.length); + if (body) + this.code(body).endBlock(nodeCount); + return this; + } + // end the current self-balancing block + endBlock(nodeCount) { + const len = this._blockStarts.pop(); + if (len === undefined) + throw new Error("CodeGen: not in self-balancing block"); + const toClose = this._nodes.length - len; + if (toClose < 0 || (nodeCount !== undefined && toClose !== nodeCount)) { + throw new Error(`CodeGen: wrong number of nodes: ${toClose} vs ${nodeCount} expected`); + } + this._nodes.length = len; + return this; + } + // `function` heading (or definition if funcBody is passed) + func(name, args = code_1.nil, async, funcBody) { + this._blockNode(new Func(name, args, async)); + if (funcBody) + this.code(funcBody).endFunc(); + return this; + } + // end function definition + endFunc() { + return this._endBlockNode(Func); + } + optimize(n = 1) { + while (n-- > 0) { + this._root.optimizeNodes(); + this._root.optimizeNames(this._root.names, this._constants); + } + } + _leafNode(node) { + this._currNode.nodes.push(node); + return this; + } + _blockNode(node) { + this._currNode.nodes.push(node); + this._nodes.push(node); + } + _endBlockNode(N1, N2) { + const n = this._currNode; + if (n instanceof N1 || (N2 && n instanceof N2)) { + this._nodes.pop(); + return this; + } + throw new Error(`CodeGen: not in block "${N2 ? `${N1.kind}/${N2.kind}` : N1.kind}"`); + } + _elseNode(node) { + const n = this._currNode; + if (!(n instanceof If)) { + throw new Error('CodeGen: "else" without "if"'); + } + this._currNode = n.else = node; + return this; + } + get _root() { + return this._nodes[0]; + } + get _currNode() { + const ns = this._nodes; + return ns[ns.length - 1]; + } + set _currNode(node) { + const ns = this._nodes; + ns[ns.length - 1] = node; + } +} +exports.CodeGen = CodeGen; +function addNames(names, from) { + for (const n in from) + names[n] = (names[n] || 0) + (from[n] || 0); + return names; +} +function addExprNames(names, from) { + return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names; +} +function optimizeExpr(expr, names, constants) { + if (expr instanceof code_1.Name) + return replaceName(expr); + if (!canOptimize(expr)) + return expr; + return new code_1._Code(expr._items.reduce((items, c) => { + if (c instanceof code_1.Name) + c = replaceName(c); + if (c instanceof code_1._Code) + items.push(...c._items); + else + items.push(c); + return items; + }, [])); + function replaceName(n) { + const c = constants[n.str]; + if (c === undefined || names[n.str] !== 1) + return n; + delete names[n.str]; + return c; + } + function canOptimize(e) { + return (e instanceof code_1._Code && + e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants[c.str] !== undefined)); + } +} +function subtractNames(names, from) { + for (const n in from) + names[n] = (names[n] || 0) - (from[n] || 0); +} +function not(x) { + return typeof x == "boolean" || typeof x == "number" || x === null ? !x : (0, code_1._) `!${par(x)}`; +} +exports.not = not; +const andCode = mappend(exports.operators.AND); +// boolean AND (&&) expression with the passed arguments +function and(...args) { + return args.reduce(andCode); +} +exports.and = and; +const orCode = mappend(exports.operators.OR); +// boolean OR (||) expression with the passed arguments +function or(...args) { + return args.reduce(orCode); +} +exports.or = or; +function mappend(op) { + return (x, y) => (x === code_1.nil ? y : y === code_1.nil ? x : (0, code_1._) `${par(x)} ${op} ${par(y)}`); +} +function par(x) { + return x instanceof code_1.Name ? x : (0, code_1._) `(${x})`; +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/codegen/index.js.map b/website/node_modules/ajv/dist/compile/codegen/index.js.map new file mode 100644 index 00000000..f3826d28 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/codegen/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/compile/codegen/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/codegen/scope.d.ts b/website/node_modules/ajv/dist/compile/codegen/scope.d.ts new file mode 100644 index 00000000..3d953053 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/codegen/scope.d.ts @@ -0,0 +1,79 @@ +import { Code, Name } from "./code"; +interface NameGroup { + prefix: string; + index: number; +} +export interface NameValue { + ref: ValueReference; + key?: unknown; + code?: Code; +} +export type ValueReference = unknown; +interface ScopeOptions { + prefixes?: Set; + parent?: Scope; +} +interface ValueScopeOptions extends ScopeOptions { + scope: ScopeStore; + es5?: boolean; + lines?: boolean; +} +export type ScopeStore = Record; +type ScopeValues = { + [Prefix in string]?: Map; +}; +export type ScopeValueSets = { + [Prefix in string]?: Set; +}; +export declare enum UsedValueState { + Started = 0, + Completed = 1 +} +export type UsedScopeValues = { + [Prefix in string]?: Map; +}; +export declare const varKinds: { + const: Name; + let: Name; + var: Name; +}; +export declare class Scope { + protected readonly _names: { + [Prefix in string]?: NameGroup; + }; + protected readonly _prefixes?: Set; + protected readonly _parent?: Scope; + constructor({ prefixes, parent }?: ScopeOptions); + toName(nameOrPrefix: Name | string): Name; + name(prefix: string): Name; + protected _newName(prefix: string): string; + private _nameGroup; +} +interface ScopePath { + property: string; + itemIndex: number; +} +export declare class ValueScopeName extends Name { + readonly prefix: string; + value?: NameValue; + scopePath?: Code; + constructor(prefix: string, nameStr: string); + setValue(value: NameValue, { property, itemIndex }: ScopePath): void; +} +interface VSOptions extends ValueScopeOptions { + _n: Code; +} +export declare class ValueScope extends Scope { + protected readonly _values: ScopeValues; + protected readonly _scope: ScopeStore; + readonly opts: VSOptions; + constructor(opts: ValueScopeOptions); + get(): ScopeStore; + name(prefix: string): ValueScopeName; + value(nameOrPrefix: ValueScopeName | string, value: NameValue): ValueScopeName; + getValue(prefix: string, keyOrRef: unknown): ValueScopeName | undefined; + scopeRefs(scopeName: Name, values?: ScopeValues | ScopeValueSets): Code; + scopeCode(values?: ScopeValues | ScopeValueSets, usedValues?: UsedScopeValues, getCode?: (n: ValueScopeName) => Code | undefined): Code; + private _reduceValues; +} +export {}; diff --git a/website/node_modules/ajv/dist/compile/codegen/scope.js b/website/node_modules/ajv/dist/compile/codegen/scope.js new file mode 100644 index 00000000..acc5f762 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/codegen/scope.js @@ -0,0 +1,143 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ValueScope = exports.ValueScopeName = exports.Scope = exports.varKinds = exports.UsedValueState = void 0; +const code_1 = require("./code"); +class ValueError extends Error { + constructor(name) { + super(`CodeGen: "code" for ${name} not defined`); + this.value = name.value; + } +} +var UsedValueState; +(function (UsedValueState) { + UsedValueState[UsedValueState["Started"] = 0] = "Started"; + UsedValueState[UsedValueState["Completed"] = 1] = "Completed"; +})(UsedValueState = exports.UsedValueState || (exports.UsedValueState = {})); +exports.varKinds = { + const: new code_1.Name("const"), + let: new code_1.Name("let"), + var: new code_1.Name("var"), +}; +class Scope { + constructor({ prefixes, parent } = {}) { + this._names = {}; + this._prefixes = prefixes; + this._parent = parent; + } + toName(nameOrPrefix) { + return nameOrPrefix instanceof code_1.Name ? nameOrPrefix : this.name(nameOrPrefix); + } + name(prefix) { + return new code_1.Name(this._newName(prefix)); + } + _newName(prefix) { + const ng = this._names[prefix] || this._nameGroup(prefix); + return `${prefix}${ng.index++}`; + } + _nameGroup(prefix) { + var _a, _b; + if (((_b = (_a = this._parent) === null || _a === void 0 ? void 0 : _a._prefixes) === null || _b === void 0 ? void 0 : _b.has(prefix)) || (this._prefixes && !this._prefixes.has(prefix))) { + throw new Error(`CodeGen: prefix "${prefix}" is not allowed in this scope`); + } + return (this._names[prefix] = { prefix, index: 0 }); + } +} +exports.Scope = Scope; +class ValueScopeName extends code_1.Name { + constructor(prefix, nameStr) { + super(nameStr); + this.prefix = prefix; + } + setValue(value, { property, itemIndex }) { + this.value = value; + this.scopePath = (0, code_1._) `.${new code_1.Name(property)}[${itemIndex}]`; + } +} +exports.ValueScopeName = ValueScopeName; +const line = (0, code_1._) `\n`; +class ValueScope extends Scope { + constructor(opts) { + super(opts); + this._values = {}; + this._scope = opts.scope; + this.opts = { ...opts, _n: opts.lines ? line : code_1.nil }; + } + get() { + return this._scope; + } + name(prefix) { + return new ValueScopeName(prefix, this._newName(prefix)); + } + value(nameOrPrefix, value) { + var _a; + if (value.ref === undefined) + throw new Error("CodeGen: ref must be passed in value"); + const name = this.toName(nameOrPrefix); + const { prefix } = name; + const valueKey = (_a = value.key) !== null && _a !== void 0 ? _a : value.ref; + let vs = this._values[prefix]; + if (vs) { + const _name = vs.get(valueKey); + if (_name) + return _name; + } + else { + vs = this._values[prefix] = new Map(); + } + vs.set(valueKey, name); + const s = this._scope[prefix] || (this._scope[prefix] = []); + const itemIndex = s.length; + s[itemIndex] = value.ref; + name.setValue(value, { property: prefix, itemIndex }); + return name; + } + getValue(prefix, keyOrRef) { + const vs = this._values[prefix]; + if (!vs) + return; + return vs.get(keyOrRef); + } + scopeRefs(scopeName, values = this._values) { + return this._reduceValues(values, (name) => { + if (name.scopePath === undefined) + throw new Error(`CodeGen: name "${name}" has no value`); + return (0, code_1._) `${scopeName}${name.scopePath}`; + }); + } + scopeCode(values = this._values, usedValues, getCode) { + return this._reduceValues(values, (name) => { + if (name.value === undefined) + throw new Error(`CodeGen: name "${name}" has no value`); + return name.value.code; + }, usedValues, getCode); + } + _reduceValues(values, valueCode, usedValues = {}, getCode) { + let code = code_1.nil; + for (const prefix in values) { + const vs = values[prefix]; + if (!vs) + continue; + const nameSet = (usedValues[prefix] = usedValues[prefix] || new Map()); + vs.forEach((name) => { + if (nameSet.has(name)) + return; + nameSet.set(name, UsedValueState.Started); + let c = valueCode(name); + if (c) { + const def = this.opts.es5 ? exports.varKinds.var : exports.varKinds.const; + code = (0, code_1._) `${code}${def} ${name} = ${c};${this.opts._n}`; + } + else if ((c = getCode === null || getCode === void 0 ? void 0 : getCode(name))) { + code = (0, code_1._) `${code}${c}${this.opts._n}`; + } + else { + throw new ValueError(name); + } + nameSet.set(name, UsedValueState.Completed); + }); + } + return code; + } +} +exports.ValueScope = ValueScope; +//# sourceMappingURL=scope.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/codegen/scope.js.map b/website/node_modules/ajv/dist/compile/codegen/scope.js.map new file mode 100644 index 00000000..7afde106 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/codegen/scope.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../../lib/compile/codegen/scope.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/errors.d.ts b/website/node_modules/ajv/dist/compile/errors.d.ts new file mode 100644 index 00000000..74eef7e2 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/errors.d.ts @@ -0,0 +1,13 @@ +import type { KeywordErrorCxt, KeywordErrorDefinition } from "../types"; +import { CodeGen, Code, Name } from "./codegen"; +export declare const keywordError: KeywordErrorDefinition; +export declare const keyword$DataError: KeywordErrorDefinition; +export interface ErrorPaths { + instancePath?: Code; + schemaPath?: string; + parentSchema?: boolean; +} +export declare function reportError(cxt: KeywordErrorCxt, error?: KeywordErrorDefinition, errorPaths?: ErrorPaths, overrideAllErrors?: boolean): void; +export declare function reportExtraError(cxt: KeywordErrorCxt, error?: KeywordErrorDefinition, errorPaths?: ErrorPaths): void; +export declare function resetErrorsCount(gen: CodeGen, errsCount: Name): void; +export declare function extendErrors({ gen, keyword, schemaValue, data, errsCount, it, }: KeywordErrorCxt): void; diff --git a/website/node_modules/ajv/dist/compile/errors.js b/website/node_modules/ajv/dist/compile/errors.js new file mode 100644 index 00000000..e2aca7b9 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/errors.js @@ -0,0 +1,123 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.extendErrors = exports.resetErrorsCount = exports.reportExtraError = exports.reportError = exports.keyword$DataError = exports.keywordError = void 0; +const codegen_1 = require("./codegen"); +const util_1 = require("./util"); +const names_1 = require("./names"); +exports.keywordError = { + message: ({ keyword }) => (0, codegen_1.str) `must pass "${keyword}" keyword validation`, +}; +exports.keyword$DataError = { + message: ({ keyword, schemaType }) => schemaType + ? (0, codegen_1.str) `"${keyword}" keyword must be ${schemaType} ($data)` + : (0, codegen_1.str) `"${keyword}" keyword is invalid ($data)`, +}; +function reportError(cxt, error = exports.keywordError, errorPaths, overrideAllErrors) { + const { it } = cxt; + const { gen, compositeRule, allErrors } = it; + const errObj = errorObjectCode(cxt, error, errorPaths); + if (overrideAllErrors !== null && overrideAllErrors !== void 0 ? overrideAllErrors : (compositeRule || allErrors)) { + addError(gen, errObj); + } + else { + returnErrors(it, (0, codegen_1._) `[${errObj}]`); + } +} +exports.reportError = reportError; +function reportExtraError(cxt, error = exports.keywordError, errorPaths) { + const { it } = cxt; + const { gen, compositeRule, allErrors } = it; + const errObj = errorObjectCode(cxt, error, errorPaths); + addError(gen, errObj); + if (!(compositeRule || allErrors)) { + returnErrors(it, names_1.default.vErrors); + } +} +exports.reportExtraError = reportExtraError; +function resetErrorsCount(gen, errsCount) { + gen.assign(names_1.default.errors, errsCount); + gen.if((0, codegen_1._) `${names_1.default.vErrors} !== null`, () => gen.if(errsCount, () => gen.assign((0, codegen_1._) `${names_1.default.vErrors}.length`, errsCount), () => gen.assign(names_1.default.vErrors, null))); +} +exports.resetErrorsCount = resetErrorsCount; +function extendErrors({ gen, keyword, schemaValue, data, errsCount, it, }) { + /* istanbul ignore if */ + if (errsCount === undefined) + throw new Error("ajv implementation error"); + const err = gen.name("err"); + gen.forRange("i", errsCount, names_1.default.errors, (i) => { + gen.const(err, (0, codegen_1._) `${names_1.default.vErrors}[${i}]`); + gen.if((0, codegen_1._) `${err}.instancePath === undefined`, () => gen.assign((0, codegen_1._) `${err}.instancePath`, (0, codegen_1.strConcat)(names_1.default.instancePath, it.errorPath))); + gen.assign((0, codegen_1._) `${err}.schemaPath`, (0, codegen_1.str) `${it.errSchemaPath}/${keyword}`); + if (it.opts.verbose) { + gen.assign((0, codegen_1._) `${err}.schema`, schemaValue); + gen.assign((0, codegen_1._) `${err}.data`, data); + } + }); +} +exports.extendErrors = extendErrors; +function addError(gen, errObj) { + const err = gen.const("err", errObj); + gen.if((0, codegen_1._) `${names_1.default.vErrors} === null`, () => gen.assign(names_1.default.vErrors, (0, codegen_1._) `[${err}]`), (0, codegen_1._) `${names_1.default.vErrors}.push(${err})`); + gen.code((0, codegen_1._) `${names_1.default.errors}++`); +} +function returnErrors(it, errs) { + const { gen, validateName, schemaEnv } = it; + if (schemaEnv.$async) { + gen.throw((0, codegen_1._) `new ${it.ValidationError}(${errs})`); + } + else { + gen.assign((0, codegen_1._) `${validateName}.errors`, errs); + gen.return(false); + } +} +const E = { + keyword: new codegen_1.Name("keyword"), + schemaPath: new codegen_1.Name("schemaPath"), + params: new codegen_1.Name("params"), + propertyName: new codegen_1.Name("propertyName"), + message: new codegen_1.Name("message"), + schema: new codegen_1.Name("schema"), + parentSchema: new codegen_1.Name("parentSchema"), +}; +function errorObjectCode(cxt, error, errorPaths) { + const { createErrors } = cxt.it; + if (createErrors === false) + return (0, codegen_1._) `{}`; + return errorObject(cxt, error, errorPaths); +} +function errorObject(cxt, error, errorPaths = {}) { + const { gen, it } = cxt; + const keyValues = [ + errorInstancePath(it, errorPaths), + errorSchemaPath(cxt, errorPaths), + ]; + extraErrorProps(cxt, error, keyValues); + return gen.object(...keyValues); +} +function errorInstancePath({ errorPath }, { instancePath }) { + const instPath = instancePath + ? (0, codegen_1.str) `${errorPath}${(0, util_1.getErrorPath)(instancePath, util_1.Type.Str)}` + : errorPath; + return [names_1.default.instancePath, (0, codegen_1.strConcat)(names_1.default.instancePath, instPath)]; +} +function errorSchemaPath({ keyword, it: { errSchemaPath } }, { schemaPath, parentSchema }) { + let schPath = parentSchema ? errSchemaPath : (0, codegen_1.str) `${errSchemaPath}/${keyword}`; + if (schemaPath) { + schPath = (0, codegen_1.str) `${schPath}${(0, util_1.getErrorPath)(schemaPath, util_1.Type.Str)}`; + } + return [E.schemaPath, schPath]; +} +function extraErrorProps(cxt, { params, message }, keyValues) { + const { keyword, data, schemaValue, it } = cxt; + const { opts, propertyName, topSchemaRef, schemaPath } = it; + keyValues.push([E.keyword, keyword], [E.params, typeof params == "function" ? params(cxt) : params || (0, codegen_1._) `{}`]); + if (opts.messages) { + keyValues.push([E.message, typeof message == "function" ? message(cxt) : message]); + } + if (opts.verbose) { + keyValues.push([E.schema, schemaValue], [E.parentSchema, (0, codegen_1._) `${topSchemaRef}${schemaPath}`], [names_1.default.data, data]); + } + if (propertyName) + keyValues.push([E.propertyName, propertyName]); +} +//# sourceMappingURL=errors.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/errors.js.map b/website/node_modules/ajv/dist/compile/errors.js.map new file mode 100644 index 00000000..66208910 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/errors.js.map @@ -0,0 +1 @@ +{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../lib/compile/errors.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/index.d.ts b/website/node_modules/ajv/dist/compile/index.d.ts new file mode 100644 index 00000000..2809353d --- /dev/null +++ b/website/node_modules/ajv/dist/compile/index.d.ts @@ -0,0 +1,80 @@ +import type { AnySchema, AnySchemaObject, AnyValidateFunction, EvaluatedProperties, EvaluatedItems } from "../types"; +import type Ajv from "../core"; +import type { InstanceOptions } from "../core"; +import { CodeGen, Name, Code, ValueScopeName } from "./codegen"; +import { LocalRefs } from "./resolve"; +import { JSONType } from "./rules"; +export type SchemaRefs = { + [Ref in string]?: SchemaEnv | AnySchema; +}; +export interface SchemaCxt { + readonly gen: CodeGen; + readonly allErrors?: boolean; + readonly data: Name; + readonly parentData: Name; + readonly parentDataProperty: Code | number; + readonly dataNames: Name[]; + readonly dataPathArr: (Code | number)[]; + readonly dataLevel: number; + dataTypes: JSONType[]; + definedProperties: Set; + readonly topSchemaRef: Code; + readonly validateName: Name; + evaluated?: Name; + readonly ValidationError?: Name; + readonly schema: AnySchema; + readonly schemaEnv: SchemaEnv; + readonly rootId: string; + baseId: string; + readonly schemaPath: Code; + readonly errSchemaPath: string; + readonly errorPath: Code; + readonly propertyName?: Name; + readonly compositeRule?: boolean; + props?: EvaluatedProperties | Name; + items?: EvaluatedItems | Name; + jtdDiscriminator?: string; + jtdMetadata?: boolean; + readonly createErrors?: boolean; + readonly opts: InstanceOptions; + readonly self: Ajv; +} +export interface SchemaObjCxt extends SchemaCxt { + readonly schema: AnySchemaObject; +} +interface SchemaEnvArgs { + readonly schema: AnySchema; + readonly schemaId?: "$id" | "id"; + readonly root?: SchemaEnv; + readonly baseId?: string; + readonly schemaPath?: string; + readonly localRefs?: LocalRefs; + readonly meta?: boolean; +} +export declare class SchemaEnv implements SchemaEnvArgs { + readonly schema: AnySchema; + readonly schemaId?: "$id" | "id"; + readonly root: SchemaEnv; + baseId: string; + schemaPath?: string; + localRefs?: LocalRefs; + readonly meta?: boolean; + readonly $async?: boolean; + readonly refs: SchemaRefs; + readonly dynamicAnchors: { + [Ref in string]?: true; + }; + validate?: AnyValidateFunction; + validateName?: ValueScopeName; + serialize?: (data: unknown) => string; + serializeName?: ValueScopeName; + parse?: (data: string) => unknown; + parseName?: ValueScopeName; + constructor(env: SchemaEnvArgs); +} +export declare function compileSchema(this: Ajv, sch: SchemaEnv): SchemaEnv; +export declare function resolveRef(this: Ajv, root: SchemaEnv, baseId: string, ref: string): AnySchema | SchemaEnv | undefined; +export declare function getCompilingSchema(this: Ajv, schEnv: SchemaEnv): SchemaEnv | void; +export declare function resolveSchema(this: Ajv, root: SchemaEnv, // root object with properties schema, refs TODO below SchemaEnv is assigned to it +ref: string): SchemaEnv | undefined; +export {}; diff --git a/website/node_modules/ajv/dist/compile/index.js b/website/node_modules/ajv/dist/compile/index.js new file mode 100644 index 00000000..b125ecd0 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/index.js @@ -0,0 +1,242 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.resolveSchema = exports.getCompilingSchema = exports.resolveRef = exports.compileSchema = exports.SchemaEnv = void 0; +const codegen_1 = require("./codegen"); +const validation_error_1 = require("../runtime/validation_error"); +const names_1 = require("./names"); +const resolve_1 = require("./resolve"); +const util_1 = require("./util"); +const validate_1 = require("./validate"); +class SchemaEnv { + constructor(env) { + var _a; + this.refs = {}; + this.dynamicAnchors = {}; + let schema; + if (typeof env.schema == "object") + schema = env.schema; + this.schema = env.schema; + this.schemaId = env.schemaId; + this.root = env.root || this; + this.baseId = (_a = env.baseId) !== null && _a !== void 0 ? _a : (0, resolve_1.normalizeId)(schema === null || schema === void 0 ? void 0 : schema[env.schemaId || "$id"]); + this.schemaPath = env.schemaPath; + this.localRefs = env.localRefs; + this.meta = env.meta; + this.$async = schema === null || schema === void 0 ? void 0 : schema.$async; + this.refs = {}; + } +} +exports.SchemaEnv = SchemaEnv; +// let codeSize = 0 +// let nodeCount = 0 +// Compiles schema in SchemaEnv +function compileSchema(sch) { + // TODO refactor - remove compilations + const _sch = getCompilingSchema.call(this, sch); + if (_sch) + return _sch; + const rootId = (0, resolve_1.getFullPath)(this.opts.uriResolver, sch.root.baseId); // TODO if getFullPath removed 1 tests fails + const { es5, lines } = this.opts.code; + const { ownProperties } = this.opts; + const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties }); + let _ValidationError; + if (sch.$async) { + _ValidationError = gen.scopeValue("Error", { + ref: validation_error_1.default, + code: (0, codegen_1._) `require("ajv/dist/runtime/validation_error").default`, + }); + } + const validateName = gen.scopeName("validate"); + sch.validateName = validateName; + const schemaCxt = { + gen, + allErrors: this.opts.allErrors, + data: names_1.default.data, + parentData: names_1.default.parentData, + parentDataProperty: names_1.default.parentDataProperty, + dataNames: [names_1.default.data], + dataPathArr: [codegen_1.nil], + dataLevel: 0, + dataTypes: [], + definedProperties: new Set(), + topSchemaRef: gen.scopeValue("schema", this.opts.code.source === true + ? { ref: sch.schema, code: (0, codegen_1.stringify)(sch.schema) } + : { ref: sch.schema }), + validateName, + ValidationError: _ValidationError, + schema: sch.schema, + schemaEnv: sch, + rootId, + baseId: sch.baseId || rootId, + schemaPath: codegen_1.nil, + errSchemaPath: sch.schemaPath || (this.opts.jtd ? "" : "#"), + errorPath: (0, codegen_1._) `""`, + opts: this.opts, + self: this, + }; + let sourceCode; + try { + this._compilations.add(sch); + (0, validate_1.validateFunctionCode)(schemaCxt); + gen.optimize(this.opts.code.optimize); + // gen.optimize(1) + const validateCode = gen.toString(); + sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${validateCode}`; + // console.log((codeSize += sourceCode.length), (nodeCount += gen.nodeCount)) + if (this.opts.code.process) + sourceCode = this.opts.code.process(sourceCode, sch); + // console.log("\n\n\n *** \n", sourceCode) + const makeValidate = new Function(`${names_1.default.self}`, `${names_1.default.scope}`, sourceCode); + const validate = makeValidate(this, this.scope.get()); + this.scope.value(validateName, { ref: validate }); + validate.errors = null; + validate.schema = sch.schema; + validate.schemaEnv = sch; + if (sch.$async) + validate.$async = true; + if (this.opts.code.source === true) { + validate.source = { validateName, validateCode, scopeValues: gen._values }; + } + if (this.opts.unevaluated) { + const { props, items } = schemaCxt; + validate.evaluated = { + props: props instanceof codegen_1.Name ? undefined : props, + items: items instanceof codegen_1.Name ? undefined : items, + dynamicProps: props instanceof codegen_1.Name, + dynamicItems: items instanceof codegen_1.Name, + }; + if (validate.source) + validate.source.evaluated = (0, codegen_1.stringify)(validate.evaluated); + } + sch.validate = validate; + return sch; + } + catch (e) { + delete sch.validate; + delete sch.validateName; + if (sourceCode) + this.logger.error("Error compiling schema, function code:", sourceCode); + // console.log("\n\n\n *** \n", sourceCode, this.opts) + throw e; + } + finally { + this._compilations.delete(sch); + } +} +exports.compileSchema = compileSchema; +function resolveRef(root, baseId, ref) { + var _a; + ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref); + const schOrFunc = root.refs[ref]; + if (schOrFunc) + return schOrFunc; + let _sch = resolve.call(this, root, ref); + if (_sch === undefined) { + const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref]; // TODO maybe localRefs should hold SchemaEnv + const { schemaId } = this.opts; + if (schema) + _sch = new SchemaEnv({ schema, schemaId, root, baseId }); + } + if (_sch === undefined) + return; + return (root.refs[ref] = inlineOrCompile.call(this, _sch)); +} +exports.resolveRef = resolveRef; +function inlineOrCompile(sch) { + if ((0, resolve_1.inlineRef)(sch.schema, this.opts.inlineRefs)) + return sch.schema; + return sch.validate ? sch : compileSchema.call(this, sch); +} +// Index of schema compilation in the currently compiled list +function getCompilingSchema(schEnv) { + for (const sch of this._compilations) { + if (sameSchemaEnv(sch, schEnv)) + return sch; + } +} +exports.getCompilingSchema = getCompilingSchema; +function sameSchemaEnv(s1, s2) { + return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId; +} +// resolve and compile the references ($ref) +// TODO returns AnySchemaObject (if the schema can be inlined) or validation function +function resolve(root, // information about the root schema for the current schema +ref // reference to resolve +) { + let sch; + while (typeof (sch = this.refs[ref]) == "string") + ref = sch; + return sch || this.schemas[ref] || resolveSchema.call(this, root, ref); +} +// Resolve schema, its root and baseId +function resolveSchema(root, // root object with properties schema, refs TODO below SchemaEnv is assigned to it +ref // reference to resolve +) { + const p = this.opts.uriResolver.parse(ref); + const refPath = (0, resolve_1._getFullPath)(this.opts.uriResolver, p); + let baseId = (0, resolve_1.getFullPath)(this.opts.uriResolver, root.baseId, undefined); + // TODO `Object.keys(root.schema).length > 0` should not be needed - but removing breaks 2 tests + if (Object.keys(root.schema).length > 0 && refPath === baseId) { + return getJsonPointer.call(this, p, root); + } + const id = (0, resolve_1.normalizeId)(refPath); + const schOrRef = this.refs[id] || this.schemas[id]; + if (typeof schOrRef == "string") { + const sch = resolveSchema.call(this, root, schOrRef); + if (typeof (sch === null || sch === void 0 ? void 0 : sch.schema) !== "object") + return; + return getJsonPointer.call(this, p, sch); + } + if (typeof (schOrRef === null || schOrRef === void 0 ? void 0 : schOrRef.schema) !== "object") + return; + if (!schOrRef.validate) + compileSchema.call(this, schOrRef); + if (id === (0, resolve_1.normalizeId)(ref)) { + const { schema } = schOrRef; + const { schemaId } = this.opts; + const schId = schema[schemaId]; + if (schId) + baseId = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schId); + return new SchemaEnv({ schema, schemaId, root, baseId }); + } + return getJsonPointer.call(this, p, schOrRef); +} +exports.resolveSchema = resolveSchema; +const PREVENT_SCOPE_CHANGE = new Set([ + "properties", + "patternProperties", + "enum", + "dependencies", + "definitions", +]); +function getJsonPointer(parsedRef, { baseId, schema, root }) { + var _a; + if (((_a = parsedRef.fragment) === null || _a === void 0 ? void 0 : _a[0]) !== "/") + return; + for (const part of parsedRef.fragment.slice(1).split("/")) { + if (typeof schema === "boolean") + return; + const partSchema = schema[(0, util_1.unescapeFragment)(part)]; + if (partSchema === undefined) + return; + schema = partSchema; + // TODO PREVENT_SCOPE_CHANGE could be defined in keyword def? + const schId = typeof schema === "object" && schema[this.opts.schemaId]; + if (!PREVENT_SCOPE_CHANGE.has(part) && schId) { + baseId = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schId); + } + } + let env; + if (typeof schema != "boolean" && schema.$ref && !(0, util_1.schemaHasRulesButRef)(schema, this.RULES)) { + const $ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schema.$ref); + env = resolveSchema.call(this, root, $ref); + } + // even though resolution failed we need to return SchemaEnv to throw exception + // so that compileAsync loads missing schema. + const { schemaId } = this.opts; + env = env || new SchemaEnv({ schema, schemaId, root, baseId }); + if (env.schema !== env.root.schema) + return env; + return undefined; +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/index.js.map b/website/node_modules/ajv/dist/compile/index.js.map new file mode 100644 index 00000000..3fd890e6 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/compile/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/jtd/parse.d.ts b/website/node_modules/ajv/dist/compile/jtd/parse.d.ts new file mode 100644 index 00000000..618c64ae --- /dev/null +++ b/website/node_modules/ajv/dist/compile/jtd/parse.d.ts @@ -0,0 +1,4 @@ +import type Ajv from "../../core"; +import { SchemaObjectMap } from "./types"; +import { SchemaEnv } from ".."; +export default function compileParser(this: Ajv, sch: SchemaEnv, definitions: SchemaObjectMap): SchemaEnv; diff --git a/website/node_modules/ajv/dist/compile/jtd/parse.js b/website/node_modules/ajv/dist/compile/jtd/parse.js new file mode 100644 index 00000000..8fc94fd0 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/jtd/parse.js @@ -0,0 +1,350 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const types_1 = require("./types"); +const __1 = require(".."); +const codegen_1 = require("../codegen"); +const ref_error_1 = require("../ref_error"); +const names_1 = require("../names"); +const code_1 = require("../../vocabularies/code"); +const ref_1 = require("../../vocabularies/jtd/ref"); +const type_1 = require("../../vocabularies/jtd/type"); +const parseJson_1 = require("../../runtime/parseJson"); +const util_1 = require("../util"); +const timestamp_1 = require("../../runtime/timestamp"); +const genParse = { + elements: parseElements, + values: parseValues, + discriminator: parseDiscriminator, + properties: parseProperties, + optionalProperties: parseProperties, + enum: parseEnum, + type: parseType, + ref: parseRef, +}; +function compileParser(sch, definitions) { + const _sch = __1.getCompilingSchema.call(this, sch); + if (_sch) + return _sch; + const { es5, lines } = this.opts.code; + const { ownProperties } = this.opts; + const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties }); + const parseName = gen.scopeName("parse"); + const cxt = { + self: this, + gen, + schema: sch.schema, + schemaEnv: sch, + definitions, + data: names_1.default.data, + parseName, + char: gen.name("c"), + }; + let sourceCode; + try { + this._compilations.add(sch); + sch.parseName = parseName; + parserFunction(cxt); + gen.optimize(this.opts.code.optimize); + const parseFuncCode = gen.toString(); + sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${parseFuncCode}`; + const makeParse = new Function(`${names_1.default.scope}`, sourceCode); + const parse = makeParse(this.scope.get()); + this.scope.value(parseName, { ref: parse }); + sch.parse = parse; + } + catch (e) { + if (sourceCode) + this.logger.error("Error compiling parser, function code:", sourceCode); + delete sch.parse; + delete sch.parseName; + throw e; + } + finally { + this._compilations.delete(sch); + } + return sch; +} +exports.default = compileParser; +const undef = (0, codegen_1._) `undefined`; +function parserFunction(cxt) { + const { gen, parseName, char } = cxt; + gen.func(parseName, (0, codegen_1._) `${names_1.default.json}, ${names_1.default.jsonPos}, ${names_1.default.jsonPart}`, false, () => { + gen.let(names_1.default.data); + gen.let(char); + gen.assign((0, codegen_1._) `${parseName}.message`, undef); + gen.assign((0, codegen_1._) `${parseName}.position`, undef); + gen.assign(names_1.default.jsonPos, (0, codegen_1._) `${names_1.default.jsonPos} || 0`); + gen.const(names_1.default.jsonLen, (0, codegen_1._) `${names_1.default.json}.length`); + parseCode(cxt); + skipWhitespace(cxt); + gen.if(names_1.default.jsonPart, () => { + gen.assign((0, codegen_1._) `${parseName}.position`, names_1.default.jsonPos); + gen.return(names_1.default.data); + }); + gen.if((0, codegen_1._) `${names_1.default.jsonPos} === ${names_1.default.jsonLen}`, () => gen.return(names_1.default.data)); + jsonSyntaxError(cxt); + }); +} +function parseCode(cxt) { + let form; + for (const key of types_1.jtdForms) { + if (key in cxt.schema) { + form = key; + break; + } + } + if (form) + parseNullable(cxt, genParse[form]); + else + parseEmpty(cxt); +} +const parseBoolean = parseBooleanToken(true, parseBooleanToken(false, jsonSyntaxError)); +function parseNullable(cxt, parseForm) { + const { gen, schema, data } = cxt; + if (!schema.nullable) + return parseForm(cxt); + tryParseToken(cxt, "null", parseForm, () => gen.assign(data, null)); +} +function parseElements(cxt) { + const { gen, schema, data } = cxt; + parseToken(cxt, "["); + const ix = gen.let("i", 0); + gen.assign(data, (0, codegen_1._) `[]`); + parseItems(cxt, "]", () => { + const el = gen.let("el"); + parseCode({ ...cxt, schema: schema.elements, data: el }); + gen.assign((0, codegen_1._) `${data}[${ix}++]`, el); + }); +} +function parseValues(cxt) { + const { gen, schema, data } = cxt; + parseToken(cxt, "{"); + gen.assign(data, (0, codegen_1._) `{}`); + parseItems(cxt, "}", () => parseKeyValue(cxt, schema.values)); +} +function parseItems(cxt, endToken, block) { + tryParseItems(cxt, endToken, block); + parseToken(cxt, endToken); +} +function tryParseItems(cxt, endToken, block) { + const { gen } = cxt; + gen.for((0, codegen_1._) `;${names_1.default.jsonPos}<${names_1.default.jsonLen} && ${jsonSlice(1)}!==${endToken};`, () => { + block(); + tryParseToken(cxt, ",", () => gen.break(), hasItem); + }); + function hasItem() { + tryParseToken(cxt, endToken, () => { }, jsonSyntaxError); + } +} +function parseKeyValue(cxt, schema) { + const { gen } = cxt; + const key = gen.let("key"); + parseString({ ...cxt, data: key }); + parseToken(cxt, ":"); + parsePropertyValue(cxt, key, schema); +} +function parseDiscriminator(cxt) { + const { gen, data, schema } = cxt; + const { discriminator, mapping } = schema; + parseToken(cxt, "{"); + gen.assign(data, (0, codegen_1._) `{}`); + const startPos = gen.const("pos", names_1.default.jsonPos); + const value = gen.let("value"); + const tag = gen.let("tag"); + tryParseItems(cxt, "}", () => { + const key = gen.let("key"); + parseString({ ...cxt, data: key }); + parseToken(cxt, ":"); + gen.if((0, codegen_1._) `${key} === ${discriminator}`, () => { + parseString({ ...cxt, data: tag }); + gen.assign((0, codegen_1._) `${data}[${key}]`, tag); + gen.break(); + }, () => parseEmpty({ ...cxt, data: value }) // can be discarded/skipped + ); + }); + gen.assign(names_1.default.jsonPos, startPos); + gen.if((0, codegen_1._) `${tag} === undefined`); + parsingError(cxt, (0, codegen_1.str) `discriminator tag not found`); + for (const tagValue in mapping) { + gen.elseIf((0, codegen_1._) `${tag} === ${tagValue}`); + parseSchemaProperties({ ...cxt, schema: mapping[tagValue] }, discriminator); + } + gen.else(); + parsingError(cxt, (0, codegen_1.str) `discriminator value not in schema`); + gen.endIf(); +} +function parseProperties(cxt) { + const { gen, data } = cxt; + parseToken(cxt, "{"); + gen.assign(data, (0, codegen_1._) `{}`); + parseSchemaProperties(cxt); +} +function parseSchemaProperties(cxt, discriminator) { + const { gen, schema, data } = cxt; + const { properties, optionalProperties, additionalProperties } = schema; + parseItems(cxt, "}", () => { + const key = gen.let("key"); + parseString({ ...cxt, data: key }); + parseToken(cxt, ":"); + gen.if(false); + parseDefinedProperty(cxt, key, properties); + parseDefinedProperty(cxt, key, optionalProperties); + if (discriminator) { + gen.elseIf((0, codegen_1._) `${key} === ${discriminator}`); + const tag = gen.let("tag"); + parseString({ ...cxt, data: tag }); // can be discarded, it is already assigned + } + gen.else(); + if (additionalProperties) { + parseEmpty({ ...cxt, data: (0, codegen_1._) `${data}[${key}]` }); + } + else { + parsingError(cxt, (0, codegen_1.str) `property ${key} not allowed`); + } + gen.endIf(); + }); + if (properties) { + const hasProp = (0, code_1.hasPropFunc)(gen); + const allProps = (0, codegen_1.and)(...Object.keys(properties).map((p) => (0, codegen_1._) `${hasProp}.call(${data}, ${p})`)); + gen.if((0, codegen_1.not)(allProps), () => parsingError(cxt, (0, codegen_1.str) `missing required properties`)); + } +} +function parseDefinedProperty(cxt, key, schemas = {}) { + const { gen } = cxt; + for (const prop in schemas) { + gen.elseIf((0, codegen_1._) `${key} === ${prop}`); + parsePropertyValue(cxt, key, schemas[prop]); + } +} +function parsePropertyValue(cxt, key, schema) { + parseCode({ ...cxt, schema, data: (0, codegen_1._) `${cxt.data}[${key}]` }); +} +function parseType(cxt) { + const { gen, schema, data, self } = cxt; + switch (schema.type) { + case "boolean": + parseBoolean(cxt); + break; + case "string": + parseString(cxt); + break; + case "timestamp": { + parseString(cxt); + const vts = (0, util_1.useFunc)(gen, timestamp_1.default); + const { allowDate, parseDate } = self.opts; + const notValid = allowDate ? (0, codegen_1._) `!${vts}(${data}, true)` : (0, codegen_1._) `!${vts}(${data})`; + const fail = parseDate + ? (0, codegen_1.or)(notValid, (0, codegen_1._) `(${data} = new Date(${data}), false)`, (0, codegen_1._) `isNaN(${data}.valueOf())`) + : notValid; + gen.if(fail, () => parsingError(cxt, (0, codegen_1.str) `invalid timestamp`)); + break; + } + case "float32": + case "float64": + parseNumber(cxt); + break; + default: { + const t = schema.type; + if (!self.opts.int32range && (t === "int32" || t === "uint32")) { + parseNumber(cxt, 16); // 2 ** 53 - max safe integer + if (t === "uint32") { + gen.if((0, codegen_1._) `${data} < 0`, () => parsingError(cxt, (0, codegen_1.str) `integer out of range`)); + } + } + else { + const [min, max, maxDigits] = type_1.intRange[t]; + parseNumber(cxt, maxDigits); + gen.if((0, codegen_1._) `${data} < ${min} || ${data} > ${max}`, () => parsingError(cxt, (0, codegen_1.str) `integer out of range`)); + } + } + } +} +function parseString(cxt) { + parseToken(cxt, '"'); + parseWith(cxt, parseJson_1.parseJsonString); +} +function parseEnum(cxt) { + const { gen, data, schema } = cxt; + const enumSch = schema.enum; + parseToken(cxt, '"'); + // TODO loopEnum + gen.if(false); + for (const value of enumSch) { + const valueStr = JSON.stringify(value).slice(1); // remove starting quote + gen.elseIf((0, codegen_1._) `${jsonSlice(valueStr.length)} === ${valueStr}`); + gen.assign(data, (0, codegen_1.str) `${value}`); + gen.add(names_1.default.jsonPos, valueStr.length); + } + gen.else(); + jsonSyntaxError(cxt); + gen.endIf(); +} +function parseNumber(cxt, maxDigits) { + const { gen } = cxt; + skipWhitespace(cxt); + gen.if((0, codegen_1._) `"-0123456789".indexOf(${jsonSlice(1)}) < 0`, () => jsonSyntaxError(cxt), () => parseWith(cxt, parseJson_1.parseJsonNumber, maxDigits)); +} +function parseBooleanToken(bool, fail) { + return (cxt) => { + const { gen, data } = cxt; + tryParseToken(cxt, `${bool}`, () => fail(cxt), () => gen.assign(data, bool)); + }; +} +function parseRef(cxt) { + const { gen, self, definitions, schema, schemaEnv } = cxt; + const { ref } = schema; + const refSchema = definitions[ref]; + if (!refSchema) + throw new ref_error_1.default(self.opts.uriResolver, "", ref, `No definition ${ref}`); + if (!(0, ref_1.hasRef)(refSchema)) + return parseCode({ ...cxt, schema: refSchema }); + const { root } = schemaEnv; + const sch = compileParser.call(self, new __1.SchemaEnv({ schema: refSchema, root }), definitions); + partialParse(cxt, getParser(gen, sch), true); +} +function getParser(gen, sch) { + return sch.parse + ? gen.scopeValue("parse", { ref: sch.parse }) + : (0, codegen_1._) `${gen.scopeValue("wrapper", { ref: sch })}.parse`; +} +function parseEmpty(cxt) { + parseWith(cxt, parseJson_1.parseJson); +} +function parseWith(cxt, parseFunc, args) { + partialParse(cxt, (0, util_1.useFunc)(cxt.gen, parseFunc), args); +} +function partialParse(cxt, parseFunc, args) { + const { gen, data } = cxt; + gen.assign(data, (0, codegen_1._) `${parseFunc}(${names_1.default.json}, ${names_1.default.jsonPos}${args ? (0, codegen_1._) `, ${args}` : codegen_1.nil})`); + gen.assign(names_1.default.jsonPos, (0, codegen_1._) `${parseFunc}.position`); + gen.if((0, codegen_1._) `${data} === undefined`, () => parsingError(cxt, (0, codegen_1._) `${parseFunc}.message`)); +} +function parseToken(cxt, tok) { + tryParseToken(cxt, tok, jsonSyntaxError); +} +function tryParseToken(cxt, tok, fail, success) { + const { gen } = cxt; + const n = tok.length; + skipWhitespace(cxt); + gen.if((0, codegen_1._) `${jsonSlice(n)} === ${tok}`, () => { + gen.add(names_1.default.jsonPos, n); + success === null || success === void 0 ? void 0 : success(cxt); + }, () => fail(cxt)); +} +function skipWhitespace({ gen, char: c }) { + gen.code((0, codegen_1._) `while((${c}=${names_1.default.json}[${names_1.default.jsonPos}],${c}===" "||${c}==="\\n"||${c}==="\\r"||${c}==="\\t"))${names_1.default.jsonPos}++;`); +} +function jsonSlice(len) { + return len === 1 + ? (0, codegen_1._) `${names_1.default.json}[${names_1.default.jsonPos}]` + : (0, codegen_1._) `${names_1.default.json}.slice(${names_1.default.jsonPos}, ${names_1.default.jsonPos}+${len})`; +} +function jsonSyntaxError(cxt) { + parsingError(cxt, (0, codegen_1._) `"unexpected token " + ${names_1.default.json}[${names_1.default.jsonPos}]`); +} +function parsingError({ gen, parseName }, msg) { + gen.assign((0, codegen_1._) `${parseName}.message`, msg); + gen.assign((0, codegen_1._) `${parseName}.position`, names_1.default.jsonPos); + gen.return(undef); +} +//# sourceMappingURL=parse.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/jtd/parse.js.map b/website/node_modules/ajv/dist/compile/jtd/parse.js.map new file mode 100644 index 00000000..a1e86279 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/jtd/parse.js.map @@ -0,0 +1 @@ +{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../lib/compile/jtd/parse.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/jtd/serialize.d.ts b/website/node_modules/ajv/dist/compile/jtd/serialize.d.ts new file mode 100644 index 00000000..b0413d71 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/jtd/serialize.d.ts @@ -0,0 +1,4 @@ +import type Ajv from "../../core"; +import { SchemaObjectMap } from "./types"; +import { SchemaEnv } from ".."; +export default function compileSerializer(this: Ajv, sch: SchemaEnv, definitions: SchemaObjectMap): SchemaEnv; diff --git a/website/node_modules/ajv/dist/compile/jtd/serialize.js b/website/node_modules/ajv/dist/compile/jtd/serialize.js new file mode 100644 index 00000000..341c5007 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/jtd/serialize.js @@ -0,0 +1,229 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const types_1 = require("./types"); +const __1 = require(".."); +const codegen_1 = require("../codegen"); +const ref_error_1 = require("../ref_error"); +const names_1 = require("../names"); +const code_1 = require("../../vocabularies/code"); +const ref_1 = require("../../vocabularies/jtd/ref"); +const util_1 = require("../util"); +const quote_1 = require("../../runtime/quote"); +const genSerialize = { + elements: serializeElements, + values: serializeValues, + discriminator: serializeDiscriminator, + properties: serializeProperties, + optionalProperties: serializeProperties, + enum: serializeString, + type: serializeType, + ref: serializeRef, +}; +function compileSerializer(sch, definitions) { + const _sch = __1.getCompilingSchema.call(this, sch); + if (_sch) + return _sch; + const { es5, lines } = this.opts.code; + const { ownProperties } = this.opts; + const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties }); + const serializeName = gen.scopeName("serialize"); + const cxt = { + self: this, + gen, + schema: sch.schema, + schemaEnv: sch, + definitions, + data: names_1.default.data, + }; + let sourceCode; + try { + this._compilations.add(sch); + sch.serializeName = serializeName; + gen.func(serializeName, names_1.default.data, false, () => { + gen.let(names_1.default.json, (0, codegen_1.str) ``); + serializeCode(cxt); + gen.return(names_1.default.json); + }); + gen.optimize(this.opts.code.optimize); + const serializeFuncCode = gen.toString(); + sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${serializeFuncCode}`; + const makeSerialize = new Function(`${names_1.default.scope}`, sourceCode); + const serialize = makeSerialize(this.scope.get()); + this.scope.value(serializeName, { ref: serialize }); + sch.serialize = serialize; + } + catch (e) { + if (sourceCode) + this.logger.error("Error compiling serializer, function code:", sourceCode); + delete sch.serialize; + delete sch.serializeName; + throw e; + } + finally { + this._compilations.delete(sch); + } + return sch; +} +exports.default = compileSerializer; +function serializeCode(cxt) { + let form; + for (const key of types_1.jtdForms) { + if (key in cxt.schema) { + form = key; + break; + } + } + serializeNullable(cxt, form ? genSerialize[form] : serializeEmpty); +} +function serializeNullable(cxt, serializeForm) { + const { gen, schema, data } = cxt; + if (!schema.nullable) + return serializeForm(cxt); + gen.if((0, codegen_1._) `${data} === undefined || ${data} === null`, () => gen.add(names_1.default.json, (0, codegen_1._) `"null"`), () => serializeForm(cxt)); +} +function serializeElements(cxt) { + const { gen, schema, data } = cxt; + gen.add(names_1.default.json, (0, codegen_1.str) `[`); + const first = gen.let("first", true); + gen.forOf("el", data, (el) => { + addComma(cxt, first); + serializeCode({ ...cxt, schema: schema.elements, data: el }); + }); + gen.add(names_1.default.json, (0, codegen_1.str) `]`); +} +function serializeValues(cxt) { + const { gen, schema, data } = cxt; + gen.add(names_1.default.json, (0, codegen_1.str) `{`); + const first = gen.let("first", true); + gen.forIn("key", data, (key) => serializeKeyValue(cxt, key, schema.values, first)); + gen.add(names_1.default.json, (0, codegen_1.str) `}`); +} +function serializeKeyValue(cxt, key, schema, first) { + const { gen, data } = cxt; + addComma(cxt, first); + serializeString({ ...cxt, data: key }); + gen.add(names_1.default.json, (0, codegen_1.str) `:`); + const value = gen.const("value", (0, codegen_1._) `${data}${(0, codegen_1.getProperty)(key)}`); + serializeCode({ ...cxt, schema, data: value }); +} +function serializeDiscriminator(cxt) { + const { gen, schema, data } = cxt; + const { discriminator } = schema; + gen.add(names_1.default.json, (0, codegen_1.str) `{${JSON.stringify(discriminator)}:`); + const tag = gen.const("tag", (0, codegen_1._) `${data}${(0, codegen_1.getProperty)(discriminator)}`); + serializeString({ ...cxt, data: tag }); + gen.if(false); + for (const tagValue in schema.mapping) { + gen.elseIf((0, codegen_1._) `${tag} === ${tagValue}`); + const sch = schema.mapping[tagValue]; + serializeSchemaProperties({ ...cxt, schema: sch }, discriminator); + } + gen.endIf(); + gen.add(names_1.default.json, (0, codegen_1.str) `}`); +} +function serializeProperties(cxt) { + const { gen } = cxt; + gen.add(names_1.default.json, (0, codegen_1.str) `{`); + serializeSchemaProperties(cxt); + gen.add(names_1.default.json, (0, codegen_1.str) `}`); +} +function serializeSchemaProperties(cxt, discriminator) { + const { gen, schema, data } = cxt; + const { properties, optionalProperties } = schema; + const props = keys(properties); + const optProps = keys(optionalProperties); + const allProps = allProperties(props.concat(optProps)); + let first = !discriminator; + let firstProp; + for (const key of props) { + if (first) + first = false; + else + gen.add(names_1.default.json, (0, codegen_1.str) `,`); + serializeProperty(key, properties[key], keyValue(key)); + } + if (first) + firstProp = gen.let("first", true); + for (const key of optProps) { + const value = keyValue(key); + gen.if((0, codegen_1.and)((0, codegen_1._) `${value} !== undefined`, (0, code_1.isOwnProperty)(gen, data, key)), () => { + addComma(cxt, firstProp); + serializeProperty(key, optionalProperties[key], value); + }); + } + if (schema.additionalProperties) { + gen.forIn("key", data, (key) => gen.if(isAdditional(key, allProps), () => serializeKeyValue(cxt, key, {}, firstProp))); + } + function keys(ps) { + return ps ? Object.keys(ps) : []; + } + function allProperties(ps) { + if (discriminator) + ps.push(discriminator); + if (new Set(ps).size !== ps.length) { + throw new Error("JTD: properties/optionalProperties/disciminator overlap"); + } + return ps; + } + function keyValue(key) { + return gen.const("value", (0, codegen_1._) `${data}${(0, codegen_1.getProperty)(key)}`); + } + function serializeProperty(key, propSchema, value) { + gen.add(names_1.default.json, (0, codegen_1.str) `${JSON.stringify(key)}:`); + serializeCode({ ...cxt, schema: propSchema, data: value }); + } + function isAdditional(key, ps) { + return ps.length ? (0, codegen_1.and)(...ps.map((p) => (0, codegen_1._) `${key} !== ${p}`)) : true; + } +} +function serializeType(cxt) { + const { gen, schema, data } = cxt; + switch (schema.type) { + case "boolean": + gen.add(names_1.default.json, (0, codegen_1._) `${data} ? "true" : "false"`); + break; + case "string": + serializeString(cxt); + break; + case "timestamp": + gen.if((0, codegen_1._) `${data} instanceof Date`, () => gen.add(names_1.default.json, (0, codegen_1._) `'"' + ${data}.toISOString() + '"'`), () => serializeString(cxt)); + break; + default: + serializeNumber(cxt); + } +} +function serializeString({ gen, data }) { + gen.add(names_1.default.json, (0, codegen_1._) `${(0, util_1.useFunc)(gen, quote_1.default)}(${data})`); +} +function serializeNumber({ gen, data }) { + gen.add(names_1.default.json, (0, codegen_1._) `"" + ${data}`); +} +function serializeRef(cxt) { + const { gen, self, data, definitions, schema, schemaEnv } = cxt; + const { ref } = schema; + const refSchema = definitions[ref]; + if (!refSchema) + throw new ref_error_1.default(self.opts.uriResolver, "", ref, `No definition ${ref}`); + if (!(0, ref_1.hasRef)(refSchema)) + return serializeCode({ ...cxt, schema: refSchema }); + const { root } = schemaEnv; + const sch = compileSerializer.call(self, new __1.SchemaEnv({ schema: refSchema, root }), definitions); + gen.add(names_1.default.json, (0, codegen_1._) `${getSerialize(gen, sch)}(${data})`); +} +function getSerialize(gen, sch) { + return sch.serialize + ? gen.scopeValue("serialize", { ref: sch.serialize }) + : (0, codegen_1._) `${gen.scopeValue("wrapper", { ref: sch })}.serialize`; +} +function serializeEmpty({ gen, data }) { + gen.add(names_1.default.json, (0, codegen_1._) `JSON.stringify(${data})`); +} +function addComma({ gen }, first) { + if (first) { + gen.if(first, () => gen.assign(first, false), () => gen.add(names_1.default.json, (0, codegen_1.str) `,`)); + } + else { + gen.add(names_1.default.json, (0, codegen_1.str) `,`); + } +} +//# sourceMappingURL=serialize.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/jtd/serialize.js.map b/website/node_modules/ajv/dist/compile/jtd/serialize.js.map new file mode 100644 index 00000000..8a574d1f --- /dev/null +++ b/website/node_modules/ajv/dist/compile/jtd/serialize.js.map @@ -0,0 +1 @@ +{"version":3,"file":"serialize.js","sourceRoot":"","sources":["../../../lib/compile/jtd/serialize.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/jtd/types.d.ts b/website/node_modules/ajv/dist/compile/jtd/types.d.ts new file mode 100644 index 00000000..f131daa5 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/jtd/types.d.ts @@ -0,0 +1,6 @@ +import type { SchemaObject } from "../../types"; +export type SchemaObjectMap = { + [Ref in string]?: SchemaObject; +}; +export declare const jtdForms: readonly ["elements", "values", "discriminator", "properties", "optionalProperties", "enum", "type", "ref"]; +export type JTDForm = typeof jtdForms[number]; diff --git a/website/node_modules/ajv/dist/compile/jtd/types.js b/website/node_modules/ajv/dist/compile/jtd/types.js new file mode 100644 index 00000000..b9c60a90 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/jtd/types.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.jtdForms = void 0; +exports.jtdForms = [ + "elements", + "values", + "discriminator", + "properties", + "optionalProperties", + "enum", + "type", + "ref", +]; +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/jtd/types.js.map b/website/node_modules/ajv/dist/compile/jtd/types.js.map new file mode 100644 index 00000000..53439e00 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/jtd/types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../lib/compile/jtd/types.ts"],"names":[],"mappings":";;;AAIa,QAAA,QAAQ,GAAG;IACtB,UAAU;IACV,QAAQ;IACR,eAAe;IACf,YAAY;IACZ,oBAAoB;IACpB,MAAM;IACN,MAAM;IACN,KAAK;CACG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/names.d.ts b/website/node_modules/ajv/dist/compile/names.d.ts new file mode 100644 index 00000000..5740e82c --- /dev/null +++ b/website/node_modules/ajv/dist/compile/names.d.ts @@ -0,0 +1,20 @@ +import { Name } from "./codegen"; +declare const names: { + data: Name; + valCxt: Name; + instancePath: Name; + parentData: Name; + parentDataProperty: Name; + rootData: Name; + dynamicAnchors: Name; + vErrors: Name; + errors: Name; + this: Name; + self: Name; + scope: Name; + json: Name; + jsonPos: Name; + jsonLen: Name; + jsonPart: Name; +}; +export default names; diff --git a/website/node_modules/ajv/dist/compile/names.js b/website/node_modules/ajv/dist/compile/names.js new file mode 100644 index 00000000..015334bd --- /dev/null +++ b/website/node_modules/ajv/dist/compile/names.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("./codegen"); +const names = { + // validation function arguments + data: new codegen_1.Name("data"), + // args passed from referencing schema + valCxt: new codegen_1.Name("valCxt"), + instancePath: new codegen_1.Name("instancePath"), + parentData: new codegen_1.Name("parentData"), + parentDataProperty: new codegen_1.Name("parentDataProperty"), + rootData: new codegen_1.Name("rootData"), + dynamicAnchors: new codegen_1.Name("dynamicAnchors"), + // function scoped variables + vErrors: new codegen_1.Name("vErrors"), + errors: new codegen_1.Name("errors"), + this: new codegen_1.Name("this"), + // "globals" + self: new codegen_1.Name("self"), + scope: new codegen_1.Name("scope"), + // JTD serialize/parse name for JSON string and position + json: new codegen_1.Name("json"), + jsonPos: new codegen_1.Name("jsonPos"), + jsonLen: new codegen_1.Name("jsonLen"), + jsonPart: new codegen_1.Name("jsonPart"), +}; +exports.default = names; +//# sourceMappingURL=names.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/names.js.map b/website/node_modules/ajv/dist/compile/names.js.map new file mode 100644 index 00000000..9bbe8e27 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/names.js.map @@ -0,0 +1 @@ +{"version":3,"file":"names.js","sourceRoot":"","sources":["../../lib/compile/names.ts"],"names":[],"mappings":";;AAAA,uCAA8B;AAE9B,MAAM,KAAK,GAAG;IACZ,gCAAgC;IAChC,IAAI,EAAE,IAAI,cAAI,CAAC,MAAM,CAAC;IACtB,sCAAsC;IACtC,MAAM,EAAE,IAAI,cAAI,CAAC,QAAQ,CAAC;IAC1B,YAAY,EAAE,IAAI,cAAI,CAAC,cAAc,CAAC;IACtC,UAAU,EAAE,IAAI,cAAI,CAAC,YAAY,CAAC;IAClC,kBAAkB,EAAE,IAAI,cAAI,CAAC,oBAAoB,CAAC;IAClD,QAAQ,EAAE,IAAI,cAAI,CAAC,UAAU,CAAC;IAC9B,cAAc,EAAE,IAAI,cAAI,CAAC,gBAAgB,CAAC;IAC1C,4BAA4B;IAC5B,OAAO,EAAE,IAAI,cAAI,CAAC,SAAS,CAAC;IAC5B,MAAM,EAAE,IAAI,cAAI,CAAC,QAAQ,CAAC;IAC1B,IAAI,EAAE,IAAI,cAAI,CAAC,MAAM,CAAC;IACtB,YAAY;IACZ,IAAI,EAAE,IAAI,cAAI,CAAC,MAAM,CAAC;IACtB,KAAK,EAAE,IAAI,cAAI,CAAC,OAAO,CAAC;IACxB,wDAAwD;IACxD,IAAI,EAAE,IAAI,cAAI,CAAC,MAAM,CAAC;IACtB,OAAO,EAAE,IAAI,cAAI,CAAC,SAAS,CAAC;IAC5B,OAAO,EAAE,IAAI,cAAI,CAAC,SAAS,CAAC;IAC5B,QAAQ,EAAE,IAAI,cAAI,CAAC,UAAU,CAAC;CAC/B,CAAA;AAED,kBAAe,KAAK,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/ref_error.d.ts b/website/node_modules/ajv/dist/compile/ref_error.d.ts new file mode 100644 index 00000000..43374439 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/ref_error.d.ts @@ -0,0 +1,6 @@ +import type { UriResolver } from "../types"; +export default class MissingRefError extends Error { + readonly missingRef: string; + readonly missingSchema: string; + constructor(resolver: UriResolver, baseId: string, ref: string, msg?: string); +} diff --git a/website/node_modules/ajv/dist/compile/ref_error.js b/website/node_modules/ajv/dist/compile/ref_error.js new file mode 100644 index 00000000..3916dec8 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/ref_error.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const resolve_1 = require("./resolve"); +class MissingRefError extends Error { + constructor(resolver, baseId, ref, msg) { + super(msg || `can't resolve reference ${ref} from id ${baseId}`); + this.missingRef = (0, resolve_1.resolveUrl)(resolver, baseId, ref); + this.missingSchema = (0, resolve_1.normalizeId)((0, resolve_1.getFullPath)(resolver, this.missingRef)); + } +} +exports.default = MissingRefError; +//# sourceMappingURL=ref_error.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/ref_error.js.map b/website/node_modules/ajv/dist/compile/ref_error.js.map new file mode 100644 index 00000000..d13f5f2d --- /dev/null +++ b/website/node_modules/ajv/dist/compile/ref_error.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ref_error.js","sourceRoot":"","sources":["../../lib/compile/ref_error.ts"],"names":[],"mappings":";;AAAA,uCAA8D;AAG9D,MAAqB,eAAgB,SAAQ,KAAK;IAIhD,YAAY,QAAqB,EAAE,MAAc,EAAE,GAAW,EAAE,GAAY;QAC1E,KAAK,CAAC,GAAG,IAAI,2BAA2B,GAAG,YAAY,MAAM,EAAE,CAAC,CAAA;QAChE,IAAI,CAAC,UAAU,GAAG,IAAA,oBAAU,EAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;QACnD,IAAI,CAAC,aAAa,GAAG,IAAA,qBAAW,EAAC,IAAA,qBAAW,EAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAA;IAC1E,CAAC;CACF;AATD,kCASC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/resolve.d.ts b/website/node_modules/ajv/dist/compile/resolve.d.ts new file mode 100644 index 00000000..70ef6ce6 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/resolve.d.ts @@ -0,0 +1,12 @@ +import type { AnySchema, AnySchemaObject, UriResolver } from "../types"; +import type Ajv from "../ajv"; +import type { URIComponents } from "uri-js"; +export type LocalRefs = { + [Ref in string]?: AnySchemaObject; +}; +export declare function inlineRef(schema: AnySchema, limit?: boolean | number): boolean; +export declare function getFullPath(resolver: UriResolver, id?: string, normalize?: boolean): string; +export declare function _getFullPath(resolver: UriResolver, p: URIComponents): string; +export declare function normalizeId(id: string | undefined): string; +export declare function resolveUrl(resolver: UriResolver, baseId: string, id: string): string; +export declare function getSchemaRefs(this: Ajv, schema: AnySchema, baseId: string): LocalRefs; diff --git a/website/node_modules/ajv/dist/compile/resolve.js b/website/node_modules/ajv/dist/compile/resolve.js new file mode 100644 index 00000000..8b51a20e --- /dev/null +++ b/website/node_modules/ajv/dist/compile/resolve.js @@ -0,0 +1,155 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getSchemaRefs = exports.resolveUrl = exports.normalizeId = exports._getFullPath = exports.getFullPath = exports.inlineRef = void 0; +const util_1 = require("./util"); +const equal = require("fast-deep-equal"); +const traverse = require("json-schema-traverse"); +// TODO refactor to use keyword definitions +const SIMPLE_INLINED = new Set([ + "type", + "format", + "pattern", + "maxLength", + "minLength", + "maxProperties", + "minProperties", + "maxItems", + "minItems", + "maximum", + "minimum", + "uniqueItems", + "multipleOf", + "required", + "enum", + "const", +]); +function inlineRef(schema, limit = true) { + if (typeof schema == "boolean") + return true; + if (limit === true) + return !hasRef(schema); + if (!limit) + return false; + return countKeys(schema) <= limit; +} +exports.inlineRef = inlineRef; +const REF_KEYWORDS = new Set([ + "$ref", + "$recursiveRef", + "$recursiveAnchor", + "$dynamicRef", + "$dynamicAnchor", +]); +function hasRef(schema) { + for (const key in schema) { + if (REF_KEYWORDS.has(key)) + return true; + const sch = schema[key]; + if (Array.isArray(sch) && sch.some(hasRef)) + return true; + if (typeof sch == "object" && hasRef(sch)) + return true; + } + return false; +} +function countKeys(schema) { + let count = 0; + for (const key in schema) { + if (key === "$ref") + return Infinity; + count++; + if (SIMPLE_INLINED.has(key)) + continue; + if (typeof schema[key] == "object") { + (0, util_1.eachItem)(schema[key], (sch) => (count += countKeys(sch))); + } + if (count === Infinity) + return Infinity; + } + return count; +} +function getFullPath(resolver, id = "", normalize) { + if (normalize !== false) + id = normalizeId(id); + const p = resolver.parse(id); + return _getFullPath(resolver, p); +} +exports.getFullPath = getFullPath; +function _getFullPath(resolver, p) { + const serialized = resolver.serialize(p); + return serialized.split("#")[0] + "#"; +} +exports._getFullPath = _getFullPath; +const TRAILING_SLASH_HASH = /#\/?$/; +function normalizeId(id) { + return id ? id.replace(TRAILING_SLASH_HASH, "") : ""; +} +exports.normalizeId = normalizeId; +function resolveUrl(resolver, baseId, id) { + id = normalizeId(id); + return resolver.resolve(baseId, id); +} +exports.resolveUrl = resolveUrl; +const ANCHOR = /^[a-z_][-a-z0-9._]*$/i; +function getSchemaRefs(schema, baseId) { + if (typeof schema == "boolean") + return {}; + const { schemaId, uriResolver } = this.opts; + const schId = normalizeId(schema[schemaId] || baseId); + const baseIds = { "": schId }; + const pathPrefix = getFullPath(uriResolver, schId, false); + const localRefs = {}; + const schemaRefs = new Set(); + traverse(schema, { allKeys: true }, (sch, jsonPtr, _, parentJsonPtr) => { + if (parentJsonPtr === undefined) + return; + const fullPath = pathPrefix + jsonPtr; + let baseId = baseIds[parentJsonPtr]; + if (typeof sch[schemaId] == "string") + baseId = addRef.call(this, sch[schemaId]); + addAnchor.call(this, sch.$anchor); + addAnchor.call(this, sch.$dynamicAnchor); + baseIds[jsonPtr] = baseId; + function addRef(ref) { + // eslint-disable-next-line @typescript-eslint/unbound-method + const _resolve = this.opts.uriResolver.resolve; + ref = normalizeId(baseId ? _resolve(baseId, ref) : ref); + if (schemaRefs.has(ref)) + throw ambiguos(ref); + schemaRefs.add(ref); + let schOrRef = this.refs[ref]; + if (typeof schOrRef == "string") + schOrRef = this.refs[schOrRef]; + if (typeof schOrRef == "object") { + checkAmbiguosRef(sch, schOrRef.schema, ref); + } + else if (ref !== normalizeId(fullPath)) { + if (ref[0] === "#") { + checkAmbiguosRef(sch, localRefs[ref], ref); + localRefs[ref] = sch; + } + else { + this.refs[ref] = fullPath; + } + } + return ref; + } + function addAnchor(anchor) { + if (typeof anchor == "string") { + if (!ANCHOR.test(anchor)) + throw new Error(`invalid anchor "${anchor}"`); + addRef.call(this, `#${anchor}`); + } + } + }); + return localRefs; + function checkAmbiguosRef(sch1, sch2, ref) { + if (sch2 !== undefined && !equal(sch1, sch2)) + throw ambiguos(ref); + } + function ambiguos(ref) { + return new Error(`reference "${ref}" resolves to more than one schema`); + } +} +exports.getSchemaRefs = getSchemaRefs; +//# sourceMappingURL=resolve.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/resolve.js.map b/website/node_modules/ajv/dist/compile/resolve.js.map new file mode 100644 index 00000000..81f08b0c --- /dev/null +++ b/website/node_modules/ajv/dist/compile/resolve.js.map @@ -0,0 +1 @@ +{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../lib/compile/resolve.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/rules.d.ts b/website/node_modules/ajv/dist/compile/rules.d.ts new file mode 100644 index 00000000..d77d68f9 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/rules.d.ts @@ -0,0 +1,28 @@ +import type { AddedKeywordDefinition } from "../types"; +declare const _jsonTypes: readonly ["string", "number", "integer", "boolean", "null", "object", "array"]; +export type JSONType = typeof _jsonTypes[number]; +export declare function isJSONType(x: unknown): x is JSONType; +type ValidationTypes = { + [K in JSONType]: boolean | RuleGroup | undefined; +}; +export interface ValidationRules { + rules: RuleGroup[]; + post: RuleGroup; + all: { + [Key in string]?: boolean | Rule; + }; + keywords: { + [Key in string]?: boolean; + }; + types: ValidationTypes; +} +export interface RuleGroup { + type?: JSONType; + rules: Rule[]; +} +export interface Rule { + keyword: string; + definition: AddedKeywordDefinition; +} +export declare function getRules(): ValidationRules; +export {}; diff --git a/website/node_modules/ajv/dist/compile/rules.js b/website/node_modules/ajv/dist/compile/rules.js new file mode 100644 index 00000000..82a591ff --- /dev/null +++ b/website/node_modules/ajv/dist/compile/rules.js @@ -0,0 +1,26 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getRules = exports.isJSONType = void 0; +const _jsonTypes = ["string", "number", "integer", "boolean", "null", "object", "array"]; +const jsonTypes = new Set(_jsonTypes); +function isJSONType(x) { + return typeof x == "string" && jsonTypes.has(x); +} +exports.isJSONType = isJSONType; +function getRules() { + const groups = { + number: { type: "number", rules: [] }, + string: { type: "string", rules: [] }, + array: { type: "array", rules: [] }, + object: { type: "object", rules: [] }, + }; + return { + types: { ...groups, integer: true, boolean: true, null: true }, + rules: [{ rules: [] }, groups.number, groups.string, groups.array, groups.object], + post: { rules: [] }, + all: {}, + keywords: {}, + }; +} +exports.getRules = getRules; +//# sourceMappingURL=rules.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/rules.js.map b/website/node_modules/ajv/dist/compile/rules.js.map new file mode 100644 index 00000000..084c70f8 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/rules.js.map @@ -0,0 +1 @@ +{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../lib/compile/rules.ts"],"names":[],"mappings":";;;AAEA,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAA;AAIjG,MAAM,SAAS,GAAgB,IAAI,GAAG,CAAC,UAAU,CAAC,CAAA;AAElD,SAAgB,UAAU,CAAC,CAAU;IACnC,OAAO,OAAO,CAAC,IAAI,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AACjD,CAAC;AAFD,gCAEC;AAyBD,SAAgB,QAAQ;IACtB,MAAM,MAAM,GAAgE;QAC1E,MAAM,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAC;QACnC,MAAM,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAC;QACnC,KAAK,EAAE,EAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAC;QACjC,MAAM,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAC;KACpC,CAAA;IACD,OAAO;QACL,KAAK,EAAE,EAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAC;QAC5D,KAAK,EAAE,CAAC,EAAC,KAAK,EAAE,EAAE,EAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;QAC/E,IAAI,EAAE,EAAC,KAAK,EAAE,EAAE,EAAC;QACjB,GAAG,EAAE,EAAE;QACP,QAAQ,EAAE,EAAE;KACb,CAAA;AACH,CAAC;AAdD,4BAcC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/util.d.ts b/website/node_modules/ajv/dist/compile/util.d.ts new file mode 100644 index 00000000..1ec282ae --- /dev/null +++ b/website/node_modules/ajv/dist/compile/util.d.ts @@ -0,0 +1,40 @@ +import type { AnySchema, EvaluatedProperties, EvaluatedItems } from "../types"; +import type { SchemaCxt, SchemaObjCxt } from "."; +import { Code, Name, CodeGen } from "./codegen"; +import type { Rule, ValidationRules } from "./rules"; +export declare function toHash(arr: T[]): { + [K in T]?: true; +}; +export declare function alwaysValidSchema(it: SchemaCxt, schema: AnySchema): boolean | void; +export declare function checkUnknownRules(it: SchemaCxt, schema?: AnySchema): void; +export declare function schemaHasRules(schema: AnySchema, rules: { + [Key in string]?: boolean | Rule; +}): boolean; +export declare function schemaHasRulesButRef(schema: AnySchema, RULES: ValidationRules): boolean; +export declare function schemaRefOrVal({ topSchemaRef, schemaPath }: SchemaObjCxt, schema: unknown, keyword: string, $data?: string | false): Code | number | boolean; +export declare function unescapeFragment(str: string): string; +export declare function escapeFragment(str: string | number): string; +export declare function escapeJsonPointer(str: string | number): string; +export declare function unescapeJsonPointer(str: string): string; +export declare function eachItem(xs: T | T[], f: (x: T) => void): void; +type SomeEvaluated = EvaluatedProperties | EvaluatedItems; +type MergeEvaluatedFunc = (gen: CodeGen, from: Name | T, to: Name | Exclude | undefined, toName?: typeof Name) => Name | T; +interface MergeEvaluated { + props: MergeEvaluatedFunc; + items: MergeEvaluatedFunc; +} +export declare const mergeEvaluated: MergeEvaluated; +export declare function evaluatedPropsToName(gen: CodeGen, ps?: EvaluatedProperties): Name; +export declare function setEvaluated(gen: CodeGen, props: Name, ps: { + [K in string]?: true; +}): void; +export declare function useFunc(gen: CodeGen, f: { + code: string; +}): Name; +export declare enum Type { + Num = 0, + Str = 1 +} +export declare function getErrorPath(dataProp: Name | string | number, dataPropType?: Type, jsPropertySyntax?: boolean): Code | string; +export declare function checkStrictMode(it: SchemaCxt, msg: string, mode?: boolean | "log"): void; +export {}; diff --git a/website/node_modules/ajv/dist/compile/util.js b/website/node_modules/ajv/dist/compile/util.js new file mode 100644 index 00000000..091ad4c0 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/util.js @@ -0,0 +1,178 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.checkStrictMode = exports.getErrorPath = exports.Type = exports.useFunc = exports.setEvaluated = exports.evaluatedPropsToName = exports.mergeEvaluated = exports.eachItem = exports.unescapeJsonPointer = exports.escapeJsonPointer = exports.escapeFragment = exports.unescapeFragment = exports.schemaRefOrVal = exports.schemaHasRulesButRef = exports.schemaHasRules = exports.checkUnknownRules = exports.alwaysValidSchema = exports.toHash = void 0; +const codegen_1 = require("./codegen"); +const code_1 = require("./codegen/code"); +// TODO refactor to use Set +function toHash(arr) { + const hash = {}; + for (const item of arr) + hash[item] = true; + return hash; +} +exports.toHash = toHash; +function alwaysValidSchema(it, schema) { + if (typeof schema == "boolean") + return schema; + if (Object.keys(schema).length === 0) + return true; + checkUnknownRules(it, schema); + return !schemaHasRules(schema, it.self.RULES.all); +} +exports.alwaysValidSchema = alwaysValidSchema; +function checkUnknownRules(it, schema = it.schema) { + const { opts, self } = it; + if (!opts.strictSchema) + return; + if (typeof schema === "boolean") + return; + const rules = self.RULES.keywords; + for (const key in schema) { + if (!rules[key]) + checkStrictMode(it, `unknown keyword: "${key}"`); + } +} +exports.checkUnknownRules = checkUnknownRules; +function schemaHasRules(schema, rules) { + if (typeof schema == "boolean") + return !schema; + for (const key in schema) + if (rules[key]) + return true; + return false; +} +exports.schemaHasRules = schemaHasRules; +function schemaHasRulesButRef(schema, RULES) { + if (typeof schema == "boolean") + return !schema; + for (const key in schema) + if (key !== "$ref" && RULES.all[key]) + return true; + return false; +} +exports.schemaHasRulesButRef = schemaHasRulesButRef; +function schemaRefOrVal({ topSchemaRef, schemaPath }, schema, keyword, $data) { + if (!$data) { + if (typeof schema == "number" || typeof schema == "boolean") + return schema; + if (typeof schema == "string") + return (0, codegen_1._) `${schema}`; + } + return (0, codegen_1._) `${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`; +} +exports.schemaRefOrVal = schemaRefOrVal; +function unescapeFragment(str) { + return unescapeJsonPointer(decodeURIComponent(str)); +} +exports.unescapeFragment = unescapeFragment; +function escapeFragment(str) { + return encodeURIComponent(escapeJsonPointer(str)); +} +exports.escapeFragment = escapeFragment; +function escapeJsonPointer(str) { + if (typeof str == "number") + return `${str}`; + return str.replace(/~/g, "~0").replace(/\//g, "~1"); +} +exports.escapeJsonPointer = escapeJsonPointer; +function unescapeJsonPointer(str) { + return str.replace(/~1/g, "/").replace(/~0/g, "~"); +} +exports.unescapeJsonPointer = unescapeJsonPointer; +function eachItem(xs, f) { + if (Array.isArray(xs)) { + for (const x of xs) + f(x); + } + else { + f(xs); + } +} +exports.eachItem = eachItem; +function makeMergeEvaluated({ mergeNames, mergeToName, mergeValues, resultToName, }) { + return (gen, from, to, toName) => { + const res = to === undefined + ? from + : to instanceof codegen_1.Name + ? (from instanceof codegen_1.Name ? mergeNames(gen, from, to) : mergeToName(gen, from, to), to) + : from instanceof codegen_1.Name + ? (mergeToName(gen, to, from), from) + : mergeValues(from, to); + return toName === codegen_1.Name && !(res instanceof codegen_1.Name) ? resultToName(gen, res) : res; + }; +} +exports.mergeEvaluated = { + props: makeMergeEvaluated({ + mergeNames: (gen, from, to) => gen.if((0, codegen_1._) `${to} !== true && ${from} !== undefined`, () => { + gen.if((0, codegen_1._) `${from} === true`, () => gen.assign(to, true), () => gen.assign(to, (0, codegen_1._) `${to} || {}`).code((0, codegen_1._) `Object.assign(${to}, ${from})`)); + }), + mergeToName: (gen, from, to) => gen.if((0, codegen_1._) `${to} !== true`, () => { + if (from === true) { + gen.assign(to, true); + } + else { + gen.assign(to, (0, codegen_1._) `${to} || {}`); + setEvaluated(gen, to, from); + } + }), + mergeValues: (from, to) => (from === true ? true : { ...from, ...to }), + resultToName: evaluatedPropsToName, + }), + items: makeMergeEvaluated({ + mergeNames: (gen, from, to) => gen.if((0, codegen_1._) `${to} !== true && ${from} !== undefined`, () => gen.assign(to, (0, codegen_1._) `${from} === true ? true : ${to} > ${from} ? ${to} : ${from}`)), + mergeToName: (gen, from, to) => gen.if((0, codegen_1._) `${to} !== true`, () => gen.assign(to, from === true ? true : (0, codegen_1._) `${to} > ${from} ? ${to} : ${from}`)), + mergeValues: (from, to) => (from === true ? true : Math.max(from, to)), + resultToName: (gen, items) => gen.var("items", items), + }), +}; +function evaluatedPropsToName(gen, ps) { + if (ps === true) + return gen.var("props", true); + const props = gen.var("props", (0, codegen_1._) `{}`); + if (ps !== undefined) + setEvaluated(gen, props, ps); + return props; +} +exports.evaluatedPropsToName = evaluatedPropsToName; +function setEvaluated(gen, props, ps) { + Object.keys(ps).forEach((p) => gen.assign((0, codegen_1._) `${props}${(0, codegen_1.getProperty)(p)}`, true)); +} +exports.setEvaluated = setEvaluated; +const snippets = {}; +function useFunc(gen, f) { + return gen.scopeValue("func", { + ref: f, + code: snippets[f.code] || (snippets[f.code] = new code_1._Code(f.code)), + }); +} +exports.useFunc = useFunc; +var Type; +(function (Type) { + Type[Type["Num"] = 0] = "Num"; + Type[Type["Str"] = 1] = "Str"; +})(Type = exports.Type || (exports.Type = {})); +function getErrorPath(dataProp, dataPropType, jsPropertySyntax) { + // let path + if (dataProp instanceof codegen_1.Name) { + const isNumber = dataPropType === Type.Num; + return jsPropertySyntax + ? isNumber + ? (0, codegen_1._) `"[" + ${dataProp} + "]"` + : (0, codegen_1._) `"['" + ${dataProp} + "']"` + : isNumber + ? (0, codegen_1._) `"/" + ${dataProp}` + : (0, codegen_1._) `"/" + ${dataProp}.replace(/~/g, "~0").replace(/\\//g, "~1")`; // TODO maybe use global escapePointer + } + return jsPropertySyntax ? (0, codegen_1.getProperty)(dataProp).toString() : "/" + escapeJsonPointer(dataProp); +} +exports.getErrorPath = getErrorPath; +function checkStrictMode(it, msg, mode = it.opts.strictSchema) { + if (!mode) + return; + msg = `strict mode: ${msg}`; + if (mode === true) + throw new Error(msg); + it.self.logger.warn(msg); +} +exports.checkStrictMode = checkStrictMode; +//# sourceMappingURL=util.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/util.js.map b/website/node_modules/ajv/dist/compile/util.js.map new file mode 100644 index 00000000..a2128fc3 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/util.js.map @@ -0,0 +1 @@ +{"version":3,"file":"util.js","sourceRoot":"","sources":["../../lib/compile/util.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/applicability.d.ts b/website/node_modules/ajv/dist/compile/validate/applicability.d.ts new file mode 100644 index 00000000..165d375d --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/applicability.d.ts @@ -0,0 +1,6 @@ +import type { AnySchemaObject } from "../../types"; +import type { SchemaObjCxt } from ".."; +import type { JSONType, RuleGroup, Rule } from "../rules"; +export declare function schemaHasRulesForType({ schema, self }: SchemaObjCxt, type: JSONType): boolean | undefined; +export declare function shouldUseGroup(schema: AnySchemaObject, group: RuleGroup): boolean; +export declare function shouldUseRule(schema: AnySchemaObject, rule: Rule): boolean | undefined; diff --git a/website/node_modules/ajv/dist/compile/validate/applicability.js b/website/node_modules/ajv/dist/compile/validate/applicability.js new file mode 100644 index 00000000..6187dbbe --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/applicability.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.shouldUseRule = exports.shouldUseGroup = exports.schemaHasRulesForType = void 0; +function schemaHasRulesForType({ schema, self }, type) { + const group = self.RULES.types[type]; + return group && group !== true && shouldUseGroup(schema, group); +} +exports.schemaHasRulesForType = schemaHasRulesForType; +function shouldUseGroup(schema, group) { + return group.rules.some((rule) => shouldUseRule(schema, rule)); +} +exports.shouldUseGroup = shouldUseGroup; +function shouldUseRule(schema, rule) { + var _a; + return (schema[rule.keyword] !== undefined || + ((_a = rule.definition.implements) === null || _a === void 0 ? void 0 : _a.some((kwd) => schema[kwd] !== undefined))); +} +exports.shouldUseRule = shouldUseRule; +//# sourceMappingURL=applicability.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/applicability.js.map b/website/node_modules/ajv/dist/compile/validate/applicability.js.map new file mode 100644 index 00000000..450cfe75 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/applicability.js.map @@ -0,0 +1 @@ +{"version":3,"file":"applicability.js","sourceRoot":"","sources":["../../../lib/compile/validate/applicability.ts"],"names":[],"mappings":";;;AAIA,SAAgB,qBAAqB,CACnC,EAAC,MAAM,EAAE,IAAI,EAAe,EAC5B,IAAc;IAEd,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACpC,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,IAAI,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AACjE,CAAC;AAND,sDAMC;AAED,SAAgB,cAAc,CAAC,MAAuB,EAAE,KAAgB;IACtE,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;AAChE,CAAC;AAFD,wCAEC;AAED,SAAgB,aAAa,CAAC,MAAuB,EAAE,IAAU;;IAC/D,OAAO,CACL,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,SAAS;SAClC,MAAA,IAAI,CAAC,UAAU,CAAC,UAAU,0CAAE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAA,CACrE,CAAA;AACH,CAAC;AALD,sCAKC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/boolSchema.d.ts b/website/node_modules/ajv/dist/compile/validate/boolSchema.d.ts new file mode 100644 index 00000000..0ce79520 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/boolSchema.d.ts @@ -0,0 +1,4 @@ +import type { SchemaCxt } from ".."; +import { Name } from "../codegen"; +export declare function topBoolOrEmptySchema(it: SchemaCxt): void; +export declare function boolOrEmptySchema(it: SchemaCxt, valid: Name): void; diff --git a/website/node_modules/ajv/dist/compile/validate/boolSchema.js b/website/node_modules/ajv/dist/compile/validate/boolSchema.js new file mode 100644 index 00000000..8eeb7b5e --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/boolSchema.js @@ -0,0 +1,50 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.boolOrEmptySchema = exports.topBoolOrEmptySchema = void 0; +const errors_1 = require("../errors"); +const codegen_1 = require("../codegen"); +const names_1 = require("../names"); +const boolError = { + message: "boolean schema is false", +}; +function topBoolOrEmptySchema(it) { + const { gen, schema, validateName } = it; + if (schema === false) { + falseSchemaError(it, false); + } + else if (typeof schema == "object" && schema.$async === true) { + gen.return(names_1.default.data); + } + else { + gen.assign((0, codegen_1._) `${validateName}.errors`, null); + gen.return(true); + } +} +exports.topBoolOrEmptySchema = topBoolOrEmptySchema; +function boolOrEmptySchema(it, valid) { + const { gen, schema } = it; + if (schema === false) { + gen.var(valid, false); // TODO var + falseSchemaError(it); + } + else { + gen.var(valid, true); // TODO var + } +} +exports.boolOrEmptySchema = boolOrEmptySchema; +function falseSchemaError(it, overrideAllErrors) { + const { gen, data } = it; + // TODO maybe some other interface should be used for non-keyword validation errors... + const cxt = { + gen, + keyword: "false schema", + data, + schema: false, + schemaCode: false, + schemaValue: false, + params: {}, + it, + }; + (0, errors_1.reportError)(cxt, boolError, undefined, overrideAllErrors); +} +//# sourceMappingURL=boolSchema.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/boolSchema.js.map b/website/node_modules/ajv/dist/compile/validate/boolSchema.js.map new file mode 100644 index 00000000..4aa6c99a --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/boolSchema.js.map @@ -0,0 +1 @@ +{"version":3,"file":"boolSchema.js","sourceRoot":"","sources":["../../../lib/compile/validate/boolSchema.ts"],"names":[],"mappings":";;;AAEA,sCAAqC;AACrC,wCAAkC;AAClC,oCAAwB;AAExB,MAAM,SAAS,GAA2B;IACxC,OAAO,EAAE,yBAAyB;CACnC,CAAA;AAED,SAAgB,oBAAoB,CAAC,EAAa;IAChD,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAC,GAAG,EAAE,CAAA;IACtC,IAAI,MAAM,KAAK,KAAK,EAAE;QACpB,gBAAgB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;KAC5B;SAAM,IAAI,OAAO,MAAM,IAAI,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE;QAC9D,GAAG,CAAC,MAAM,CAAC,eAAC,CAAC,IAAI,CAAC,CAAA;KACnB;SAAM;QACL,GAAG,CAAC,MAAM,CAAC,IAAA,WAAC,EAAA,GAAG,YAAY,SAAS,EAAE,IAAI,CAAC,CAAA;QAC3C,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;KACjB;AACH,CAAC;AAVD,oDAUC;AAED,SAAgB,iBAAiB,CAAC,EAAa,EAAE,KAAW;IAC1D,MAAM,EAAC,GAAG,EAAE,MAAM,EAAC,GAAG,EAAE,CAAA;IACxB,IAAI,MAAM,KAAK,KAAK,EAAE;QACpB,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA,CAAC,WAAW;QACjC,gBAAgB,CAAC,EAAE,CAAC,CAAA;KACrB;SAAM;QACL,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA,CAAC,WAAW;KACjC;AACH,CAAC;AARD,8CAQC;AAED,SAAS,gBAAgB,CAAC,EAAa,EAAE,iBAA2B;IAClE,MAAM,EAAC,GAAG,EAAE,IAAI,EAAC,GAAG,EAAE,CAAA;IACtB,sFAAsF;IACtF,MAAM,GAAG,GAAoB;QAC3B,GAAG;QACH,OAAO,EAAE,cAAc;QACvB,IAAI;QACJ,MAAM,EAAE,KAAK;QACb,UAAU,EAAE,KAAK;QACjB,WAAW,EAAE,KAAK;QAClB,MAAM,EAAE,EAAE;QACV,EAAE;KACH,CAAA;IACD,IAAA,oBAAW,EAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAA;AAC3D,CAAC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/dataType.d.ts b/website/node_modules/ajv/dist/compile/validate/dataType.d.ts new file mode 100644 index 00000000..91a6194a --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/dataType.d.ts @@ -0,0 +1,17 @@ +import type { ErrorObject, AnySchemaObject } from "../../types"; +import type { SchemaObjCxt } from ".."; +import { JSONType } from "../rules"; +import { Code, Name } from "../codegen"; +export declare enum DataType { + Correct = 0, + Wrong = 1 +} +export declare function getSchemaTypes(schema: AnySchemaObject): JSONType[]; +export declare function getJSONTypes(ts: unknown | unknown[]): JSONType[]; +export declare function coerceAndCheckDataType(it: SchemaObjCxt, types: JSONType[]): boolean; +export declare function checkDataType(dataType: JSONType, data: Name, strictNums?: boolean | "log", correct?: DataType): Code; +export declare function checkDataTypes(dataTypes: JSONType[], data: Name, strictNums?: boolean | "log", correct?: DataType): Code; +export type TypeError = ErrorObject<"type", { + type: string; +}>; +export declare function reportTypeError(it: SchemaObjCxt): void; diff --git a/website/node_modules/ajv/dist/compile/validate/dataType.js b/website/node_modules/ajv/dist/compile/validate/dataType.js new file mode 100644 index 00000000..1732d466 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/dataType.js @@ -0,0 +1,202 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.reportTypeError = exports.checkDataTypes = exports.checkDataType = exports.coerceAndCheckDataType = exports.getJSONTypes = exports.getSchemaTypes = exports.DataType = void 0; +const rules_1 = require("../rules"); +const applicability_1 = require("./applicability"); +const errors_1 = require("../errors"); +const codegen_1 = require("../codegen"); +const util_1 = require("../util"); +var DataType; +(function (DataType) { + DataType[DataType["Correct"] = 0] = "Correct"; + DataType[DataType["Wrong"] = 1] = "Wrong"; +})(DataType = exports.DataType || (exports.DataType = {})); +function getSchemaTypes(schema) { + const types = getJSONTypes(schema.type); + const hasNull = types.includes("null"); + if (hasNull) { + if (schema.nullable === false) + throw new Error("type: null contradicts nullable: false"); + } + else { + if (!types.length && schema.nullable !== undefined) { + throw new Error('"nullable" cannot be used without "type"'); + } + if (schema.nullable === true) + types.push("null"); + } + return types; +} +exports.getSchemaTypes = getSchemaTypes; +function getJSONTypes(ts) { + const types = Array.isArray(ts) ? ts : ts ? [ts] : []; + if (types.every(rules_1.isJSONType)) + return types; + throw new Error("type must be JSONType or JSONType[]: " + types.join(",")); +} +exports.getJSONTypes = getJSONTypes; +function coerceAndCheckDataType(it, types) { + const { gen, data, opts } = it; + const coerceTo = coerceToTypes(types, opts.coerceTypes); + const checkTypes = types.length > 0 && + !(coerceTo.length === 0 && types.length === 1 && (0, applicability_1.schemaHasRulesForType)(it, types[0])); + if (checkTypes) { + const wrongType = checkDataTypes(types, data, opts.strictNumbers, DataType.Wrong); + gen.if(wrongType, () => { + if (coerceTo.length) + coerceData(it, types, coerceTo); + else + reportTypeError(it); + }); + } + return checkTypes; +} +exports.coerceAndCheckDataType = coerceAndCheckDataType; +const COERCIBLE = new Set(["string", "number", "integer", "boolean", "null"]); +function coerceToTypes(types, coerceTypes) { + return coerceTypes + ? types.filter((t) => COERCIBLE.has(t) || (coerceTypes === "array" && t === "array")) + : []; +} +function coerceData(it, types, coerceTo) { + const { gen, data, opts } = it; + const dataType = gen.let("dataType", (0, codegen_1._) `typeof ${data}`); + const coerced = gen.let("coerced", (0, codegen_1._) `undefined`); + if (opts.coerceTypes === "array") { + gen.if((0, codegen_1._) `${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen + .assign(data, (0, codegen_1._) `${data}[0]`) + .assign(dataType, (0, codegen_1._) `typeof ${data}`) + .if(checkDataTypes(types, data, opts.strictNumbers), () => gen.assign(coerced, data))); + } + gen.if((0, codegen_1._) `${coerced} !== undefined`); + for (const t of coerceTo) { + if (COERCIBLE.has(t) || (t === "array" && opts.coerceTypes === "array")) { + coerceSpecificType(t); + } + } + gen.else(); + reportTypeError(it); + gen.endIf(); + gen.if((0, codegen_1._) `${coerced} !== undefined`, () => { + gen.assign(data, coerced); + assignParentData(it, coerced); + }); + function coerceSpecificType(t) { + switch (t) { + case "string": + gen + .elseIf((0, codegen_1._) `${dataType} == "number" || ${dataType} == "boolean"`) + .assign(coerced, (0, codegen_1._) `"" + ${data}`) + .elseIf((0, codegen_1._) `${data} === null`) + .assign(coerced, (0, codegen_1._) `""`); + return; + case "number": + gen + .elseIf((0, codegen_1._) `${dataType} == "boolean" || ${data} === null + || (${dataType} == "string" && ${data} && ${data} == +${data})`) + .assign(coerced, (0, codegen_1._) `+${data}`); + return; + case "integer": + gen + .elseIf((0, codegen_1._) `${dataType} === "boolean" || ${data} === null + || (${dataType} === "string" && ${data} && ${data} == +${data} && !(${data} % 1))`) + .assign(coerced, (0, codegen_1._) `+${data}`); + return; + case "boolean": + gen + .elseIf((0, codegen_1._) `${data} === "false" || ${data} === 0 || ${data} === null`) + .assign(coerced, false) + .elseIf((0, codegen_1._) `${data} === "true" || ${data} === 1`) + .assign(coerced, true); + return; + case "null": + gen.elseIf((0, codegen_1._) `${data} === "" || ${data} === 0 || ${data} === false`); + gen.assign(coerced, null); + return; + case "array": + gen + .elseIf((0, codegen_1._) `${dataType} === "string" || ${dataType} === "number" + || ${dataType} === "boolean" || ${data} === null`) + .assign(coerced, (0, codegen_1._) `[${data}]`); + } + } +} +function assignParentData({ gen, parentData, parentDataProperty }, expr) { + // TODO use gen.property + gen.if((0, codegen_1._) `${parentData} !== undefined`, () => gen.assign((0, codegen_1._) `${parentData}[${parentDataProperty}]`, expr)); +} +function checkDataType(dataType, data, strictNums, correct = DataType.Correct) { + const EQ = correct === DataType.Correct ? codegen_1.operators.EQ : codegen_1.operators.NEQ; + let cond; + switch (dataType) { + case "null": + return (0, codegen_1._) `${data} ${EQ} null`; + case "array": + cond = (0, codegen_1._) `Array.isArray(${data})`; + break; + case "object": + cond = (0, codegen_1._) `${data} && typeof ${data} == "object" && !Array.isArray(${data})`; + break; + case "integer": + cond = numCond((0, codegen_1._) `!(${data} % 1) && !isNaN(${data})`); + break; + case "number": + cond = numCond(); + break; + default: + return (0, codegen_1._) `typeof ${data} ${EQ} ${dataType}`; + } + return correct === DataType.Correct ? cond : (0, codegen_1.not)(cond); + function numCond(_cond = codegen_1.nil) { + return (0, codegen_1.and)((0, codegen_1._) `typeof ${data} == "number"`, _cond, strictNums ? (0, codegen_1._) `isFinite(${data})` : codegen_1.nil); + } +} +exports.checkDataType = checkDataType; +function checkDataTypes(dataTypes, data, strictNums, correct) { + if (dataTypes.length === 1) { + return checkDataType(dataTypes[0], data, strictNums, correct); + } + let cond; + const types = (0, util_1.toHash)(dataTypes); + if (types.array && types.object) { + const notObj = (0, codegen_1._) `typeof ${data} != "object"`; + cond = types.null ? notObj : (0, codegen_1._) `!${data} || ${notObj}`; + delete types.null; + delete types.array; + delete types.object; + } + else { + cond = codegen_1.nil; + } + if (types.number) + delete types.integer; + for (const t in types) + cond = (0, codegen_1.and)(cond, checkDataType(t, data, strictNums, correct)); + return cond; +} +exports.checkDataTypes = checkDataTypes; +const typeError = { + message: ({ schema }) => `must be ${schema}`, + params: ({ schema, schemaValue }) => typeof schema == "string" ? (0, codegen_1._) `{type: ${schema}}` : (0, codegen_1._) `{type: ${schemaValue}}`, +}; +function reportTypeError(it) { + const cxt = getTypeErrorContext(it); + (0, errors_1.reportError)(cxt, typeError); +} +exports.reportTypeError = reportTypeError; +function getTypeErrorContext(it) { + const { gen, data, schema } = it; + const schemaCode = (0, util_1.schemaRefOrVal)(it, schema, "type"); + return { + gen, + keyword: "type", + data, + schema: schema.type, + schemaCode, + schemaValue: schemaCode, + parentSchema: schema, + params: {}, + it, + }; +} +//# sourceMappingURL=dataType.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/dataType.js.map b/website/node_modules/ajv/dist/compile/validate/dataType.js.map new file mode 100644 index 00000000..f19c40bc --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/dataType.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dataType.js","sourceRoot":"","sources":["../../../lib/compile/validate/dataType.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/defaults.d.ts b/website/node_modules/ajv/dist/compile/validate/defaults.d.ts new file mode 100644 index 00000000..02ba453b --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/defaults.d.ts @@ -0,0 +1,2 @@ +import type { SchemaObjCxt } from ".."; +export declare function assignDefaults(it: SchemaObjCxt, ty?: string): void; diff --git a/website/node_modules/ajv/dist/compile/validate/defaults.js b/website/node_modules/ajv/dist/compile/validate/defaults.js new file mode 100644 index 00000000..cd9c42d9 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/defaults.js @@ -0,0 +1,35 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.assignDefaults = void 0; +const codegen_1 = require("../codegen"); +const util_1 = require("../util"); +function assignDefaults(it, ty) { + const { properties, items } = it.schema; + if (ty === "object" && properties) { + for (const key in properties) { + assignDefault(it, key, properties[key].default); + } + } + else if (ty === "array" && Array.isArray(items)) { + items.forEach((sch, i) => assignDefault(it, i, sch.default)); + } +} +exports.assignDefaults = assignDefaults; +function assignDefault(it, prop, defaultValue) { + const { gen, compositeRule, data, opts } = it; + if (defaultValue === undefined) + return; + const childData = (0, codegen_1._) `${data}${(0, codegen_1.getProperty)(prop)}`; + if (compositeRule) { + (0, util_1.checkStrictMode)(it, `default is ignored for: ${childData}`); + return; + } + let condition = (0, codegen_1._) `${childData} === undefined`; + if (opts.useDefaults === "empty") { + condition = (0, codegen_1._) `${condition} || ${childData} === null || ${childData} === ""`; + } + // `${childData} === undefined` + + // (opts.useDefaults === "empty" ? ` || ${childData} === null || ${childData} === ""` : "") + gen.if(condition, (0, codegen_1._) `${childData} = ${(0, codegen_1.stringify)(defaultValue)}`); +} +//# sourceMappingURL=defaults.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/defaults.js.map b/website/node_modules/ajv/dist/compile/validate/defaults.js.map new file mode 100644 index 00000000..c435d057 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/defaults.js.map @@ -0,0 +1 @@ +{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../../lib/compile/validate/defaults.ts"],"names":[],"mappings":";;;AACA,wCAAoD;AACpD,kCAAuC;AAEvC,SAAgB,cAAc,CAAC,EAAgB,EAAE,EAAW;IAC1D,MAAM,EAAC,UAAU,EAAE,KAAK,EAAC,GAAG,EAAE,CAAC,MAAM,CAAA;IACrC,IAAI,EAAE,KAAK,QAAQ,IAAI,UAAU,EAAE;QACjC,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE;YAC5B,aAAa,CAAC,EAAE,EAAE,GAAG,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAA;SAChD;KACF;SAAM,IAAI,EAAE,KAAK,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;QACjD,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAS,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAA;KACrE;AACH,CAAC;AATD,wCASC;AAED,SAAS,aAAa,CAAC,EAAgB,EAAE,IAAqB,EAAE,YAAqB;IACnF,MAAM,EAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAC,GAAG,EAAE,CAAA;IAC3C,IAAI,YAAY,KAAK,SAAS;QAAE,OAAM;IACtC,MAAM,SAAS,GAAG,IAAA,WAAC,EAAA,GAAG,IAAI,GAAG,IAAA,qBAAW,EAAC,IAAI,CAAC,EAAE,CAAA;IAChD,IAAI,aAAa,EAAE;QACjB,IAAA,sBAAe,EAAC,EAAE,EAAE,2BAA2B,SAAS,EAAE,CAAC,CAAA;QAC3D,OAAM;KACP;IAED,IAAI,SAAS,GAAG,IAAA,WAAC,EAAA,GAAG,SAAS,gBAAgB,CAAA;IAC7C,IAAI,IAAI,CAAC,WAAW,KAAK,OAAO,EAAE;QAChC,SAAS,GAAG,IAAA,WAAC,EAAA,GAAG,SAAS,OAAO,SAAS,gBAAgB,SAAS,SAAS,CAAA;KAC5E;IACD,iCAAiC;IACjC,2FAA2F;IAC3F,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,IAAA,WAAC,EAAA,GAAG,SAAS,MAAM,IAAA,mBAAS,EAAC,YAAY,CAAC,EAAE,CAAC,CAAA;AACjE,CAAC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/index.d.ts b/website/node_modules/ajv/dist/compile/validate/index.d.ts new file mode 100644 index 00000000..6c533ed7 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/index.d.ts @@ -0,0 +1,42 @@ +import type { AddedKeywordDefinition, AnySchemaObject, KeywordErrorCxt, KeywordCxtParams } from "../../types"; +import type { SchemaCxt, SchemaObjCxt } from ".."; +import { SubschemaArgs } from "./subschema"; +import { Code, Name, CodeGen } from "../codegen"; +import type { JSONType } from "../rules"; +import { ErrorPaths } from "../errors"; +export declare function validateFunctionCode(it: SchemaCxt): void; +export declare class KeywordCxt implements KeywordErrorCxt { + readonly gen: CodeGen; + readonly allErrors?: boolean; + readonly keyword: string; + readonly data: Name; + readonly $data?: string | false; + schema: any; + readonly schemaValue: Code | number | boolean; + readonly schemaCode: Code | number | boolean; + readonly schemaType: JSONType[]; + readonly parentSchema: AnySchemaObject; + readonly errsCount?: Name; + params: KeywordCxtParams; + readonly it: SchemaObjCxt; + readonly def: AddedKeywordDefinition; + constructor(it: SchemaObjCxt, def: AddedKeywordDefinition, keyword: string); + result(condition: Code, successAction?: () => void, failAction?: () => void): void; + failResult(condition: Code, successAction?: () => void, failAction?: () => void): void; + pass(condition: Code, failAction?: () => void): void; + fail(condition?: Code): void; + fail$data(condition: Code): void; + error(append?: boolean, errorParams?: KeywordCxtParams, errorPaths?: ErrorPaths): void; + private _error; + $dataError(): void; + reset(): void; + ok(cond: Code | boolean): void; + setParams(obj: KeywordCxtParams, assign?: true): void; + block$data(valid: Name, codeBlock: () => void, $dataValid?: Code): void; + check$data(valid?: Name, $dataValid?: Code): void; + invalid$data(): Code; + subschema(appl: SubschemaArgs, valid: Name): SchemaCxt; + mergeEvaluated(schemaCxt: SchemaCxt, toName?: typeof Name): void; + mergeValidEvaluated(schemaCxt: SchemaCxt, valid: Name): boolean | void; +} +export declare function getData($data: string, { dataLevel, dataNames, dataPathArr }: SchemaCxt): Code | number; diff --git a/website/node_modules/ajv/dist/compile/validate/index.js b/website/node_modules/ajv/dist/compile/validate/index.js new file mode 100644 index 00000000..0d683322 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/index.js @@ -0,0 +1,520 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getData = exports.KeywordCxt = exports.validateFunctionCode = void 0; +const boolSchema_1 = require("./boolSchema"); +const dataType_1 = require("./dataType"); +const applicability_1 = require("./applicability"); +const dataType_2 = require("./dataType"); +const defaults_1 = require("./defaults"); +const keyword_1 = require("./keyword"); +const subschema_1 = require("./subschema"); +const codegen_1 = require("../codegen"); +const names_1 = require("../names"); +const resolve_1 = require("../resolve"); +const util_1 = require("../util"); +const errors_1 = require("../errors"); +// schema compilation - generates validation function, subschemaCode (below) is used for subschemas +function validateFunctionCode(it) { + if (isSchemaObj(it)) { + checkKeywords(it); + if (schemaCxtHasRules(it)) { + topSchemaObjCode(it); + return; + } + } + validateFunction(it, () => (0, boolSchema_1.topBoolOrEmptySchema)(it)); +} +exports.validateFunctionCode = validateFunctionCode; +function validateFunction({ gen, validateName, schema, schemaEnv, opts }, body) { + if (opts.code.es5) { + gen.func(validateName, (0, codegen_1._) `${names_1.default.data}, ${names_1.default.valCxt}`, schemaEnv.$async, () => { + gen.code((0, codegen_1._) `"use strict"; ${funcSourceUrl(schema, opts)}`); + destructureValCxtES5(gen, opts); + gen.code(body); + }); + } + else { + gen.func(validateName, (0, codegen_1._) `${names_1.default.data}, ${destructureValCxt(opts)}`, schemaEnv.$async, () => gen.code(funcSourceUrl(schema, opts)).code(body)); + } +} +function destructureValCxt(opts) { + return (0, codegen_1._) `{${names_1.default.instancePath}="", ${names_1.default.parentData}, ${names_1.default.parentDataProperty}, ${names_1.default.rootData}=${names_1.default.data}${opts.dynamicRef ? (0, codegen_1._) `, ${names_1.default.dynamicAnchors}={}` : codegen_1.nil}}={}`; +} +function destructureValCxtES5(gen, opts) { + gen.if(names_1.default.valCxt, () => { + gen.var(names_1.default.instancePath, (0, codegen_1._) `${names_1.default.valCxt}.${names_1.default.instancePath}`); + gen.var(names_1.default.parentData, (0, codegen_1._) `${names_1.default.valCxt}.${names_1.default.parentData}`); + gen.var(names_1.default.parentDataProperty, (0, codegen_1._) `${names_1.default.valCxt}.${names_1.default.parentDataProperty}`); + gen.var(names_1.default.rootData, (0, codegen_1._) `${names_1.default.valCxt}.${names_1.default.rootData}`); + if (opts.dynamicRef) + gen.var(names_1.default.dynamicAnchors, (0, codegen_1._) `${names_1.default.valCxt}.${names_1.default.dynamicAnchors}`); + }, () => { + gen.var(names_1.default.instancePath, (0, codegen_1._) `""`); + gen.var(names_1.default.parentData, (0, codegen_1._) `undefined`); + gen.var(names_1.default.parentDataProperty, (0, codegen_1._) `undefined`); + gen.var(names_1.default.rootData, names_1.default.data); + if (opts.dynamicRef) + gen.var(names_1.default.dynamicAnchors, (0, codegen_1._) `{}`); + }); +} +function topSchemaObjCode(it) { + const { schema, opts, gen } = it; + validateFunction(it, () => { + if (opts.$comment && schema.$comment) + commentKeyword(it); + checkNoDefault(it); + gen.let(names_1.default.vErrors, null); + gen.let(names_1.default.errors, 0); + if (opts.unevaluated) + resetEvaluated(it); + typeAndKeywords(it); + returnResults(it); + }); + return; +} +function resetEvaluated(it) { + // TODO maybe some hook to execute it in the end to check whether props/items are Name, as in assignEvaluated + const { gen, validateName } = it; + it.evaluated = gen.const("evaluated", (0, codegen_1._) `${validateName}.evaluated`); + gen.if((0, codegen_1._) `${it.evaluated}.dynamicProps`, () => gen.assign((0, codegen_1._) `${it.evaluated}.props`, (0, codegen_1._) `undefined`)); + gen.if((0, codegen_1._) `${it.evaluated}.dynamicItems`, () => gen.assign((0, codegen_1._) `${it.evaluated}.items`, (0, codegen_1._) `undefined`)); +} +function funcSourceUrl(schema, opts) { + const schId = typeof schema == "object" && schema[opts.schemaId]; + return schId && (opts.code.source || opts.code.process) ? (0, codegen_1._) `/*# sourceURL=${schId} */` : codegen_1.nil; +} +// schema compilation - this function is used recursively to generate code for sub-schemas +function subschemaCode(it, valid) { + if (isSchemaObj(it)) { + checkKeywords(it); + if (schemaCxtHasRules(it)) { + subSchemaObjCode(it, valid); + return; + } + } + (0, boolSchema_1.boolOrEmptySchema)(it, valid); +} +function schemaCxtHasRules({ schema, self }) { + if (typeof schema == "boolean") + return !schema; + for (const key in schema) + if (self.RULES.all[key]) + return true; + return false; +} +function isSchemaObj(it) { + return typeof it.schema != "boolean"; +} +function subSchemaObjCode(it, valid) { + const { schema, gen, opts } = it; + if (opts.$comment && schema.$comment) + commentKeyword(it); + updateContext(it); + checkAsyncSchema(it); + const errsCount = gen.const("_errs", names_1.default.errors); + typeAndKeywords(it, errsCount); + // TODO var + gen.var(valid, (0, codegen_1._) `${errsCount} === ${names_1.default.errors}`); +} +function checkKeywords(it) { + (0, util_1.checkUnknownRules)(it); + checkRefsAndKeywords(it); +} +function typeAndKeywords(it, errsCount) { + if (it.opts.jtd) + return schemaKeywords(it, [], false, errsCount); + const types = (0, dataType_1.getSchemaTypes)(it.schema); + const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it, types); + schemaKeywords(it, types, !checkedTypes, errsCount); +} +function checkRefsAndKeywords(it) { + const { schema, errSchemaPath, opts, self } = it; + if (schema.$ref && opts.ignoreKeywordsWithRef && (0, util_1.schemaHasRulesButRef)(schema, self.RULES)) { + self.logger.warn(`$ref: keywords ignored in schema at path "${errSchemaPath}"`); + } +} +function checkNoDefault(it) { + const { schema, opts } = it; + if (schema.default !== undefined && opts.useDefaults && opts.strictSchema) { + (0, util_1.checkStrictMode)(it, "default is ignored in the schema root"); + } +} +function updateContext(it) { + const schId = it.schema[it.opts.schemaId]; + if (schId) + it.baseId = (0, resolve_1.resolveUrl)(it.opts.uriResolver, it.baseId, schId); +} +function checkAsyncSchema(it) { + if (it.schema.$async && !it.schemaEnv.$async) + throw new Error("async schema in sync schema"); +} +function commentKeyword({ gen, schemaEnv, schema, errSchemaPath, opts }) { + const msg = schema.$comment; + if (opts.$comment === true) { + gen.code((0, codegen_1._) `${names_1.default.self}.logger.log(${msg})`); + } + else if (typeof opts.$comment == "function") { + const schemaPath = (0, codegen_1.str) `${errSchemaPath}/$comment`; + const rootName = gen.scopeValue("root", { ref: schemaEnv.root }); + gen.code((0, codegen_1._) `${names_1.default.self}.opts.$comment(${msg}, ${schemaPath}, ${rootName}.schema)`); + } +} +function returnResults(it) { + const { gen, schemaEnv, validateName, ValidationError, opts } = it; + if (schemaEnv.$async) { + // TODO assign unevaluated + gen.if((0, codegen_1._) `${names_1.default.errors} === 0`, () => gen.return(names_1.default.data), () => gen.throw((0, codegen_1._) `new ${ValidationError}(${names_1.default.vErrors})`)); + } + else { + gen.assign((0, codegen_1._) `${validateName}.errors`, names_1.default.vErrors); + if (opts.unevaluated) + assignEvaluated(it); + gen.return((0, codegen_1._) `${names_1.default.errors} === 0`); + } +} +function assignEvaluated({ gen, evaluated, props, items }) { + if (props instanceof codegen_1.Name) + gen.assign((0, codegen_1._) `${evaluated}.props`, props); + if (items instanceof codegen_1.Name) + gen.assign((0, codegen_1._) `${evaluated}.items`, items); +} +function schemaKeywords(it, types, typeErrors, errsCount) { + const { gen, schema, data, allErrors, opts, self } = it; + const { RULES } = self; + if (schema.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema, RULES))) { + gen.block(() => keywordCode(it, "$ref", RULES.all.$ref.definition)); // TODO typecast + return; + } + if (!opts.jtd) + checkStrictTypes(it, types); + gen.block(() => { + for (const group of RULES.rules) + groupKeywords(group); + groupKeywords(RULES.post); + }); + function groupKeywords(group) { + if (!(0, applicability_1.shouldUseGroup)(schema, group)) + return; + if (group.type) { + gen.if((0, dataType_2.checkDataType)(group.type, data, opts.strictNumbers)); + iterateKeywords(it, group); + if (types.length === 1 && types[0] === group.type && typeErrors) { + gen.else(); + (0, dataType_2.reportTypeError)(it); + } + gen.endIf(); + } + else { + iterateKeywords(it, group); + } + // TODO make it "ok" call? + if (!allErrors) + gen.if((0, codegen_1._) `${names_1.default.errors} === ${errsCount || 0}`); + } +} +function iterateKeywords(it, group) { + const { gen, schema, opts: { useDefaults }, } = it; + if (useDefaults) + (0, defaults_1.assignDefaults)(it, group.type); + gen.block(() => { + for (const rule of group.rules) { + if ((0, applicability_1.shouldUseRule)(schema, rule)) { + keywordCode(it, rule.keyword, rule.definition, group.type); + } + } + }); +} +function checkStrictTypes(it, types) { + if (it.schemaEnv.meta || !it.opts.strictTypes) + return; + checkContextTypes(it, types); + if (!it.opts.allowUnionTypes) + checkMultipleTypes(it, types); + checkKeywordTypes(it, it.dataTypes); +} +function checkContextTypes(it, types) { + if (!types.length) + return; + if (!it.dataTypes.length) { + it.dataTypes = types; + return; + } + types.forEach((t) => { + if (!includesType(it.dataTypes, t)) { + strictTypesError(it, `type "${t}" not allowed by context "${it.dataTypes.join(",")}"`); + } + }); + narrowSchemaTypes(it, types); +} +function checkMultipleTypes(it, ts) { + if (ts.length > 1 && !(ts.length === 2 && ts.includes("null"))) { + strictTypesError(it, "use allowUnionTypes to allow union type keyword"); + } +} +function checkKeywordTypes(it, ts) { + const rules = it.self.RULES.all; + for (const keyword in rules) { + const rule = rules[keyword]; + if (typeof rule == "object" && (0, applicability_1.shouldUseRule)(it.schema, rule)) { + const { type } = rule.definition; + if (type.length && !type.some((t) => hasApplicableType(ts, t))) { + strictTypesError(it, `missing type "${type.join(",")}" for keyword "${keyword}"`); + } + } + } +} +function hasApplicableType(schTs, kwdT) { + return schTs.includes(kwdT) || (kwdT === "number" && schTs.includes("integer")); +} +function includesType(ts, t) { + return ts.includes(t) || (t === "integer" && ts.includes("number")); +} +function narrowSchemaTypes(it, withTypes) { + const ts = []; + for (const t of it.dataTypes) { + if (includesType(withTypes, t)) + ts.push(t); + else if (withTypes.includes("integer") && t === "number") + ts.push("integer"); + } + it.dataTypes = ts; +} +function strictTypesError(it, msg) { + const schemaPath = it.schemaEnv.baseId + it.errSchemaPath; + msg += ` at "${schemaPath}" (strictTypes)`; + (0, util_1.checkStrictMode)(it, msg, it.opts.strictTypes); +} +class KeywordCxt { + constructor(it, def, keyword) { + (0, keyword_1.validateKeywordUsage)(it, def, keyword); + this.gen = it.gen; + this.allErrors = it.allErrors; + this.keyword = keyword; + this.data = it.data; + this.schema = it.schema[keyword]; + this.$data = def.$data && it.opts.$data && this.schema && this.schema.$data; + this.schemaValue = (0, util_1.schemaRefOrVal)(it, this.schema, keyword, this.$data); + this.schemaType = def.schemaType; + this.parentSchema = it.schema; + this.params = {}; + this.it = it; + this.def = def; + if (this.$data) { + this.schemaCode = it.gen.const("vSchema", getData(this.$data, it)); + } + else { + this.schemaCode = this.schemaValue; + if (!(0, keyword_1.validSchemaType)(this.schema, def.schemaType, def.allowUndefined)) { + throw new Error(`${keyword} value must be ${JSON.stringify(def.schemaType)}`); + } + } + if ("code" in def ? def.trackErrors : def.errors !== false) { + this.errsCount = it.gen.const("_errs", names_1.default.errors); + } + } + result(condition, successAction, failAction) { + this.failResult((0, codegen_1.not)(condition), successAction, failAction); + } + failResult(condition, successAction, failAction) { + this.gen.if(condition); + if (failAction) + failAction(); + else + this.error(); + if (successAction) { + this.gen.else(); + successAction(); + if (this.allErrors) + this.gen.endIf(); + } + else { + if (this.allErrors) + this.gen.endIf(); + else + this.gen.else(); + } + } + pass(condition, failAction) { + this.failResult((0, codegen_1.not)(condition), undefined, failAction); + } + fail(condition) { + if (condition === undefined) { + this.error(); + if (!this.allErrors) + this.gen.if(false); // this branch will be removed by gen.optimize + return; + } + this.gen.if(condition); + this.error(); + if (this.allErrors) + this.gen.endIf(); + else + this.gen.else(); + } + fail$data(condition) { + if (!this.$data) + return this.fail(condition); + const { schemaCode } = this; + this.fail((0, codegen_1._) `${schemaCode} !== undefined && (${(0, codegen_1.or)(this.invalid$data(), condition)})`); + } + error(append, errorParams, errorPaths) { + if (errorParams) { + this.setParams(errorParams); + this._error(append, errorPaths); + this.setParams({}); + return; + } + this._error(append, errorPaths); + } + _error(append, errorPaths) { + ; + (append ? errors_1.reportExtraError : errors_1.reportError)(this, this.def.error, errorPaths); + } + $dataError() { + (0, errors_1.reportError)(this, this.def.$dataError || errors_1.keyword$DataError); + } + reset() { + if (this.errsCount === undefined) + throw new Error('add "trackErrors" to keyword definition'); + (0, errors_1.resetErrorsCount)(this.gen, this.errsCount); + } + ok(cond) { + if (!this.allErrors) + this.gen.if(cond); + } + setParams(obj, assign) { + if (assign) + Object.assign(this.params, obj); + else + this.params = obj; + } + block$data(valid, codeBlock, $dataValid = codegen_1.nil) { + this.gen.block(() => { + this.check$data(valid, $dataValid); + codeBlock(); + }); + } + check$data(valid = codegen_1.nil, $dataValid = codegen_1.nil) { + if (!this.$data) + return; + const { gen, schemaCode, schemaType, def } = this; + gen.if((0, codegen_1.or)((0, codegen_1._) `${schemaCode} === undefined`, $dataValid)); + if (valid !== codegen_1.nil) + gen.assign(valid, true); + if (schemaType.length || def.validateSchema) { + gen.elseIf(this.invalid$data()); + this.$dataError(); + if (valid !== codegen_1.nil) + gen.assign(valid, false); + } + gen.else(); + } + invalid$data() { + const { gen, schemaCode, schemaType, def, it } = this; + return (0, codegen_1.or)(wrong$DataType(), invalid$DataSchema()); + function wrong$DataType() { + if (schemaType.length) { + /* istanbul ignore if */ + if (!(schemaCode instanceof codegen_1.Name)) + throw new Error("ajv implementation error"); + const st = Array.isArray(schemaType) ? schemaType : [schemaType]; + return (0, codegen_1._) `${(0, dataType_2.checkDataTypes)(st, schemaCode, it.opts.strictNumbers, dataType_2.DataType.Wrong)}`; + } + return codegen_1.nil; + } + function invalid$DataSchema() { + if (def.validateSchema) { + const validateSchemaRef = gen.scopeValue("validate$data", { ref: def.validateSchema }); // TODO value.code for standalone + return (0, codegen_1._) `!${validateSchemaRef}(${schemaCode})`; + } + return codegen_1.nil; + } + } + subschema(appl, valid) { + const subschema = (0, subschema_1.getSubschema)(this.it, appl); + (0, subschema_1.extendSubschemaData)(subschema, this.it, appl); + (0, subschema_1.extendSubschemaMode)(subschema, appl); + const nextContext = { ...this.it, ...subschema, items: undefined, props: undefined }; + subschemaCode(nextContext, valid); + return nextContext; + } + mergeEvaluated(schemaCxt, toName) { + const { it, gen } = this; + if (!it.opts.unevaluated) + return; + if (it.props !== true && schemaCxt.props !== undefined) { + it.props = util_1.mergeEvaluated.props(gen, schemaCxt.props, it.props, toName); + } + if (it.items !== true && schemaCxt.items !== undefined) { + it.items = util_1.mergeEvaluated.items(gen, schemaCxt.items, it.items, toName); + } + } + mergeValidEvaluated(schemaCxt, valid) { + const { it, gen } = this; + if (it.opts.unevaluated && (it.props !== true || it.items !== true)) { + gen.if(valid, () => this.mergeEvaluated(schemaCxt, codegen_1.Name)); + return true; + } + } +} +exports.KeywordCxt = KeywordCxt; +function keywordCode(it, keyword, def, ruleType) { + const cxt = new KeywordCxt(it, def, keyword); + if ("code" in def) { + def.code(cxt, ruleType); + } + else if (cxt.$data && def.validate) { + (0, keyword_1.funcKeywordCode)(cxt, def); + } + else if ("macro" in def) { + (0, keyword_1.macroKeywordCode)(cxt, def); + } + else if (def.compile || def.validate) { + (0, keyword_1.funcKeywordCode)(cxt, def); + } +} +const JSON_POINTER = /^\/(?:[^~]|~0|~1)*$/; +const RELATIVE_JSON_POINTER = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/; +function getData($data, { dataLevel, dataNames, dataPathArr }) { + let jsonPointer; + let data; + if ($data === "") + return names_1.default.rootData; + if ($data[0] === "/") { + if (!JSON_POINTER.test($data)) + throw new Error(`Invalid JSON-pointer: ${$data}`); + jsonPointer = $data; + data = names_1.default.rootData; + } + else { + const matches = RELATIVE_JSON_POINTER.exec($data); + if (!matches) + throw new Error(`Invalid JSON-pointer: ${$data}`); + const up = +matches[1]; + jsonPointer = matches[2]; + if (jsonPointer === "#") { + if (up >= dataLevel) + throw new Error(errorMsg("property/index", up)); + return dataPathArr[dataLevel - up]; + } + if (up > dataLevel) + throw new Error(errorMsg("data", up)); + data = dataNames[dataLevel - up]; + if (!jsonPointer) + return data; + } + let expr = data; + const segments = jsonPointer.split("/"); + for (const segment of segments) { + if (segment) { + data = (0, codegen_1._) `${data}${(0, codegen_1.getProperty)((0, util_1.unescapeJsonPointer)(segment))}`; + expr = (0, codegen_1._) `${expr} && ${data}`; + } + } + return expr; + function errorMsg(pointerType, up) { + return `Cannot access ${pointerType} ${up} levels up, current level is ${dataLevel}`; + } +} +exports.getData = getData; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/index.js.map b/website/node_modules/ajv/dist/compile/validate/index.js.map new file mode 100644 index 00000000..873d09b5 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/compile/validate/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/keyword.d.ts b/website/node_modules/ajv/dist/compile/validate/keyword.d.ts new file mode 100644 index 00000000..d15cee87 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/keyword.d.ts @@ -0,0 +1,8 @@ +import type { KeywordCxt } from "."; +import type { AddedKeywordDefinition, MacroKeywordDefinition, FuncKeywordDefinition } from "../../types"; +import type { SchemaObjCxt } from ".."; +import type { JSONType } from "../rules"; +export declare function macroKeywordCode(cxt: KeywordCxt, def: MacroKeywordDefinition): void; +export declare function funcKeywordCode(cxt: KeywordCxt, def: FuncKeywordDefinition): void; +export declare function validSchemaType(schema: unknown, schemaType: JSONType[], allowUndefined?: boolean): boolean; +export declare function validateKeywordUsage({ schema, opts, self, errSchemaPath }: SchemaObjCxt, def: AddedKeywordDefinition, keyword: string): void; diff --git a/website/node_modules/ajv/dist/compile/validate/keyword.js b/website/node_modules/ajv/dist/compile/validate/keyword.js new file mode 100644 index 00000000..1109d3a4 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/keyword.js @@ -0,0 +1,124 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.validateKeywordUsage = exports.validSchemaType = exports.funcKeywordCode = exports.macroKeywordCode = void 0; +const codegen_1 = require("../codegen"); +const names_1 = require("../names"); +const code_1 = require("../../vocabularies/code"); +const errors_1 = require("../errors"); +function macroKeywordCode(cxt, def) { + const { gen, keyword, schema, parentSchema, it } = cxt; + const macroSchema = def.macro.call(it.self, schema, parentSchema, it); + const schemaRef = useKeyword(gen, keyword, macroSchema); + if (it.opts.validateSchema !== false) + it.self.validateSchema(macroSchema, true); + const valid = gen.name("valid"); + cxt.subschema({ + schema: macroSchema, + schemaPath: codegen_1.nil, + errSchemaPath: `${it.errSchemaPath}/${keyword}`, + topSchemaRef: schemaRef, + compositeRule: true, + }, valid); + cxt.pass(valid, () => cxt.error(true)); +} +exports.macroKeywordCode = macroKeywordCode; +function funcKeywordCode(cxt, def) { + var _a; + const { gen, keyword, schema, parentSchema, $data, it } = cxt; + checkAsyncKeyword(it, def); + const validate = !$data && def.compile ? def.compile.call(it.self, schema, parentSchema, it) : def.validate; + const validateRef = useKeyword(gen, keyword, validate); + const valid = gen.let("valid"); + cxt.block$data(valid, validateKeyword); + cxt.ok((_a = def.valid) !== null && _a !== void 0 ? _a : valid); + function validateKeyword() { + if (def.errors === false) { + assignValid(); + if (def.modifying) + modifyData(cxt); + reportErrs(() => cxt.error()); + } + else { + const ruleErrs = def.async ? validateAsync() : validateSync(); + if (def.modifying) + modifyData(cxt); + reportErrs(() => addErrs(cxt, ruleErrs)); + } + } + function validateAsync() { + const ruleErrs = gen.let("ruleErrs", null); + gen.try(() => assignValid((0, codegen_1._) `await `), (e) => gen.assign(valid, false).if((0, codegen_1._) `${e} instanceof ${it.ValidationError}`, () => gen.assign(ruleErrs, (0, codegen_1._) `${e}.errors`), () => gen.throw(e))); + return ruleErrs; + } + function validateSync() { + const validateErrs = (0, codegen_1._) `${validateRef}.errors`; + gen.assign(validateErrs, null); + assignValid(codegen_1.nil); + return validateErrs; + } + function assignValid(_await = def.async ? (0, codegen_1._) `await ` : codegen_1.nil) { + const passCxt = it.opts.passContext ? names_1.default.this : names_1.default.self; + const passSchema = !(("compile" in def && !$data) || def.schema === false); + gen.assign(valid, (0, codegen_1._) `${_await}${(0, code_1.callValidateCode)(cxt, validateRef, passCxt, passSchema)}`, def.modifying); + } + function reportErrs(errors) { + var _a; + gen.if((0, codegen_1.not)((_a = def.valid) !== null && _a !== void 0 ? _a : valid), errors); + } +} +exports.funcKeywordCode = funcKeywordCode; +function modifyData(cxt) { + const { gen, data, it } = cxt; + gen.if(it.parentData, () => gen.assign(data, (0, codegen_1._) `${it.parentData}[${it.parentDataProperty}]`)); +} +function addErrs(cxt, errs) { + const { gen } = cxt; + gen.if((0, codegen_1._) `Array.isArray(${errs})`, () => { + gen + .assign(names_1.default.vErrors, (0, codegen_1._) `${names_1.default.vErrors} === null ? ${errs} : ${names_1.default.vErrors}.concat(${errs})`) + .assign(names_1.default.errors, (0, codegen_1._) `${names_1.default.vErrors}.length`); + (0, errors_1.extendErrors)(cxt); + }, () => cxt.error()); +} +function checkAsyncKeyword({ schemaEnv }, def) { + if (def.async && !schemaEnv.$async) + throw new Error("async keyword in sync schema"); +} +function useKeyword(gen, keyword, result) { + if (result === undefined) + throw new Error(`keyword "${keyword}" failed to compile`); + return gen.scopeValue("keyword", typeof result == "function" ? { ref: result } : { ref: result, code: (0, codegen_1.stringify)(result) }); +} +function validSchemaType(schema, schemaType, allowUndefined = false) { + // TODO add tests + return (!schemaType.length || + schemaType.some((st) => st === "array" + ? Array.isArray(schema) + : st === "object" + ? schema && typeof schema == "object" && !Array.isArray(schema) + : typeof schema == st || (allowUndefined && typeof schema == "undefined"))); +} +exports.validSchemaType = validSchemaType; +function validateKeywordUsage({ schema, opts, self, errSchemaPath }, def, keyword) { + /* istanbul ignore if */ + if (Array.isArray(def.keyword) ? !def.keyword.includes(keyword) : def.keyword !== keyword) { + throw new Error("ajv implementation error"); + } + const deps = def.dependencies; + if (deps === null || deps === void 0 ? void 0 : deps.some((kwd) => !Object.prototype.hasOwnProperty.call(schema, kwd))) { + throw new Error(`parent schema must have dependencies of ${keyword}: ${deps.join(",")}`); + } + if (def.validateSchema) { + const valid = def.validateSchema(schema[keyword]); + if (!valid) { + const msg = `keyword "${keyword}" value is invalid at path "${errSchemaPath}": ` + + self.errorsText(def.validateSchema.errors); + if (opts.validateSchema === "log") + self.logger.error(msg); + else + throw new Error(msg); + } + } +} +exports.validateKeywordUsage = validateKeywordUsage; +//# sourceMappingURL=keyword.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/keyword.js.map b/website/node_modules/ajv/dist/compile/validate/keyword.js.map new file mode 100644 index 00000000..10d716d7 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/keyword.js.map @@ -0,0 +1 @@ +{"version":3,"file":"keyword.js","sourceRoot":"","sources":["../../../lib/compile/validate/keyword.ts"],"names":[],"mappings":";;;AAUA,wCAAsE;AACtE,oCAAwB;AAExB,kDAAwD;AACxD,sCAAsC;AAItC,SAAgB,gBAAgB,CAAC,GAAe,EAAE,GAA2B;IAC3E,MAAM,EAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;IACpD,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC,CAAA;IACrE,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,CAAC,CAAA;IACvD,IAAI,EAAE,CAAC,IAAI,CAAC,cAAc,KAAK,KAAK;QAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,CAAA;IAE/E,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC/B,GAAG,CAAC,SAAS,CACX;QACE,MAAM,EAAE,WAAW;QACnB,UAAU,EAAE,aAAG;QACf,aAAa,EAAE,GAAG,EAAE,CAAC,aAAa,IAAI,OAAO,EAAE;QAC/C,YAAY,EAAE,SAAS;QACvB,aAAa,EAAE,IAAI;KACpB,EACD,KAAK,CACN,CAAA;IACD,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;AACxC,CAAC;AAlBD,4CAkBC;AAED,SAAgB,eAAe,CAAC,GAAe,EAAE,GAA0B;;IACzE,MAAM,EAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;IAC3D,iBAAiB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAA;IAC1B,MAAM,QAAQ,GACZ,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAA;IAC5F,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;IACtD,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC9B,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,eAAe,CAAC,CAAA;IACtC,GAAG,CAAC,EAAE,CAAC,MAAA,GAAG,CAAC,KAAK,mCAAI,KAAK,CAAC,CAAA;IAE1B,SAAS,eAAe;QACtB,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE;YACxB,WAAW,EAAE,CAAA;YACb,IAAI,GAAG,CAAC,SAAS;gBAAE,UAAU,CAAC,GAAG,CAAC,CAAA;YAClC,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAA;SAC9B;aAAM;YACL,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,CAAA;YAC7D,IAAI,GAAG,CAAC,SAAS;gBAAE,UAAU,CAAC,GAAG,CAAC,CAAA;YAClC,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAA;SACzC;IACH,CAAC;IAED,SAAS,aAAa;QACpB,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;QAC1C,GAAG,CAAC,GAAG,CACL,GAAG,EAAE,CAAC,WAAW,CAAC,IAAA,WAAC,EAAA,QAAQ,CAAC,EAC5B,CAAC,CAAC,EAAE,EAAE,CACJ,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CACzB,IAAA,WAAC,EAAA,GAAG,CAAC,eAAe,EAAE,CAAC,eAAuB,EAAE,EAChD,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAA,WAAC,EAAA,GAAG,CAAC,SAAS,CAAC,EAC1C,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CACnB,CACJ,CAAA;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,SAAS,YAAY;QACnB,MAAM,YAAY,GAAG,IAAA,WAAC,EAAA,GAAG,WAAW,SAAS,CAAA;QAC7C,GAAG,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,CAAA;QAC9B,WAAW,CAAC,aAAG,CAAC,CAAA;QAChB,OAAO,YAAY,CAAA;IACrB,CAAC;IAED,SAAS,WAAW,CAAC,SAAe,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAA,WAAC,EAAA,QAAQ,CAAC,CAAC,CAAC,aAAG;QAC7D,MAAM,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,eAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAC,CAAC,IAAI,CAAA;QACrD,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,CAAC,CAAA;QAC1E,GAAG,CAAC,MAAM,CACR,KAAK,EACL,IAAA,WAAC,EAAA,GAAG,MAAM,GAAG,IAAA,uBAAgB,EAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,EACtE,GAAG,CAAC,SAAS,CACd,CAAA;IACH,CAAC;IAED,SAAS,UAAU,CAAC,MAAkB;;QACpC,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,MAAA,GAAG,CAAC,KAAK,mCAAI,KAAK,CAAC,EAAE,MAAM,CAAC,CAAA;IACzC,CAAC;AACH,CAAC;AAxDD,0CAwDC;AAED,SAAS,UAAU,CAAC,GAAe;IACjC,MAAM,EAAC,GAAG,EAAE,IAAI,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;IAC3B,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAA,WAAC,EAAA,GAAG,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC,kBAAkB,GAAG,CAAC,CAAC,CAAA;AAC9F,CAAC;AAED,SAAS,OAAO,CAAC,GAAe,EAAE,IAAU;IAC1C,MAAM,EAAC,GAAG,EAAC,GAAG,GAAG,CAAA;IACjB,GAAG,CAAC,EAAE,CACJ,IAAA,WAAC,EAAA,iBAAiB,IAAI,GAAG,EACzB,GAAG,EAAE;QACH,GAAG;aACA,MAAM,CAAC,eAAC,CAAC,OAAO,EAAE,IAAA,WAAC,EAAA,GAAG,eAAC,CAAC,OAAO,eAAe,IAAI,MAAM,eAAC,CAAC,OAAO,WAAW,IAAI,GAAG,CAAC;aACpF,MAAM,CAAC,eAAC,CAAC,MAAM,EAAE,IAAA,WAAC,EAAA,GAAG,eAAC,CAAC,OAAO,SAAS,CAAC,CAAA;QAC3C,IAAA,qBAAY,EAAC,GAAG,CAAC,CAAA;IACnB,CAAC,EACD,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAClB,CAAA;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,EAAC,SAAS,EAAe,EAAE,GAA0B;IAC9E,IAAI,GAAG,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;AACrF,CAAC;AAED,SAAS,UAAU,CAAC,GAAY,EAAE,OAAe,EAAE,MAAiC;IAClF,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,YAAY,OAAO,qBAAqB,CAAC,CAAA;IACnF,OAAO,GAAG,CAAC,UAAU,CACnB,SAAS,EACT,OAAO,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC,EAAC,GAAG,EAAE,MAAM,EAAC,CAAC,CAAC,CAAC,EAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAA,mBAAS,EAAC,MAAM,CAAC,EAAC,CACrF,CAAA;AACH,CAAC;AAED,SAAgB,eAAe,CAC7B,MAAe,EACf,UAAsB,EACtB,cAAc,GAAG,KAAK;IAEtB,iBAAiB;IACjB,OAAO,CACL,CAAC,UAAU,CAAC,MAAM;QAClB,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CACrB,EAAE,KAAK,OAAO;YACZ,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YACvB,CAAC,CAAC,EAAE,KAAK,QAAQ;gBACjB,CAAC,CAAC,MAAM,IAAI,OAAO,MAAM,IAAI,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAC/D,CAAC,CAAC,OAAO,MAAM,IAAI,EAAE,IAAI,CAAC,cAAc,IAAI,OAAO,MAAM,IAAI,WAAW,CAAC,CAC5E,CACF,CAAA;AACH,CAAC;AAhBD,0CAgBC;AAED,SAAgB,oBAAoB,CAClC,EAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAe,EACjD,GAA2B,EAC3B,OAAe;IAEf,wBAAwB;IACxB,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,KAAK,OAAO,EAAE;QACzF,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;KAC5C;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAA;IAC7B,IAAI,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE;QAC3E,MAAM,IAAI,KAAK,CAAC,2CAA2C,OAAO,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;KACzF;IAED,IAAI,GAAG,CAAC,cAAc,EAAE;QACtB,MAAM,KAAK,GAAG,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;QACjD,IAAI,CAAC,KAAK,EAAE;YACV,MAAM,GAAG,GACP,YAAY,OAAO,+BAA+B,aAAa,KAAK;gBACpE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;YAC5C,IAAI,IAAI,CAAC,cAAc,KAAK,KAAK;gBAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;;gBACpD,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,CAAA;SAC1B;KACF;AACH,CAAC;AAzBD,oDAyBC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/subschema.d.ts b/website/node_modules/ajv/dist/compile/validate/subschema.d.ts new file mode 100644 index 00000000..b92785f2 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/subschema.d.ts @@ -0,0 +1,47 @@ +import type { AnySchema } from "../../types"; +import type { SchemaObjCxt } from ".."; +import { Code, Name } from "../codegen"; +import { Type } from "../util"; +import type { JSONType } from "../rules"; +export interface SubschemaContext { + schema: AnySchema; + schemaPath: Code; + errSchemaPath: string; + topSchemaRef?: Code; + errorPath?: Code; + dataLevel?: number; + dataTypes?: JSONType[]; + data?: Name; + parentData?: Name; + parentDataProperty?: Code | number; + dataNames?: Name[]; + dataPathArr?: (Code | number)[]; + propertyName?: Name; + jtdDiscriminator?: string; + jtdMetadata?: boolean; + compositeRule?: true; + createErrors?: boolean; + allErrors?: boolean; +} +export type SubschemaArgs = Partial<{ + keyword: string; + schemaProp: string | number; + schema: AnySchema; + schemaPath: Code; + errSchemaPath: string; + topSchemaRef: Code; + data: Name | Code; + dataProp: Code | string | number; + dataTypes: JSONType[]; + definedProperties: Set; + propertyName: Name; + dataPropType: Type; + jtdDiscriminator: string; + jtdMetadata: boolean; + compositeRule: true; + createErrors: boolean; + allErrors: boolean; +}>; +export declare function getSubschema(it: SchemaObjCxt, { keyword, schemaProp, schema, schemaPath, errSchemaPath, topSchemaRef }: SubschemaArgs): SubschemaContext; +export declare function extendSubschemaData(subschema: SubschemaContext, it: SchemaObjCxt, { dataProp, dataPropType: dpType, data, dataTypes, propertyName }: SubschemaArgs): void; +export declare function extendSubschemaMode(subschema: SubschemaContext, { jtdDiscriminator, jtdMetadata, compositeRule, createErrors, allErrors }: SubschemaArgs): void; diff --git a/website/node_modules/ajv/dist/compile/validate/subschema.js b/website/node_modules/ajv/dist/compile/validate/subschema.js new file mode 100644 index 00000000..9de28286 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/subschema.js @@ -0,0 +1,81 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.extendSubschemaMode = exports.extendSubschemaData = exports.getSubschema = void 0; +const codegen_1 = require("../codegen"); +const util_1 = require("../util"); +function getSubschema(it, { keyword, schemaProp, schema, schemaPath, errSchemaPath, topSchemaRef }) { + if (keyword !== undefined && schema !== undefined) { + throw new Error('both "keyword" and "schema" passed, only one allowed'); + } + if (keyword !== undefined) { + const sch = it.schema[keyword]; + return schemaProp === undefined + ? { + schema: sch, + schemaPath: (0, codegen_1._) `${it.schemaPath}${(0, codegen_1.getProperty)(keyword)}`, + errSchemaPath: `${it.errSchemaPath}/${keyword}`, + } + : { + schema: sch[schemaProp], + schemaPath: (0, codegen_1._) `${it.schemaPath}${(0, codegen_1.getProperty)(keyword)}${(0, codegen_1.getProperty)(schemaProp)}`, + errSchemaPath: `${it.errSchemaPath}/${keyword}/${(0, util_1.escapeFragment)(schemaProp)}`, + }; + } + if (schema !== undefined) { + if (schemaPath === undefined || errSchemaPath === undefined || topSchemaRef === undefined) { + throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"'); + } + return { + schema, + schemaPath, + topSchemaRef, + errSchemaPath, + }; + } + throw new Error('either "keyword" or "schema" must be passed'); +} +exports.getSubschema = getSubschema; +function extendSubschemaData(subschema, it, { dataProp, dataPropType: dpType, data, dataTypes, propertyName }) { + if (data !== undefined && dataProp !== undefined) { + throw new Error('both "data" and "dataProp" passed, only one allowed'); + } + const { gen } = it; + if (dataProp !== undefined) { + const { errorPath, dataPathArr, opts } = it; + const nextData = gen.let("data", (0, codegen_1._) `${it.data}${(0, codegen_1.getProperty)(dataProp)}`, true); + dataContextProps(nextData); + subschema.errorPath = (0, codegen_1.str) `${errorPath}${(0, util_1.getErrorPath)(dataProp, dpType, opts.jsPropertySyntax)}`; + subschema.parentDataProperty = (0, codegen_1._) `${dataProp}`; + subschema.dataPathArr = [...dataPathArr, subschema.parentDataProperty]; + } + if (data !== undefined) { + const nextData = data instanceof codegen_1.Name ? data : gen.let("data", data, true); // replaceable if used once? + dataContextProps(nextData); + if (propertyName !== undefined) + subschema.propertyName = propertyName; + // TODO something is possibly wrong here with not changing parentDataProperty and not appending dataPathArr + } + if (dataTypes) + subschema.dataTypes = dataTypes; + function dataContextProps(_nextData) { + subschema.data = _nextData; + subschema.dataLevel = it.dataLevel + 1; + subschema.dataTypes = []; + it.definedProperties = new Set(); + subschema.parentData = it.data; + subschema.dataNames = [...it.dataNames, _nextData]; + } +} +exports.extendSubschemaData = extendSubschemaData; +function extendSubschemaMode(subschema, { jtdDiscriminator, jtdMetadata, compositeRule, createErrors, allErrors }) { + if (compositeRule !== undefined) + subschema.compositeRule = compositeRule; + if (createErrors !== undefined) + subschema.createErrors = createErrors; + if (allErrors !== undefined) + subschema.allErrors = allErrors; + subschema.jtdDiscriminator = jtdDiscriminator; // not inherited + subschema.jtdMetadata = jtdMetadata; // not inherited +} +exports.extendSubschemaMode = extendSubschemaMode; +//# sourceMappingURL=subschema.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/compile/validate/subschema.js.map b/website/node_modules/ajv/dist/compile/validate/subschema.js.map new file mode 100644 index 00000000..a1b84d37 --- /dev/null +++ b/website/node_modules/ajv/dist/compile/validate/subschema.js.map @@ -0,0 +1 @@ +{"version":3,"file":"subschema.js","sourceRoot":"","sources":["../../../lib/compile/validate/subschema.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/core.d.ts b/website/node_modules/ajv/dist/core.d.ts new file mode 100644 index 00000000..4591ed9e --- /dev/null +++ b/website/node_modules/ajv/dist/core.d.ts @@ -0,0 +1,173 @@ +export { Format, FormatDefinition, AsyncFormatDefinition, KeywordDefinition, KeywordErrorDefinition, CodeKeywordDefinition, MacroKeywordDefinition, FuncKeywordDefinition, Vocabulary, Schema, SchemaObject, AnySchemaObject, AsyncSchema, AnySchema, ValidateFunction, AsyncValidateFunction, AnyValidateFunction, ErrorObject, ErrorNoParams, } from "./types"; +export { SchemaCxt, SchemaObjCxt } from "./compile"; +export interface Plugin { + (ajv: Ajv, options?: Opts): Ajv; + [prop: string]: any; +} +export { KeywordCxt } from "./compile/validate"; +export { DefinedError } from "./vocabularies/errors"; +export { JSONType } from "./compile/rules"; +export { JSONSchemaType } from "./types/json-schema"; +export { JTDSchemaType, SomeJTDSchemaType, JTDDataType } from "./types/jtd-schema"; +export { _, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions } from "./compile/codegen"; +import type { Schema, AnySchema, AnySchemaObject, SchemaObject, AsyncSchema, Vocabulary, KeywordDefinition, AddedKeywordDefinition, AnyValidateFunction, ValidateFunction, AsyncValidateFunction, ErrorObject, Format, AddedFormat, RegExpEngine, UriResolver } from "./types"; +import type { JSONSchemaType } from "./types/json-schema"; +import type { JTDSchemaType, SomeJTDSchemaType, JTDDataType } from "./types/jtd-schema"; +import ValidationError from "./runtime/validation_error"; +import MissingRefError from "./compile/ref_error"; +import { ValidationRules } from "./compile/rules"; +import { SchemaEnv } from "./compile"; +import { Code, ValueScope } from "./compile/codegen"; +export type Options = CurrentOptions & DeprecatedOptions; +export interface CurrentOptions { + strict?: boolean | "log"; + strictSchema?: boolean | "log"; + strictNumbers?: boolean | "log"; + strictTypes?: boolean | "log"; + strictTuples?: boolean | "log"; + strictRequired?: boolean | "log"; + allowMatchingProperties?: boolean; + allowUnionTypes?: boolean; + validateFormats?: boolean; + $data?: boolean; + allErrors?: boolean; + verbose?: boolean; + discriminator?: boolean; + unicodeRegExp?: boolean; + timestamp?: "string" | "date"; + parseDate?: boolean; + allowDate?: boolean; + $comment?: true | ((comment: string, schemaPath?: string, rootSchema?: AnySchemaObject) => unknown); + formats?: { + [Name in string]?: Format; + }; + keywords?: Vocabulary; + schemas?: AnySchema[] | { + [Key in string]?: AnySchema; + }; + logger?: Logger | false; + loadSchema?: (uri: string) => Promise; + removeAdditional?: boolean | "all" | "failing"; + useDefaults?: boolean | "empty"; + coerceTypes?: boolean | "array"; + next?: boolean; + unevaluated?: boolean; + dynamicRef?: boolean; + schemaId?: "id" | "$id"; + jtd?: boolean; + meta?: SchemaObject | boolean; + defaultMeta?: string | AnySchemaObject; + validateSchema?: boolean | "log"; + addUsedSchema?: boolean; + inlineRefs?: boolean | number; + passContext?: boolean; + loopRequired?: number; + loopEnum?: number; + ownProperties?: boolean; + multipleOfPrecision?: number; + int32range?: boolean; + messages?: boolean; + code?: CodeOptions; + uriResolver?: UriResolver; +} +export interface CodeOptions { + es5?: boolean; + esm?: boolean; + lines?: boolean; + optimize?: boolean | number; + formats?: Code; + source?: boolean; + process?: (code: string, schema?: SchemaEnv) => string; + regExp?: RegExpEngine; +} +interface InstanceCodeOptions extends CodeOptions { + regExp: RegExpEngine; + optimize: number; +} +interface DeprecatedOptions { + /** @deprecated */ + ignoreKeywordsWithRef?: boolean; + /** @deprecated */ + jsPropertySyntax?: boolean; + /** @deprecated */ + unicode?: boolean; +} +type RequiredInstanceOptions = { + [K in "strictSchema" | "strictNumbers" | "strictTypes" | "strictTuples" | "strictRequired" | "inlineRefs" | "loopRequired" | "loopEnum" | "meta" | "messages" | "schemaId" | "addUsedSchema" | "validateSchema" | "validateFormats" | "int32range" | "unicodeRegExp" | "uriResolver"]: NonNullable; +} & { + code: InstanceCodeOptions; +}; +export type InstanceOptions = Options & RequiredInstanceOptions; +export interface Logger { + log(...args: unknown[]): unknown; + warn(...args: unknown[]): unknown; + error(...args: unknown[]): unknown; +} +export default class Ajv { + opts: InstanceOptions; + errors?: ErrorObject[] | null; + logger: Logger; + readonly scope: ValueScope; + readonly schemas: { + [Key in string]?: SchemaEnv; + }; + readonly refs: { + [Ref in string]?: SchemaEnv | string; + }; + readonly formats: { + [Name in string]?: AddedFormat; + }; + readonly RULES: ValidationRules; + readonly _compilations: Set; + private readonly _loading; + private readonly _cache; + private readonly _metaOpts; + static ValidationError: typeof ValidationError; + static MissingRefError: typeof MissingRefError; + constructor(opts?: Options); + _addVocabularies(): void; + _addDefaultMetaSchema(): void; + defaultMeta(): string | AnySchemaObject | undefined; + validate(schema: Schema | string, data: unknown): boolean; + validate(schemaKeyRef: AnySchema | string, data: unknown): boolean | Promise; + validate(schema: Schema | JSONSchemaType | string, data: unknown): data is T; + validate(schema: JTDSchemaType, data: unknown): data is T; + validate(schema: T, data: unknown): data is JTDDataType; + validate(schema: AsyncSchema, data: unknown | T): Promise; + validate(schemaKeyRef: AnySchema | string, data: unknown): data is T | Promise; + compile(schema: Schema | JSONSchemaType, _meta?: boolean): ValidateFunction; + compile(schema: JTDSchemaType, _meta?: boolean): ValidateFunction; + compile(schema: T, _meta?: boolean): ValidateFunction>; + compile(schema: AsyncSchema, _meta?: boolean): AsyncValidateFunction; + compile(schema: AnySchema, _meta?: boolean): AnyValidateFunction; + compileAsync(schema: SchemaObject | JSONSchemaType, _meta?: boolean): Promise>; + compileAsync(schema: JTDSchemaType, _meta?: boolean): Promise>; + compileAsync(schema: AsyncSchema, meta?: boolean): Promise>; + compileAsync(schema: AnySchemaObject, meta?: boolean): Promise>; + addSchema(schema: AnySchema | AnySchema[], // If array is passed, `key` will be ignored + key?: string, // Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`. + _meta?: boolean, // true if schema is a meta-schema. Used internally, addMetaSchema should be used instead. + _validateSchema?: boolean | "log"): Ajv; + addMetaSchema(schema: AnySchemaObject, key?: string, // schema key + _validateSchema?: boolean | "log"): Ajv; + validateSchema(schema: AnySchema, throwOrLogError?: boolean): boolean | Promise; + getSchema(keyRef: string): AnyValidateFunction | undefined; + removeSchema(schemaKeyRef?: AnySchema | string | RegExp): Ajv; + addVocabulary(definitions: Vocabulary): Ajv; + addKeyword(kwdOrDef: string | KeywordDefinition, def?: KeywordDefinition): Ajv; + getKeyword(keyword: string): AddedKeywordDefinition | boolean; + removeKeyword(keyword: string): Ajv; + addFormat(name: string, format: Format): Ajv; + errorsText(errors?: ErrorObject[] | null | undefined, // optional array of validation errors + { separator, dataVar }?: ErrorsTextOptions): string; + $dataMetaSchema(metaSchema: AnySchemaObject, keywordsJsonPointers: string[]): AnySchemaObject; + private _removeAllSchemas; + _addSchema(schema: AnySchema, meta?: boolean, baseId?: string, validateSchema?: boolean | "log", addSchema?: boolean): SchemaEnv; + private _checkUnique; + private _compileSchemaEnv; + private _compileMetaSchema; +} +export interface ErrorsTextOptions { + separator?: string; + dataVar?: string; +} diff --git a/website/node_modules/ajv/dist/core.js b/website/node_modules/ajv/dist/core.js new file mode 100644 index 00000000..84911161 --- /dev/null +++ b/website/node_modules/ajv/dist/core.js @@ -0,0 +1,617 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = void 0; +var validate_1 = require("./compile/validate"); +Object.defineProperty(exports, "KeywordCxt", { enumerable: true, get: function () { return validate_1.KeywordCxt; } }); +var codegen_1 = require("./compile/codegen"); +Object.defineProperty(exports, "_", { enumerable: true, get: function () { return codegen_1._; } }); +Object.defineProperty(exports, "str", { enumerable: true, get: function () { return codegen_1.str; } }); +Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return codegen_1.stringify; } }); +Object.defineProperty(exports, "nil", { enumerable: true, get: function () { return codegen_1.nil; } }); +Object.defineProperty(exports, "Name", { enumerable: true, get: function () { return codegen_1.Name; } }); +Object.defineProperty(exports, "CodeGen", { enumerable: true, get: function () { return codegen_1.CodeGen; } }); +const validation_error_1 = require("./runtime/validation_error"); +const ref_error_1 = require("./compile/ref_error"); +const rules_1 = require("./compile/rules"); +const compile_1 = require("./compile"); +const codegen_2 = require("./compile/codegen"); +const resolve_1 = require("./compile/resolve"); +const dataType_1 = require("./compile/validate/dataType"); +const util_1 = require("./compile/util"); +const $dataRefSchema = require("./refs/data.json"); +const uri_1 = require("./runtime/uri"); +const defaultRegExp = (str, flags) => new RegExp(str, flags); +defaultRegExp.code = "new RegExp"; +const META_IGNORE_OPTIONS = ["removeAdditional", "useDefaults", "coerceTypes"]; +const EXT_SCOPE_NAMES = new Set([ + "validate", + "serialize", + "parse", + "wrapper", + "root", + "schema", + "keyword", + "pattern", + "formats", + "validate$data", + "func", + "obj", + "Error", +]); +const removedOptions = { + errorDataPath: "", + format: "`validateFormats: false` can be used instead.", + nullable: '"nullable" keyword is supported by default.', + jsonPointers: "Deprecated jsPropertySyntax can be used instead.", + extendRefs: "Deprecated ignoreKeywordsWithRef can be used instead.", + missingRefs: "Pass empty schema with $id that should be ignored to ajv.addSchema.", + processCode: "Use option `code: {process: (code, schemaEnv: object) => string}`", + sourceCode: "Use option `code: {source: true}`", + strictDefaults: "It is default now, see option `strict`.", + strictKeywords: "It is default now, see option `strict`.", + uniqueItems: '"uniqueItems" keyword is always validated.', + unknownFormats: "Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).", + cache: "Map is used as cache, schema object as key.", + serialize: "Map is used as cache, schema object as key.", + ajvErrors: "It is default now.", +}; +const deprecatedOptions = { + ignoreKeywordsWithRef: "", + jsPropertySyntax: "", + unicode: '"minLength"/"maxLength" account for unicode characters by default.', +}; +const MAX_EXPRESSION = 200; +// eslint-disable-next-line complexity +function requiredOptions(o) { + var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0; + const s = o.strict; + const _optz = (_a = o.code) === null || _a === void 0 ? void 0 : _a.optimize; + const optimize = _optz === true || _optz === undefined ? 1 : _optz || 0; + const regExp = (_c = (_b = o.code) === null || _b === void 0 ? void 0 : _b.regExp) !== null && _c !== void 0 ? _c : defaultRegExp; + const uriResolver = (_d = o.uriResolver) !== null && _d !== void 0 ? _d : uri_1.default; + return { + strictSchema: (_f = (_e = o.strictSchema) !== null && _e !== void 0 ? _e : s) !== null && _f !== void 0 ? _f : true, + strictNumbers: (_h = (_g = o.strictNumbers) !== null && _g !== void 0 ? _g : s) !== null && _h !== void 0 ? _h : true, + strictTypes: (_k = (_j = o.strictTypes) !== null && _j !== void 0 ? _j : s) !== null && _k !== void 0 ? _k : "log", + strictTuples: (_m = (_l = o.strictTuples) !== null && _l !== void 0 ? _l : s) !== null && _m !== void 0 ? _m : "log", + strictRequired: (_p = (_o = o.strictRequired) !== null && _o !== void 0 ? _o : s) !== null && _p !== void 0 ? _p : false, + code: o.code ? { ...o.code, optimize, regExp } : { optimize, regExp }, + loopRequired: (_q = o.loopRequired) !== null && _q !== void 0 ? _q : MAX_EXPRESSION, + loopEnum: (_r = o.loopEnum) !== null && _r !== void 0 ? _r : MAX_EXPRESSION, + meta: (_s = o.meta) !== null && _s !== void 0 ? _s : true, + messages: (_t = o.messages) !== null && _t !== void 0 ? _t : true, + inlineRefs: (_u = o.inlineRefs) !== null && _u !== void 0 ? _u : true, + schemaId: (_v = o.schemaId) !== null && _v !== void 0 ? _v : "$id", + addUsedSchema: (_w = o.addUsedSchema) !== null && _w !== void 0 ? _w : true, + validateSchema: (_x = o.validateSchema) !== null && _x !== void 0 ? _x : true, + validateFormats: (_y = o.validateFormats) !== null && _y !== void 0 ? _y : true, + unicodeRegExp: (_z = o.unicodeRegExp) !== null && _z !== void 0 ? _z : true, + int32range: (_0 = o.int32range) !== null && _0 !== void 0 ? _0 : true, + uriResolver: uriResolver, + }; +} +class Ajv { + constructor(opts = {}) { + this.schemas = {}; + this.refs = {}; + this.formats = {}; + this._compilations = new Set(); + this._loading = {}; + this._cache = new Map(); + opts = this.opts = { ...opts, ...requiredOptions(opts) }; + const { es5, lines } = this.opts.code; + this.scope = new codegen_2.ValueScope({ scope: {}, prefixes: EXT_SCOPE_NAMES, es5, lines }); + this.logger = getLogger(opts.logger); + const formatOpt = opts.validateFormats; + opts.validateFormats = false; + this.RULES = (0, rules_1.getRules)(); + checkOptions.call(this, removedOptions, opts, "NOT SUPPORTED"); + checkOptions.call(this, deprecatedOptions, opts, "DEPRECATED", "warn"); + this._metaOpts = getMetaSchemaOptions.call(this); + if (opts.formats) + addInitialFormats.call(this); + this._addVocabularies(); + this._addDefaultMetaSchema(); + if (opts.keywords) + addInitialKeywords.call(this, opts.keywords); + if (typeof opts.meta == "object") + this.addMetaSchema(opts.meta); + addInitialSchemas.call(this); + opts.validateFormats = formatOpt; + } + _addVocabularies() { + this.addKeyword("$async"); + } + _addDefaultMetaSchema() { + const { $data, meta, schemaId } = this.opts; + let _dataRefSchema = $dataRefSchema; + if (schemaId === "id") { + _dataRefSchema = { ...$dataRefSchema }; + _dataRefSchema.id = _dataRefSchema.$id; + delete _dataRefSchema.$id; + } + if (meta && $data) + this.addMetaSchema(_dataRefSchema, _dataRefSchema[schemaId], false); + } + defaultMeta() { + const { meta, schemaId } = this.opts; + return (this.opts.defaultMeta = typeof meta == "object" ? meta[schemaId] || meta : undefined); + } + validate(schemaKeyRef, // key, ref or schema object + data // to be validated + ) { + let v; + if (typeof schemaKeyRef == "string") { + v = this.getSchema(schemaKeyRef); + if (!v) + throw new Error(`no schema with key or ref "${schemaKeyRef}"`); + } + else { + v = this.compile(schemaKeyRef); + } + const valid = v(data); + if (!("$async" in v)) + this.errors = v.errors; + return valid; + } + compile(schema, _meta) { + const sch = this._addSchema(schema, _meta); + return (sch.validate || this._compileSchemaEnv(sch)); + } + compileAsync(schema, meta) { + if (typeof this.opts.loadSchema != "function") { + throw new Error("options.loadSchema should be a function"); + } + const { loadSchema } = this.opts; + return runCompileAsync.call(this, schema, meta); + async function runCompileAsync(_schema, _meta) { + await loadMetaSchema.call(this, _schema.$schema); + const sch = this._addSchema(_schema, _meta); + return sch.validate || _compileAsync.call(this, sch); + } + async function loadMetaSchema($ref) { + if ($ref && !this.getSchema($ref)) { + await runCompileAsync.call(this, { $ref }, true); + } + } + async function _compileAsync(sch) { + try { + return this._compileSchemaEnv(sch); + } + catch (e) { + if (!(e instanceof ref_error_1.default)) + throw e; + checkLoaded.call(this, e); + await loadMissingSchema.call(this, e.missingSchema); + return _compileAsync.call(this, sch); + } + } + function checkLoaded({ missingSchema: ref, missingRef }) { + if (this.refs[ref]) { + throw new Error(`AnySchema ${ref} is loaded but ${missingRef} cannot be resolved`); + } + } + async function loadMissingSchema(ref) { + const _schema = await _loadSchema.call(this, ref); + if (!this.refs[ref]) + await loadMetaSchema.call(this, _schema.$schema); + if (!this.refs[ref]) + this.addSchema(_schema, ref, meta); + } + async function _loadSchema(ref) { + const p = this._loading[ref]; + if (p) + return p; + try { + return await (this._loading[ref] = loadSchema(ref)); + } + finally { + delete this._loading[ref]; + } + } + } + // Adds schema to the instance + addSchema(schema, // If array is passed, `key` will be ignored + key, // Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`. + _meta, // true if schema is a meta-schema. Used internally, addMetaSchema should be used instead. + _validateSchema = this.opts.validateSchema // false to skip schema validation. Used internally, option validateSchema should be used instead. + ) { + if (Array.isArray(schema)) { + for (const sch of schema) + this.addSchema(sch, undefined, _meta, _validateSchema); + return this; + } + let id; + if (typeof schema === "object") { + const { schemaId } = this.opts; + id = schema[schemaId]; + if (id !== undefined && typeof id != "string") { + throw new Error(`schema ${schemaId} must be string`); + } + } + key = (0, resolve_1.normalizeId)(key || id); + this._checkUnique(key); + this.schemas[key] = this._addSchema(schema, _meta, key, _validateSchema, true); + return this; + } + // Add schema that will be used to validate other schemas + // options in META_IGNORE_OPTIONS are alway set to false + addMetaSchema(schema, key, // schema key + _validateSchema = this.opts.validateSchema // false to skip schema validation, can be used to override validateSchema option for meta-schema + ) { + this.addSchema(schema, key, true, _validateSchema); + return this; + } + // Validate schema against its meta-schema + validateSchema(schema, throwOrLogError) { + if (typeof schema == "boolean") + return true; + let $schema; + $schema = schema.$schema; + if ($schema !== undefined && typeof $schema != "string") { + throw new Error("$schema must be a string"); + } + $schema = $schema || this.opts.defaultMeta || this.defaultMeta(); + if (!$schema) { + this.logger.warn("meta-schema not available"); + this.errors = null; + return true; + } + const valid = this.validate($schema, schema); + if (!valid && throwOrLogError) { + const message = "schema is invalid: " + this.errorsText(); + if (this.opts.validateSchema === "log") + this.logger.error(message); + else + throw new Error(message); + } + return valid; + } + // Get compiled schema by `key` or `ref`. + // (`key` that was passed to `addSchema` or full schema reference - `schema.$id` or resolved id) + getSchema(keyRef) { + let sch; + while (typeof (sch = getSchEnv.call(this, keyRef)) == "string") + keyRef = sch; + if (sch === undefined) { + const { schemaId } = this.opts; + const root = new compile_1.SchemaEnv({ schema: {}, schemaId }); + sch = compile_1.resolveSchema.call(this, root, keyRef); + if (!sch) + return; + this.refs[keyRef] = sch; + } + return (sch.validate || this._compileSchemaEnv(sch)); + } + // Remove cached schema(s). + // If no parameter is passed all schemas but meta-schemas are removed. + // If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed. + // Even if schema is referenced by other schemas it still can be removed as other schemas have local references. + removeSchema(schemaKeyRef) { + if (schemaKeyRef instanceof RegExp) { + this._removeAllSchemas(this.schemas, schemaKeyRef); + this._removeAllSchemas(this.refs, schemaKeyRef); + return this; + } + switch (typeof schemaKeyRef) { + case "undefined": + this._removeAllSchemas(this.schemas); + this._removeAllSchemas(this.refs); + this._cache.clear(); + return this; + case "string": { + const sch = getSchEnv.call(this, schemaKeyRef); + if (typeof sch == "object") + this._cache.delete(sch.schema); + delete this.schemas[schemaKeyRef]; + delete this.refs[schemaKeyRef]; + return this; + } + case "object": { + const cacheKey = schemaKeyRef; + this._cache.delete(cacheKey); + let id = schemaKeyRef[this.opts.schemaId]; + if (id) { + id = (0, resolve_1.normalizeId)(id); + delete this.schemas[id]; + delete this.refs[id]; + } + return this; + } + default: + throw new Error("ajv.removeSchema: invalid parameter"); + } + } + // add "vocabulary" - a collection of keywords + addVocabulary(definitions) { + for (const def of definitions) + this.addKeyword(def); + return this; + } + addKeyword(kwdOrDef, def // deprecated + ) { + let keyword; + if (typeof kwdOrDef == "string") { + keyword = kwdOrDef; + if (typeof def == "object") { + this.logger.warn("these parameters are deprecated, see docs for addKeyword"); + def.keyword = keyword; + } + } + else if (typeof kwdOrDef == "object" && def === undefined) { + def = kwdOrDef; + keyword = def.keyword; + if (Array.isArray(keyword) && !keyword.length) { + throw new Error("addKeywords: keyword must be string or non-empty array"); + } + } + else { + throw new Error("invalid addKeywords parameters"); + } + checkKeyword.call(this, keyword, def); + if (!def) { + (0, util_1.eachItem)(keyword, (kwd) => addRule.call(this, kwd)); + return this; + } + keywordMetaschema.call(this, def); + const definition = { + ...def, + type: (0, dataType_1.getJSONTypes)(def.type), + schemaType: (0, dataType_1.getJSONTypes)(def.schemaType), + }; + (0, util_1.eachItem)(keyword, definition.type.length === 0 + ? (k) => addRule.call(this, k, definition) + : (k) => definition.type.forEach((t) => addRule.call(this, k, definition, t))); + return this; + } + getKeyword(keyword) { + const rule = this.RULES.all[keyword]; + return typeof rule == "object" ? rule.definition : !!rule; + } + // Remove keyword + removeKeyword(keyword) { + // TODO return type should be Ajv + const { RULES } = this; + delete RULES.keywords[keyword]; + delete RULES.all[keyword]; + for (const group of RULES.rules) { + const i = group.rules.findIndex((rule) => rule.keyword === keyword); + if (i >= 0) + group.rules.splice(i, 1); + } + return this; + } + // Add format + addFormat(name, format) { + if (typeof format == "string") + format = new RegExp(format); + this.formats[name] = format; + return this; + } + errorsText(errors = this.errors, // optional array of validation errors + { separator = ", ", dataVar = "data" } = {} // optional options with properties `separator` and `dataVar` + ) { + if (!errors || errors.length === 0) + return "No errors"; + return errors + .map((e) => `${dataVar}${e.instancePath} ${e.message}`) + .reduce((text, msg) => text + separator + msg); + } + $dataMetaSchema(metaSchema, keywordsJsonPointers) { + const rules = this.RULES.all; + metaSchema = JSON.parse(JSON.stringify(metaSchema)); + for (const jsonPointer of keywordsJsonPointers) { + const segments = jsonPointer.split("/").slice(1); // first segment is an empty string + let keywords = metaSchema; + for (const seg of segments) + keywords = keywords[seg]; + for (const key in rules) { + const rule = rules[key]; + if (typeof rule != "object") + continue; + const { $data } = rule.definition; + const schema = keywords[key]; + if ($data && schema) + keywords[key] = schemaOrData(schema); + } + } + return metaSchema; + } + _removeAllSchemas(schemas, regex) { + for (const keyRef in schemas) { + const sch = schemas[keyRef]; + if (!regex || regex.test(keyRef)) { + if (typeof sch == "string") { + delete schemas[keyRef]; + } + else if (sch && !sch.meta) { + this._cache.delete(sch.schema); + delete schemas[keyRef]; + } + } + } + } + _addSchema(schema, meta, baseId, validateSchema = this.opts.validateSchema, addSchema = this.opts.addUsedSchema) { + let id; + const { schemaId } = this.opts; + if (typeof schema == "object") { + id = schema[schemaId]; + } + else { + if (this.opts.jtd) + throw new Error("schema must be object"); + else if (typeof schema != "boolean") + throw new Error("schema must be object or boolean"); + } + let sch = this._cache.get(schema); + if (sch !== undefined) + return sch; + baseId = (0, resolve_1.normalizeId)(id || baseId); + const localRefs = resolve_1.getSchemaRefs.call(this, schema, baseId); + sch = new compile_1.SchemaEnv({ schema, schemaId, meta, baseId, localRefs }); + this._cache.set(sch.schema, sch); + if (addSchema && !baseId.startsWith("#")) { + // TODO atm it is allowed to overwrite schemas without id (instead of not adding them) + if (baseId) + this._checkUnique(baseId); + this.refs[baseId] = sch; + } + if (validateSchema) + this.validateSchema(schema, true); + return sch; + } + _checkUnique(id) { + if (this.schemas[id] || this.refs[id]) { + throw new Error(`schema with key or id "${id}" already exists`); + } + } + _compileSchemaEnv(sch) { + if (sch.meta) + this._compileMetaSchema(sch); + else + compile_1.compileSchema.call(this, sch); + /* istanbul ignore if */ + if (!sch.validate) + throw new Error("ajv implementation error"); + return sch.validate; + } + _compileMetaSchema(sch) { + const currentOpts = this.opts; + this.opts = this._metaOpts; + try { + compile_1.compileSchema.call(this, sch); + } + finally { + this.opts = currentOpts; + } + } +} +exports.default = Ajv; +Ajv.ValidationError = validation_error_1.default; +Ajv.MissingRefError = ref_error_1.default; +function checkOptions(checkOpts, options, msg, log = "error") { + for (const key in checkOpts) { + const opt = key; + if (opt in options) + this.logger[log](`${msg}: option ${key}. ${checkOpts[opt]}`); + } +} +function getSchEnv(keyRef) { + keyRef = (0, resolve_1.normalizeId)(keyRef); // TODO tests fail without this line + return this.schemas[keyRef] || this.refs[keyRef]; +} +function addInitialSchemas() { + const optsSchemas = this.opts.schemas; + if (!optsSchemas) + return; + if (Array.isArray(optsSchemas)) + this.addSchema(optsSchemas); + else + for (const key in optsSchemas) + this.addSchema(optsSchemas[key], key); +} +function addInitialFormats() { + for (const name in this.opts.formats) { + const format = this.opts.formats[name]; + if (format) + this.addFormat(name, format); + } +} +function addInitialKeywords(defs) { + if (Array.isArray(defs)) { + this.addVocabulary(defs); + return; + } + this.logger.warn("keywords option as map is deprecated, pass array"); + for (const keyword in defs) { + const def = defs[keyword]; + if (!def.keyword) + def.keyword = keyword; + this.addKeyword(def); + } +} +function getMetaSchemaOptions() { + const metaOpts = { ...this.opts }; + for (const opt of META_IGNORE_OPTIONS) + delete metaOpts[opt]; + return metaOpts; +} +const noLogs = { log() { }, warn() { }, error() { } }; +function getLogger(logger) { + if (logger === false) + return noLogs; + if (logger === undefined) + return console; + if (logger.log && logger.warn && logger.error) + return logger; + throw new Error("logger must implement log, warn and error methods"); +} +const KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i; +function checkKeyword(keyword, def) { + const { RULES } = this; + (0, util_1.eachItem)(keyword, (kwd) => { + if (RULES.keywords[kwd]) + throw new Error(`Keyword ${kwd} is already defined`); + if (!KEYWORD_NAME.test(kwd)) + throw new Error(`Keyword ${kwd} has invalid name`); + }); + if (!def) + return; + if (def.$data && !("code" in def || "validate" in def)) { + throw new Error('$data keyword must have "code" or "validate" function'); + } +} +function addRule(keyword, definition, dataType) { + var _a; + const post = definition === null || definition === void 0 ? void 0 : definition.post; + if (dataType && post) + throw new Error('keyword with "post" flag cannot have "type"'); + const { RULES } = this; + let ruleGroup = post ? RULES.post : RULES.rules.find(({ type: t }) => t === dataType); + if (!ruleGroup) { + ruleGroup = { type: dataType, rules: [] }; + RULES.rules.push(ruleGroup); + } + RULES.keywords[keyword] = true; + if (!definition) + return; + const rule = { + keyword, + definition: { + ...definition, + type: (0, dataType_1.getJSONTypes)(definition.type), + schemaType: (0, dataType_1.getJSONTypes)(definition.schemaType), + }, + }; + if (definition.before) + addBeforeRule.call(this, ruleGroup, rule, definition.before); + else + ruleGroup.rules.push(rule); + RULES.all[keyword] = rule; + (_a = definition.implements) === null || _a === void 0 ? void 0 : _a.forEach((kwd) => this.addKeyword(kwd)); +} +function addBeforeRule(ruleGroup, rule, before) { + const i = ruleGroup.rules.findIndex((_rule) => _rule.keyword === before); + if (i >= 0) { + ruleGroup.rules.splice(i, 0, rule); + } + else { + ruleGroup.rules.push(rule); + this.logger.warn(`rule ${before} is not defined`); + } +} +function keywordMetaschema(def) { + let { metaSchema } = def; + if (metaSchema === undefined) + return; + if (def.$data && this.opts.$data) + metaSchema = schemaOrData(metaSchema); + def.validateSchema = this.compile(metaSchema, true); +} +const $dataRef = { + $ref: "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#", +}; +function schemaOrData(schema) { + return { anyOf: [schema, $dataRef] }; +} +//# sourceMappingURL=core.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/core.js.map b/website/node_modules/ajv/dist/core.js.map new file mode 100644 index 00000000..c39cd6b5 --- /dev/null +++ b/website/node_modules/ajv/dist/core.js.map @@ -0,0 +1 @@ +{"version":3,"file":"core.js","sourceRoot":"","sources":["../lib/core.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/jtd.d.ts b/website/node_modules/ajv/dist/jtd.d.ts new file mode 100644 index 00000000..de3a2273 --- /dev/null +++ b/website/node_modules/ajv/dist/jtd.d.ts @@ -0,0 +1,47 @@ +import type { AnySchemaObject, SchemaObject, JTDParser } from "./types"; +import type { JTDSchemaType, SomeJTDSchemaType, JTDDataType } from "./types/jtd-schema"; +import AjvCore, { CurrentOptions } from "./core"; +type JTDOptions = CurrentOptions & { + strict?: never; + allowMatchingProperties?: never; + allowUnionTypes?: never; + validateFormats?: never; + $data?: never; + verbose?: boolean; + $comment?: never; + formats?: never; + loadSchema?: never; + useDefaults?: never; + coerceTypes?: never; + next?: never; + unevaluated?: never; + dynamicRef?: never; + meta?: boolean; + defaultMeta?: never; + inlineRefs?: boolean; + loopRequired?: never; + multipleOfPrecision?: never; +}; +declare class Ajv extends AjvCore { + constructor(opts?: JTDOptions); + _addVocabularies(): void; + _addDefaultMetaSchema(): void; + defaultMeta(): string | AnySchemaObject | undefined; + compileSerializer(schema: SchemaObject): (data: T) => string; + compileSerializer(schema: JTDSchemaType): (data: T) => string; + compileParser(schema: SchemaObject): JTDParser; + compileParser(schema: JTDSchemaType): JTDParser; + private _compileSerializer; + private _compileParser; +} +export default Ajv; +export { Format, FormatDefinition, AsyncFormatDefinition, KeywordDefinition, KeywordErrorDefinition, CodeKeywordDefinition, MacroKeywordDefinition, FuncKeywordDefinition, Vocabulary, Schema, SchemaObject, AnySchemaObject, AsyncSchema, AnySchema, ValidateFunction, AsyncValidateFunction, ErrorObject, ErrorNoParams, JTDParser, } from "./types"; +export { Plugin, Options, CodeOptions, InstanceOptions, Logger, ErrorsTextOptions } from "./core"; +export { SchemaCxt, SchemaObjCxt } from "./compile"; +export { KeywordCxt } from "./compile/validate"; +export { JTDErrorObject } from "./vocabularies/jtd"; +export { _, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions } from "./compile/codegen"; +export { JTDSchemaType, SomeJTDSchemaType, JTDDataType }; +export { JTDOptions }; +export { default as ValidationError } from "./runtime/validation_error"; +export { default as MissingRefError } from "./compile/ref_error"; diff --git a/website/node_modules/ajv/dist/jtd.js b/website/node_modules/ajv/dist/jtd.js new file mode 100644 index 00000000..b8938799 --- /dev/null +++ b/website/node_modules/ajv/dist/jtd.js @@ -0,0 +1,70 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MissingRefError = exports.ValidationError = exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = void 0; +const core_1 = require("./core"); +const jtd_1 = require("./vocabularies/jtd"); +const jtd_schema_1 = require("./refs/jtd-schema"); +const serialize_1 = require("./compile/jtd/serialize"); +const parse_1 = require("./compile/jtd/parse"); +const META_SCHEMA_ID = "JTD-meta-schema"; +class Ajv extends core_1.default { + constructor(opts = {}) { + super({ + ...opts, + jtd: true, + }); + } + _addVocabularies() { + super._addVocabularies(); + this.addVocabulary(jtd_1.default); + } + _addDefaultMetaSchema() { + super._addDefaultMetaSchema(); + if (!this.opts.meta) + return; + this.addMetaSchema(jtd_schema_1.default, META_SCHEMA_ID, false); + } + defaultMeta() { + return (this.opts.defaultMeta = + super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined)); + } + compileSerializer(schema) { + const sch = this._addSchema(schema); + return sch.serialize || this._compileSerializer(sch); + } + compileParser(schema) { + const sch = this._addSchema(schema); + return (sch.parse || this._compileParser(sch)); + } + _compileSerializer(sch) { + serialize_1.default.call(this, sch, sch.schema.definitions || {}); + /* istanbul ignore if */ + if (!sch.serialize) + throw new Error("ajv implementation error"); + return sch.serialize; + } + _compileParser(sch) { + parse_1.default.call(this, sch, sch.schema.definitions || {}); + /* istanbul ignore if */ + if (!sch.parse) + throw new Error("ajv implementation error"); + return sch.parse; + } +} +module.exports = exports = Ajv; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = Ajv; +var validate_1 = require("./compile/validate"); +Object.defineProperty(exports, "KeywordCxt", { enumerable: true, get: function () { return validate_1.KeywordCxt; } }); +var codegen_1 = require("./compile/codegen"); +Object.defineProperty(exports, "_", { enumerable: true, get: function () { return codegen_1._; } }); +Object.defineProperty(exports, "str", { enumerable: true, get: function () { return codegen_1.str; } }); +Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return codegen_1.stringify; } }); +Object.defineProperty(exports, "nil", { enumerable: true, get: function () { return codegen_1.nil; } }); +Object.defineProperty(exports, "Name", { enumerable: true, get: function () { return codegen_1.Name; } }); +Object.defineProperty(exports, "CodeGen", { enumerable: true, get: function () { return codegen_1.CodeGen; } }); +var validation_error_1 = require("./runtime/validation_error"); +Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return validation_error_1.default; } }); +var ref_error_1 = require("./compile/ref_error"); +Object.defineProperty(exports, "MissingRefError", { enumerable: true, get: function () { return ref_error_1.default; } }); +//# sourceMappingURL=jtd.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/jtd.js.map b/website/node_modules/ajv/dist/jtd.js.map new file mode 100644 index 00000000..e5ac473d --- /dev/null +++ b/website/node_modules/ajv/dist/jtd.js.map @@ -0,0 +1 @@ +{"version":3,"file":"jtd.js","sourceRoot":"","sources":["../lib/jtd.ts"],"names":[],"mappings":";;;AAEA,iCAA8C;AAC9C,4CAA8C;AAC9C,kDAA6C;AAC7C,uDAAuD;AACvD,+CAA+C;AAG/C,MAAM,cAAc,GAAG,iBAAiB,CAAA;AA4BxC,MAAM,GAAI,SAAQ,cAAO;IACvB,YAAY,OAAmB,EAAE;QAC/B,KAAK,CAAC;YACJ,GAAG,IAAI;YACP,GAAG,EAAE,IAAI;SACV,CAAC,CAAA;IACJ,CAAC;IAED,gBAAgB;QACd,KAAK,CAAC,gBAAgB,EAAE,CAAA;QACxB,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAA;IACnC,CAAC;IAED,qBAAqB;QACnB,KAAK,CAAC,qBAAqB,EAAE,CAAA;QAC7B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAM;QAC3B,IAAI,CAAC,aAAa,CAAC,oBAAa,EAAE,cAAc,EAAE,KAAK,CAAC,CAAA;IAC1D,CAAC;IAED,WAAW;QACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW;YAC3B,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;IACzF,CAAC;IAMD,iBAAiB,CAAc,MAAoB;QACjD,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;QACnC,OAAO,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAA;IACtD,CAAC;IAMD,aAAa,CAAc,MAAoB;QAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;QACnC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAiB,CAAA;IAChE,CAAC;IAEO,kBAAkB,CAAI,GAAc;QAC1C,mBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAG,GAAG,CAAC,MAA0B,CAAC,WAAW,IAAI,EAAE,CAAC,CAAA;QACpF,wBAAwB;QACxB,IAAI,CAAC,GAAG,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;QAC/D,OAAO,GAAG,CAAC,SAAS,CAAA;IACtB,CAAC;IAEO,cAAc,CAAC,GAAc;QACnC,eAAa,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAG,GAAG,CAAC,MAA0B,CAAC,WAAW,IAAI,EAAE,CAAC,CAAA;QAChF,wBAAwB;QACxB,IAAI,CAAC,GAAG,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;QAC3D,OAAO,GAAG,CAAC,KAAK,CAAA;IAClB,CAAC;CACF;AAED,MAAM,CAAC,OAAO,GAAG,OAAO,GAAG,GAAG,CAAA;AAC9B,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC,CAAA;AAE3D,kBAAe,GAAG,CAAA;AA0BlB,+CAA6C;AAArC,sGAAA,UAAU,OAAA;AAElB,6CAA6F;AAArF,4FAAA,CAAC,OAAA;AAAE,8FAAA,GAAG,OAAA;AAAE,oGAAA,SAAS,OAAA;AAAE,8FAAA,GAAG,OAAA;AAAE,+FAAA,IAAI,OAAA;AAAQ,kGAAA,OAAO,OAAA;AAInD,+DAAqE;AAA7D,mHAAA,OAAO,OAAmB;AAClC,iDAA8D;AAAtD,4GAAA,OAAO,OAAmB"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/refs/data.json b/website/node_modules/ajv/dist/refs/data.json new file mode 100644 index 00000000..9ffc9f5c --- /dev/null +++ b/website/node_modules/ajv/dist/refs/data.json @@ -0,0 +1,13 @@ +{ + "$id": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#", + "description": "Meta-schema for $data reference (JSON AnySchema extension proposal)", + "type": "object", + "required": ["$data"], + "properties": { + "$data": { + "type": "string", + "anyOf": [{"format": "relative-json-pointer"}, {"format": "json-pointer"}] + } + }, + "additionalProperties": false +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2019-09/index.d.ts b/website/node_modules/ajv/dist/refs/json-schema-2019-09/index.d.ts new file mode 100644 index 00000000..cf008331 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2019-09/index.d.ts @@ -0,0 +1,2 @@ +import type Ajv from "../../core"; +export default function addMetaSchema2019(this: Ajv, $data?: boolean): Ajv; diff --git a/website/node_modules/ajv/dist/refs/json-schema-2019-09/index.js b/website/node_modules/ajv/dist/refs/json-schema-2019-09/index.js new file mode 100644 index 00000000..e8649628 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2019-09/index.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const metaSchema = require("./schema.json"); +const applicator = require("./meta/applicator.json"); +const content = require("./meta/content.json"); +const core = require("./meta/core.json"); +const format = require("./meta/format.json"); +const metadata = require("./meta/meta-data.json"); +const validation = require("./meta/validation.json"); +const META_SUPPORT_DATA = ["/properties"]; +function addMetaSchema2019($data) { + ; + [ + metaSchema, + applicator, + content, + core, + with$data(this, format), + metadata, + with$data(this, validation), + ].forEach((sch) => this.addMetaSchema(sch, undefined, false)); + return this; + function with$data(ajv, sch) { + return $data ? ajv.$dataMetaSchema(sch, META_SUPPORT_DATA) : sch; + } +} +exports.default = addMetaSchema2019; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/refs/json-schema-2019-09/index.js.map b/website/node_modules/ajv/dist/refs/json-schema-2019-09/index.js.map new file mode 100644 index 00000000..9b8a36d6 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2019-09/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/refs/json-schema-2019-09/index.ts"],"names":[],"mappings":";;AAEA,4CAA2C;AAC3C,qDAAoD;AACpD,+CAA8C;AAC9C,yCAAwC;AACxC,6CAA4C;AAC5C,kDAAiD;AACjD,qDAAoD;AAEpD,MAAM,iBAAiB,GAAG,CAAC,aAAa,CAAC,CAAA;AAEzC,SAAwB,iBAAiB,CAAY,KAAe;IAClE,CAAC;IAAA;QACC,UAAU;QACV,UAAU;QACV,OAAO;QACP,IAAI;QACJ,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC;QACvB,QAAQ;QACR,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC;KAC5B,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAA;IAC7D,OAAO,IAAI,CAAA;IAEX,SAAS,SAAS,CAAC,GAAQ,EAAE,GAAoB;QAC/C,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;IAClE,CAAC;AACH,CAAC;AAfD,oCAeC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/applicator.json b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/applicator.json new file mode 100644 index 00000000..c5e91cf2 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/applicator.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/applicator", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/applicator": true + }, + "$recursiveAnchor": true, + + "title": "Applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "additionalItems": {"$recursiveRef": "#"}, + "unevaluatedItems": {"$recursiveRef": "#"}, + "items": { + "anyOf": [{"$recursiveRef": "#"}, {"$ref": "#/$defs/schemaArray"}] + }, + "contains": {"$recursiveRef": "#"}, + "additionalProperties": {"$recursiveRef": "#"}, + "unevaluatedProperties": {"$recursiveRef": "#"}, + "properties": { + "type": "object", + "additionalProperties": {"$recursiveRef": "#"}, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": {"$recursiveRef": "#"}, + "propertyNames": {"format": "regex"}, + "default": {} + }, + "dependentSchemas": { + "type": "object", + "additionalProperties": { + "$recursiveRef": "#" + } + }, + "propertyNames": {"$recursiveRef": "#"}, + "if": {"$recursiveRef": "#"}, + "then": {"$recursiveRef": "#"}, + "else": {"$recursiveRef": "#"}, + "allOf": {"$ref": "#/$defs/schemaArray"}, + "anyOf": {"$ref": "#/$defs/schemaArray"}, + "oneOf": {"$ref": "#/$defs/schemaArray"}, + "not": {"$recursiveRef": "#"} + }, + "$defs": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$recursiveRef": "#"} + } + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/content.json b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/content.json new file mode 100644 index 00000000..b8f63734 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/content.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/content", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/content": true + }, + "$recursiveAnchor": true, + + "title": "Content vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "contentMediaType": {"type": "string"}, + "contentEncoding": {"type": "string"}, + "contentSchema": {"$recursiveRef": "#"} + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/core.json b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/core.json new file mode 100644 index 00000000..f71adbff --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/core.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/core", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/core": true + }, + "$recursiveAnchor": true, + + "title": "Core vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference", + "$comment": "Non-empty fragments not allowed.", + "pattern": "^[^#]*#?$" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$anchor": { + "type": "string", + "pattern": "^[A-Za-z][-A-Za-z0-9.:_]*$" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$recursiveRef": { + "type": "string", + "format": "uri-reference" + }, + "$recursiveAnchor": { + "type": "boolean", + "default": false + }, + "$vocabulary": { + "type": "object", + "propertyNames": { + "type": "string", + "format": "uri" + }, + "additionalProperties": { + "type": "boolean" + } + }, + "$comment": { + "type": "string" + }, + "$defs": { + "type": "object", + "additionalProperties": {"$recursiveRef": "#"}, + "default": {} + } + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/format.json b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/format.json new file mode 100644 index 00000000..03ccfce2 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/format.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/format", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/format": true + }, + "$recursiveAnchor": true, + + "title": "Format vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "format": {"type": "string"} + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/meta-data.json b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/meta-data.json new file mode 100644 index 00000000..0e194326 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/meta-data.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/meta-data", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/meta-data": true + }, + "$recursiveAnchor": true, + + "title": "Meta-data vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "deprecated": { + "type": "boolean", + "default": false + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + } + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/validation.json b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/validation.json new file mode 100644 index 00000000..7027a127 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2019-09/meta/validation.json @@ -0,0 +1,90 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/validation", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/validation": true + }, + "$recursiveAnchor": true, + + "title": "Validation vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": {"$ref": "#/$defs/nonNegativeInteger"}, + "minLength": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": {"$ref": "#/$defs/nonNegativeInteger"}, + "minItems": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxContains": {"$ref": "#/$defs/nonNegativeInteger"}, + "minContains": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 1 + }, + "maxProperties": {"$ref": "#/$defs/nonNegativeInteger"}, + "minProperties": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "required": {"$ref": "#/$defs/stringArray"}, + "dependentRequired": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/stringArray" + } + }, + "const": true, + "enum": { + "type": "array", + "items": true + }, + "type": { + "anyOf": [ + {"$ref": "#/$defs/simpleTypes"}, + { + "type": "array", + "items": {"$ref": "#/$defs/simpleTypes"}, + "minItems": 1, + "uniqueItems": true + } + ] + } + }, + "$defs": { + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 0 + }, + "simpleTypes": { + "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + "stringArray": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true, + "default": [] + } + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2019-09/schema.json b/website/node_modules/ajv/dist/refs/json-schema-2019-09/schema.json new file mode 100644 index 00000000..54eb7157 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2019-09/schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/schema", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/core": true, + "https://json-schema.org/draft/2019-09/vocab/applicator": true, + "https://json-schema.org/draft/2019-09/vocab/validation": true, + "https://json-schema.org/draft/2019-09/vocab/meta-data": true, + "https://json-schema.org/draft/2019-09/vocab/format": false, + "https://json-schema.org/draft/2019-09/vocab/content": true + }, + "$recursiveAnchor": true, + + "title": "Core and Validation specifications meta-schema", + "allOf": [ + {"$ref": "meta/core"}, + {"$ref": "meta/applicator"}, + {"$ref": "meta/validation"}, + {"$ref": "meta/meta-data"}, + {"$ref": "meta/format"}, + {"$ref": "meta/content"} + ], + "type": ["object", "boolean"], + "properties": { + "definitions": { + "$comment": "While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.", + "type": "object", + "additionalProperties": {"$recursiveRef": "#"}, + "default": {} + }, + "dependencies": { + "$comment": "\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"", + "type": "object", + "additionalProperties": { + "anyOf": [{"$recursiveRef": "#"}, {"$ref": "meta/validation#/$defs/stringArray"}] + } + } + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/index.d.ts b/website/node_modules/ajv/dist/refs/json-schema-2020-12/index.d.ts new file mode 100644 index 00000000..c232ab05 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/index.d.ts @@ -0,0 +1,2 @@ +import type Ajv from "../../core"; +export default function addMetaSchema2020(this: Ajv, $data?: boolean): Ajv; diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/index.js b/website/node_modules/ajv/dist/refs/json-schema-2020-12/index.js new file mode 100644 index 00000000..d9256756 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/index.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const metaSchema = require("./schema.json"); +const applicator = require("./meta/applicator.json"); +const unevaluated = require("./meta/unevaluated.json"); +const content = require("./meta/content.json"); +const core = require("./meta/core.json"); +const format = require("./meta/format-annotation.json"); +const metadata = require("./meta/meta-data.json"); +const validation = require("./meta/validation.json"); +const META_SUPPORT_DATA = ["/properties"]; +function addMetaSchema2020($data) { + ; + [ + metaSchema, + applicator, + unevaluated, + content, + core, + with$data(this, format), + metadata, + with$data(this, validation), + ].forEach((sch) => this.addMetaSchema(sch, undefined, false)); + return this; + function with$data(ajv, sch) { + return $data ? ajv.$dataMetaSchema(sch, META_SUPPORT_DATA) : sch; + } +} +exports.default = addMetaSchema2020; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/index.js.map b/website/node_modules/ajv/dist/refs/json-schema-2020-12/index.js.map new file mode 100644 index 00000000..eb90027d --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/refs/json-schema-2020-12/index.ts"],"names":[],"mappings":";;AAEA,4CAA2C;AAC3C,qDAAoD;AACpD,uDAAsD;AACtD,+CAA8C;AAC9C,yCAAwC;AACxC,wDAAuD;AACvD,kDAAiD;AACjD,qDAAoD;AAEpD,MAAM,iBAAiB,GAAG,CAAC,aAAa,CAAC,CAAA;AAEzC,SAAwB,iBAAiB,CAAY,KAAe;IAClE,CAAC;IAAA;QACC,UAAU;QACV,UAAU;QACV,WAAW;QACX,OAAO;QACP,IAAI;QACJ,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC;QACvB,QAAQ;QACR,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC;KAC5B,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAA;IAC7D,OAAO,IAAI,CAAA;IAEX,SAAS,SAAS,CAAC,GAAQ,EAAE,GAAoB;QAC/C,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;IAClE,CAAC;AACH,CAAC;AAhBD,oCAgBC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/applicator.json b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/applicator.json new file mode 100644 index 00000000..674c913d --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/applicator.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/applicator", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/applicator": true + }, + "$dynamicAnchor": "meta", + + "title": "Applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "prefixItems": {"$ref": "#/$defs/schemaArray"}, + "items": {"$dynamicRef": "#meta"}, + "contains": {"$dynamicRef": "#meta"}, + "additionalProperties": {"$dynamicRef": "#meta"}, + "properties": { + "type": "object", + "additionalProperties": {"$dynamicRef": "#meta"}, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": {"$dynamicRef": "#meta"}, + "propertyNames": {"format": "regex"}, + "default": {} + }, + "dependentSchemas": { + "type": "object", + "additionalProperties": {"$dynamicRef": "#meta"}, + "default": {} + }, + "propertyNames": {"$dynamicRef": "#meta"}, + "if": {"$dynamicRef": "#meta"}, + "then": {"$dynamicRef": "#meta"}, + "else": {"$dynamicRef": "#meta"}, + "allOf": {"$ref": "#/$defs/schemaArray"}, + "anyOf": {"$ref": "#/$defs/schemaArray"}, + "oneOf": {"$ref": "#/$defs/schemaArray"}, + "not": {"$dynamicRef": "#meta"} + }, + "$defs": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$dynamicRef": "#meta"} + } + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/content.json b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/content.json new file mode 100644 index 00000000..2ae23ddb --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/content.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/content", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/content": true + }, + "$dynamicAnchor": "meta", + + "title": "Content vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "contentEncoding": {"type": "string"}, + "contentMediaType": {"type": "string"}, + "contentSchema": {"$dynamicRef": "#meta"} + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/core.json b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/core.json new file mode 100644 index 00000000..4c8e5cb6 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/core.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/core", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true + }, + "$dynamicAnchor": "meta", + + "title": "Core vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "$id": { + "$ref": "#/$defs/uriReferenceString", + "$comment": "Non-empty fragments not allowed.", + "pattern": "^[^#]*#?$" + }, + "$schema": {"$ref": "#/$defs/uriString"}, + "$ref": {"$ref": "#/$defs/uriReferenceString"}, + "$anchor": {"$ref": "#/$defs/anchorString"}, + "$dynamicRef": {"$ref": "#/$defs/uriReferenceString"}, + "$dynamicAnchor": {"$ref": "#/$defs/anchorString"}, + "$vocabulary": { + "type": "object", + "propertyNames": {"$ref": "#/$defs/uriString"}, + "additionalProperties": { + "type": "boolean" + } + }, + "$comment": { + "type": "string" + }, + "$defs": { + "type": "object", + "additionalProperties": {"$dynamicRef": "#meta"} + } + }, + "$defs": { + "anchorString": { + "type": "string", + "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$" + }, + "uriString": { + "type": "string", + "format": "uri" + }, + "uriReferenceString": { + "type": "string", + "format": "uri-reference" + } + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/format-annotation.json b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/format-annotation.json new file mode 100644 index 00000000..83c26e35 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/format-annotation.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/format-annotation", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/format-annotation": true + }, + "$dynamicAnchor": "meta", + + "title": "Format vocabulary meta-schema for annotation results", + "type": ["object", "boolean"], + "properties": { + "format": {"type": "string"} + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/meta-data.json b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/meta-data.json new file mode 100644 index 00000000..11946fb5 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/meta-data.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/meta-data", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/meta-data": true + }, + "$dynamicAnchor": "meta", + + "title": "Meta-data vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "deprecated": { + "type": "boolean", + "default": false + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + } + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/unevaluated.json b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/unevaluated.json new file mode 100644 index 00000000..5e4b203b --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/unevaluated.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/unevaluated", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/unevaluated": true + }, + "$dynamicAnchor": "meta", + + "title": "Unevaluated applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "unevaluatedItems": {"$dynamicRef": "#meta"}, + "unevaluatedProperties": {"$dynamicRef": "#meta"} + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/validation.json b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/validation.json new file mode 100644 index 00000000..e0ae13d9 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/meta/validation.json @@ -0,0 +1,90 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/validation", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/validation": true + }, + "$dynamicAnchor": "meta", + + "title": "Validation vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "type": { + "anyOf": [ + {"$ref": "#/$defs/simpleTypes"}, + { + "type": "array", + "items": {"$ref": "#/$defs/simpleTypes"}, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "const": true, + "enum": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": {"$ref": "#/$defs/nonNegativeInteger"}, + "minLength": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": {"$ref": "#/$defs/nonNegativeInteger"}, + "minItems": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxContains": {"$ref": "#/$defs/nonNegativeInteger"}, + "minContains": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 1 + }, + "maxProperties": {"$ref": "#/$defs/nonNegativeInteger"}, + "minProperties": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "required": {"$ref": "#/$defs/stringArray"}, + "dependentRequired": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/stringArray" + } + } + }, + "$defs": { + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 0 + }, + "simpleTypes": { + "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + "stringArray": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true, + "default": [] + } + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-2020-12/schema.json b/website/node_modules/ajv/dist/refs/json-schema-2020-12/schema.json new file mode 100644 index 00000000..1c68270f --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-2020-12/schema.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/schema", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true, + "https://json-schema.org/draft/2020-12/vocab/applicator": true, + "https://json-schema.org/draft/2020-12/vocab/unevaluated": true, + "https://json-schema.org/draft/2020-12/vocab/validation": true, + "https://json-schema.org/draft/2020-12/vocab/meta-data": true, + "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, + "https://json-schema.org/draft/2020-12/vocab/content": true + }, + "$dynamicAnchor": "meta", + + "title": "Core and Validation specifications meta-schema", + "allOf": [ + {"$ref": "meta/core"}, + {"$ref": "meta/applicator"}, + {"$ref": "meta/unevaluated"}, + {"$ref": "meta/validation"}, + {"$ref": "meta/meta-data"}, + {"$ref": "meta/format-annotation"}, + {"$ref": "meta/content"} + ], + "type": ["object", "boolean"], + "$comment": "This meta-schema also defines keywords that have appeared in previous drafts in order to prevent incompatible extensions as they remain in common use.", + "properties": { + "definitions": { + "$comment": "\"definitions\" has been replaced by \"$defs\".", + "type": "object", + "additionalProperties": {"$dynamicRef": "#meta"}, + "deprecated": true, + "default": {} + }, + "dependencies": { + "$comment": "\"dependencies\" has been split and replaced by \"dependentSchemas\" and \"dependentRequired\" in order to serve their differing semantics.", + "type": "object", + "additionalProperties": { + "anyOf": [{"$dynamicRef": "#meta"}, {"$ref": "meta/validation#/$defs/stringArray"}] + }, + "deprecated": true, + "default": {} + }, + "$recursiveAnchor": { + "$comment": "\"$recursiveAnchor\" has been replaced by \"$dynamicAnchor\".", + "$ref": "meta/core#/$defs/anchorString", + "deprecated": true + }, + "$recursiveRef": { + "$comment": "\"$recursiveRef\" has been replaced by \"$dynamicRef\".", + "$ref": "meta/core#/$defs/uriReferenceString", + "deprecated": true + } + } +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-draft-06.json b/website/node_modules/ajv/dist/refs/json-schema-draft-06.json new file mode 100644 index 00000000..5410064b --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-draft-06.json @@ -0,0 +1,137 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "http://json-schema.org/draft-06/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$ref": "#"} + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [{"$ref": "#/definitions/nonNegativeInteger"}, {"default": 0}] + }, + "simpleTypes": { + "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + "stringArray": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "examples": { + "type": "array", + "items": {} + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": {"$ref": "#/definitions/nonNegativeInteger"}, + "minLength": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": {"$ref": "#"}, + "items": { + "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/schemaArray"}], + "default": {} + }, + "maxItems": {"$ref": "#/definitions/nonNegativeInteger"}, + "minItems": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": {"$ref": "#"}, + "maxProperties": {"$ref": "#/definitions/nonNegativeInteger"}, + "minProperties": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "required": {"$ref": "#/definitions/stringArray"}, + "additionalProperties": {"$ref": "#"}, + "definitions": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/stringArray"}] + } + }, + "propertyNames": {"$ref": "#"}, + "const": {}, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + {"$ref": "#/definitions/simpleTypes"}, + { + "type": "array", + "items": {"$ref": "#/definitions/simpleTypes"}, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": {"type": "string"}, + "allOf": {"$ref": "#/definitions/schemaArray"}, + "anyOf": {"$ref": "#/definitions/schemaArray"}, + "oneOf": {"$ref": "#/definitions/schemaArray"}, + "not": {"$ref": "#"} + }, + "default": {} +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-draft-07.json b/website/node_modules/ajv/dist/refs/json-schema-draft-07.json new file mode 100644 index 00000000..6a748510 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-draft-07.json @@ -0,0 +1,151 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://json-schema.org/draft-07/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$ref": "#"} + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [{"$ref": "#/definitions/nonNegativeInteger"}, {"default": 0}] + }, + "simpleTypes": { + "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + "stringArray": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": {"$ref": "#/definitions/nonNegativeInteger"}, + "minLength": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": {"$ref": "#"}, + "items": { + "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/schemaArray"}], + "default": true + }, + "maxItems": {"$ref": "#/definitions/nonNegativeInteger"}, + "minItems": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": {"$ref": "#"}, + "maxProperties": {"$ref": "#/definitions/nonNegativeInteger"}, + "minProperties": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "required": {"$ref": "#/definitions/stringArray"}, + "additionalProperties": {"$ref": "#"}, + "definitions": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "propertyNames": {"format": "regex"}, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/stringArray"}] + } + }, + "propertyNames": {"$ref": "#"}, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + {"$ref": "#/definitions/simpleTypes"}, + { + "type": "array", + "items": {"$ref": "#/definitions/simpleTypes"}, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": {"type": "string"}, + "contentMediaType": {"type": "string"}, + "contentEncoding": {"type": "string"}, + "if": {"$ref": "#"}, + "then": {"$ref": "#"}, + "else": {"$ref": "#"}, + "allOf": {"$ref": "#/definitions/schemaArray"}, + "anyOf": {"$ref": "#/definitions/schemaArray"}, + "oneOf": {"$ref": "#/definitions/schemaArray"}, + "not": {"$ref": "#"} + }, + "default": true +} diff --git a/website/node_modules/ajv/dist/refs/json-schema-secure.json b/website/node_modules/ajv/dist/refs/json-schema-secure.json new file mode 100644 index 00000000..3968abd5 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/json-schema-secure.json @@ -0,0 +1,88 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#", + "title": "Meta-schema for the security assessment of JSON Schemas", + "description": "If a JSON AnySchema fails validation against this meta-schema, it may be unsafe to validate untrusted data", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$ref": "#"} + } + }, + "dependencies": { + "patternProperties": { + "description": "prevent slow validation of large property names", + "required": ["propertyNames"], + "properties": { + "propertyNames": { + "required": ["maxLength"] + } + } + }, + "uniqueItems": { + "description": "prevent slow validation of large non-scalar arrays", + "if": { + "properties": { + "uniqueItems": {"const": true}, + "items": { + "properties": { + "type": { + "anyOf": [ + { + "enum": ["object", "array"] + }, + { + "type": "array", + "contains": {"enum": ["object", "array"]} + } + ] + } + } + } + } + }, + "then": { + "required": ["maxItems"] + } + }, + "pattern": { + "description": "prevent slow pattern matching of large strings", + "required": ["maxLength"] + }, + "format": { + "description": "prevent slow format validation of large strings", + "required": ["maxLength"] + } + }, + "properties": { + "additionalItems": {"$ref": "#"}, + "additionalProperties": {"$ref": "#"}, + "dependencies": { + "additionalProperties": { + "anyOf": [{"type": "array"}, {"$ref": "#"}] + } + }, + "items": { + "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/schemaArray"}] + }, + "definitions": { + "additionalProperties": {"$ref": "#"} + }, + "patternProperties": { + "additionalProperties": {"$ref": "#"} + }, + "properties": { + "additionalProperties": {"$ref": "#"} + }, + "if": {"$ref": "#"}, + "then": {"$ref": "#"}, + "else": {"$ref": "#"}, + "allOf": {"$ref": "#/definitions/schemaArray"}, + "anyOf": {"$ref": "#/definitions/schemaArray"}, + "oneOf": {"$ref": "#/definitions/schemaArray"}, + "not": {"$ref": "#"}, + "contains": {"$ref": "#"}, + "propertyNames": {"$ref": "#"} + } +} diff --git a/website/node_modules/ajv/dist/refs/jtd-schema.d.ts b/website/node_modules/ajv/dist/refs/jtd-schema.d.ts new file mode 100644 index 00000000..932797a3 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/jtd-schema.d.ts @@ -0,0 +1,3 @@ +import { SchemaObject } from "../types"; +declare const jtdMetaSchema: SchemaObject; +export default jtdMetaSchema; diff --git a/website/node_modules/ajv/dist/refs/jtd-schema.js b/website/node_modules/ajv/dist/refs/jtd-schema.js new file mode 100644 index 00000000..1ee940af --- /dev/null +++ b/website/node_modules/ajv/dist/refs/jtd-schema.js @@ -0,0 +1,118 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const shared = (root) => { + const sch = { + nullable: { type: "boolean" }, + metadata: { + optionalProperties: { + union: { elements: { ref: "schema" } }, + }, + additionalProperties: true, + }, + }; + if (root) + sch.definitions = { values: { ref: "schema" } }; + return sch; +}; +const emptyForm = (root) => ({ + optionalProperties: shared(root), +}); +const refForm = (root) => ({ + properties: { + ref: { type: "string" }, + }, + optionalProperties: shared(root), +}); +const typeForm = (root) => ({ + properties: { + type: { + enum: [ + "boolean", + "timestamp", + "string", + "float32", + "float64", + "int8", + "uint8", + "int16", + "uint16", + "int32", + "uint32", + ], + }, + }, + optionalProperties: shared(root), +}); +const enumForm = (root) => ({ + properties: { + enum: { elements: { type: "string" } }, + }, + optionalProperties: shared(root), +}); +const elementsForm = (root) => ({ + properties: { + elements: { ref: "schema" }, + }, + optionalProperties: shared(root), +}); +const propertiesForm = (root) => ({ + properties: { + properties: { values: { ref: "schema" } }, + }, + optionalProperties: { + optionalProperties: { values: { ref: "schema" } }, + additionalProperties: { type: "boolean" }, + ...shared(root), + }, +}); +const optionalPropertiesForm = (root) => ({ + properties: { + optionalProperties: { values: { ref: "schema" } }, + }, + optionalProperties: { + additionalProperties: { type: "boolean" }, + ...shared(root), + }, +}); +const discriminatorForm = (root) => ({ + properties: { + discriminator: { type: "string" }, + mapping: { + values: { + metadata: { + union: [propertiesForm(false), optionalPropertiesForm(false)], + }, + }, + }, + }, + optionalProperties: shared(root), +}); +const valuesForm = (root) => ({ + properties: { + values: { ref: "schema" }, + }, + optionalProperties: shared(root), +}); +const schema = (root) => ({ + metadata: { + union: [ + emptyForm, + refForm, + typeForm, + enumForm, + elementsForm, + propertiesForm, + optionalPropertiesForm, + discriminatorForm, + valuesForm, + ].map((s) => s(root)), + }, +}); +const jtdMetaSchema = { + definitions: { + schema: schema(false), + }, + ...schema(true), +}; +exports.default = jtdMetaSchema; +//# sourceMappingURL=jtd-schema.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/refs/jtd-schema.js.map b/website/node_modules/ajv/dist/refs/jtd-schema.js.map new file mode 100644 index 00000000..d46755b2 --- /dev/null +++ b/website/node_modules/ajv/dist/refs/jtd-schema.js.map @@ -0,0 +1 @@ +{"version":3,"file":"jtd-schema.js","sourceRoot":"","sources":["../../lib/refs/jtd-schema.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/equal.d.ts b/website/node_modules/ajv/dist/runtime/equal.d.ts new file mode 100644 index 00000000..777cae20 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/equal.d.ts @@ -0,0 +1,6 @@ +import * as equal from "fast-deep-equal"; +type Equal = typeof equal & { + code: string; +}; +declare const _default: Equal; +export default _default; diff --git a/website/node_modules/ajv/dist/runtime/equal.js b/website/node_modules/ajv/dist/runtime/equal.js new file mode 100644 index 00000000..774bba05 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/equal.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +// https://github.com/ajv-validator/ajv/issues/889 +const equal = require("fast-deep-equal"); +equal.code = 'require("ajv/dist/runtime/equal").default'; +exports.default = equal; +//# sourceMappingURL=equal.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/equal.js.map b/website/node_modules/ajv/dist/runtime/equal.js.map new file mode 100644 index 00000000..0e17901c --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/equal.js.map @@ -0,0 +1 @@ +{"version":3,"file":"equal.js","sourceRoot":"","sources":["../../lib/runtime/equal.ts"],"names":[],"mappings":";;AAAA,kDAAkD;AAClD,yCAAwC;AAGtC,KAAe,CAAC,IAAI,GAAG,2CAA2C,CAAA;AAEpE,kBAAe,KAAc,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/parseJson.d.ts b/website/node_modules/ajv/dist/runtime/parseJson.d.ts new file mode 100644 index 00000000..85f1d567 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/parseJson.d.ts @@ -0,0 +1,18 @@ +export declare function parseJson(s: string, pos: number): unknown; +export declare namespace parseJson { + var message: string | undefined; + var position: number; + var code: string; +} +export declare function parseJsonNumber(s: string, pos: number, maxDigits?: number): number | undefined; +export declare namespace parseJsonNumber { + var message: string | undefined; + var position: number; + var code: string; +} +export declare function parseJsonString(s: string, pos: number): string | undefined; +export declare namespace parseJsonString { + var message: string | undefined; + var position: number; + var code: string; +} diff --git a/website/node_modules/ajv/dist/runtime/parseJson.js b/website/node_modules/ajv/dist/runtime/parseJson.js new file mode 100644 index 00000000..7ebbc1bf --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/parseJson.js @@ -0,0 +1,184 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.parseJsonString = exports.parseJsonNumber = exports.parseJson = void 0; +const rxParseJson = /position\s(\d+)$/; +function parseJson(s, pos) { + let endPos; + parseJson.message = undefined; + let matches; + if (pos) + s = s.slice(pos); + try { + parseJson.position = pos + s.length; + return JSON.parse(s); + } + catch (e) { + matches = rxParseJson.exec(e.message); + if (!matches) { + parseJson.message = "unexpected end"; + return undefined; + } + endPos = +matches[1]; + const c = s[endPos]; + s = s.slice(0, endPos); + parseJson.position = pos + endPos; + try { + return JSON.parse(s); + } + catch (e1) { + parseJson.message = `unexpected token ${c}`; + return undefined; + } + } +} +exports.parseJson = parseJson; +parseJson.message = undefined; +parseJson.position = 0; +parseJson.code = 'require("ajv/dist/runtime/parseJson").parseJson'; +function parseJsonNumber(s, pos, maxDigits) { + let numStr = ""; + let c; + parseJsonNumber.message = undefined; + if (s[pos] === "-") { + numStr += "-"; + pos++; + } + if (s[pos] === "0") { + numStr += "0"; + pos++; + } + else { + if (!parseDigits(maxDigits)) { + errorMessage(); + return undefined; + } + } + if (maxDigits) { + parseJsonNumber.position = pos; + return +numStr; + } + if (s[pos] === ".") { + numStr += "."; + pos++; + if (!parseDigits()) { + errorMessage(); + return undefined; + } + } + if (((c = s[pos]), c === "e" || c === "E")) { + numStr += "e"; + pos++; + if (((c = s[pos]), c === "+" || c === "-")) { + numStr += c; + pos++; + } + if (!parseDigits()) { + errorMessage(); + return undefined; + } + } + parseJsonNumber.position = pos; + return +numStr; + function parseDigits(maxLen) { + let digit = false; + while (((c = s[pos]), c >= "0" && c <= "9" && (maxLen === undefined || maxLen-- > 0))) { + digit = true; + numStr += c; + pos++; + } + return digit; + } + function errorMessage() { + parseJsonNumber.position = pos; + parseJsonNumber.message = pos < s.length ? `unexpected token ${s[pos]}` : "unexpected end"; + } +} +exports.parseJsonNumber = parseJsonNumber; +parseJsonNumber.message = undefined; +parseJsonNumber.position = 0; +parseJsonNumber.code = 'require("ajv/dist/runtime/parseJson").parseJsonNumber'; +const escapedChars = { + b: "\b", + f: "\f", + n: "\n", + r: "\r", + t: "\t", + '"': '"', + "/": "/", + "\\": "\\", +}; +const CODE_A = "a".charCodeAt(0); +const CODE_0 = "0".charCodeAt(0); +function parseJsonString(s, pos) { + let str = ""; + let c; + parseJsonString.message = undefined; + // eslint-disable-next-line no-constant-condition, @typescript-eslint/no-unnecessary-condition + while (true) { + c = s[pos++]; + if (c === '"') + break; + if (c === "\\") { + c = s[pos]; + if (c in escapedChars) { + str += escapedChars[c]; + pos++; + } + else if (c === "u") { + pos++; + let count = 4; + let code = 0; + while (count--) { + code <<= 4; + c = s[pos]; + if (c === undefined) { + errorMessage("unexpected end"); + return undefined; + } + c = c.toLowerCase(); + if (c >= "a" && c <= "f") { + code += c.charCodeAt(0) - CODE_A + 10; + } + else if (c >= "0" && c <= "9") { + code += c.charCodeAt(0) - CODE_0; + } + else { + errorMessage(`unexpected token ${c}`); + return undefined; + } + pos++; + } + str += String.fromCharCode(code); + } + else { + errorMessage(`unexpected token ${c}`); + return undefined; + } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + } + else if (c === undefined) { + errorMessage("unexpected end"); + return undefined; + } + else { + if (c.charCodeAt(0) >= 0x20) { + str += c; + } + else { + errorMessage(`unexpected token ${c}`); + return undefined; + } + } + } + parseJsonString.position = pos; + return str; + function errorMessage(msg) { + parseJsonString.position = pos; + parseJsonString.message = msg; + } +} +exports.parseJsonString = parseJsonString; +parseJsonString.message = undefined; +parseJsonString.position = 0; +parseJsonString.code = 'require("ajv/dist/runtime/parseJson").parseJsonString'; +//# sourceMappingURL=parseJson.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/parseJson.js.map b/website/node_modules/ajv/dist/runtime/parseJson.js.map new file mode 100644 index 00000000..7f239fb7 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/parseJson.js.map @@ -0,0 +1 @@ +{"version":3,"file":"parseJson.js","sourceRoot":"","sources":["../../lib/runtime/parseJson.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/quote.d.ts b/website/node_modules/ajv/dist/runtime/quote.d.ts new file mode 100644 index 00000000..0579dd3c --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/quote.d.ts @@ -0,0 +1,5 @@ +declare function quote(s: string): string; +declare namespace quote { + var code: string; +} +export default quote; diff --git a/website/node_modules/ajv/dist/runtime/quote.js b/website/node_modules/ajv/dist/runtime/quote.js new file mode 100644 index 00000000..ebf78f70 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/quote.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const rxEscapable = +// eslint-disable-next-line no-control-regex, no-misleading-character-class +/[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; +const escaped = { + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + '"': '\\"', + "\\": "\\\\", +}; +function quote(s) { + rxEscapable.lastIndex = 0; + return ('"' + + (rxEscapable.test(s) + ? s.replace(rxEscapable, (a) => { + const c = escaped[a]; + return typeof c === "string" + ? c + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + : s) + + '"'); +} +exports.default = quote; +quote.code = 'require("ajv/dist/runtime/quote").default'; +//# sourceMappingURL=quote.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/quote.js.map b/website/node_modules/ajv/dist/runtime/quote.js.map new file mode 100644 index 00000000..4d226252 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/quote.js.map @@ -0,0 +1 @@ +{"version":3,"file":"quote.js","sourceRoot":"","sources":["../../lib/runtime/quote.ts"],"names":[],"mappings":";;AAAA,MAAM,WAAW;AACf,2EAA2E;AAC3E,iIAAiI,CAAA;AAEnI,MAAM,OAAO,GAA6B;IACxC,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,KAAK;IACX,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,MAAM;CACb,CAAA;AAED,SAAwB,KAAK,CAAC,CAAS;IACrC,WAAW,CAAC,SAAS,GAAG,CAAC,CAAA;IACzB,OAAO,CACL,GAAG;QACH,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE;gBAC3B,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;gBACpB,OAAO,OAAO,CAAC,KAAK,QAAQ;oBAC1B,CAAC,CAAC,CAAC;oBACH,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAC/D,CAAC,CAAC;YACJ,CAAC,CAAC,CAAC,CAAC;QACN,GAAG,CACJ,CAAA;AACH,CAAC;AAdD,wBAcC;AAED,KAAK,CAAC,IAAI,GAAG,2CAA2C,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/re2.d.ts b/website/node_modules/ajv/dist/runtime/re2.d.ts new file mode 100644 index 00000000..c34a98f2 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/re2.d.ts @@ -0,0 +1,6 @@ +import * as re2 from "re2"; +type Re2 = typeof re2 & { + code: string; +}; +declare const _default: Re2; +export default _default; diff --git a/website/node_modules/ajv/dist/runtime/re2.js b/website/node_modules/ajv/dist/runtime/re2.js new file mode 100644 index 00000000..4b1ee253 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/re2.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const re2 = require("re2"); +re2.code = 'require("ajv/dist/runtime/re2").default'; +exports.default = re2; +//# sourceMappingURL=re2.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/re2.js.map b/website/node_modules/ajv/dist/runtime/re2.js.map new file mode 100644 index 00000000..bb938a2c --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/re2.js.map @@ -0,0 +1 @@ +{"version":3,"file":"re2.js","sourceRoot":"","sources":["../../lib/runtime/re2.ts"],"names":[],"mappings":";;AAAA,2BAA0B;AAGxB,GAAW,CAAC,IAAI,GAAG,yCAAyC,CAAA;AAE9D,kBAAe,GAAU,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/timestamp.d.ts b/website/node_modules/ajv/dist/runtime/timestamp.d.ts new file mode 100644 index 00000000..cd483268 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/timestamp.d.ts @@ -0,0 +1,5 @@ +declare function validTimestamp(str: string, allowDate: boolean): boolean; +declare namespace validTimestamp { + var code: string; +} +export default validTimestamp; diff --git a/website/node_modules/ajv/dist/runtime/timestamp.js b/website/node_modules/ajv/dist/runtime/timestamp.js new file mode 100644 index 00000000..5e0f0656 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/timestamp.js @@ -0,0 +1,42 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const DT_SEPARATOR = /t|\s/i; +const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/; +const TIME = /^(\d\d):(\d\d):(\d\d)(?:\.\d+)?(?:z|([+-]\d\d)(?::?(\d\d))?)$/i; +const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; +function validTimestamp(str, allowDate) { + // http://tools.ietf.org/html/rfc3339#section-5.6 + const dt = str.split(DT_SEPARATOR); + return ((dt.length === 2 && validDate(dt[0]) && validTime(dt[1])) || + (allowDate && dt.length === 1 && validDate(dt[0]))); +} +exports.default = validTimestamp; +function validDate(str) { + const matches = DATE.exec(str); + if (!matches) + return false; + const y = +matches[1]; + const m = +matches[2]; + const d = +matches[3]; + return (m >= 1 && + m <= 12 && + d >= 1 && + (d <= DAYS[m] || + // leap year: https://tools.ietf.org/html/rfc3339#appendix-C + (m === 2 && d === 29 && (y % 100 === 0 ? y % 400 === 0 : y % 4 === 0)))); +} +function validTime(str) { + const matches = TIME.exec(str); + if (!matches) + return false; + const hr = +matches[1]; + const min = +matches[2]; + const sec = +matches[3]; + const tzH = +(matches[4] || 0); + const tzM = +(matches[5] || 0); + return ((hr <= 23 && min <= 59 && sec <= 59) || + // leap second + (hr - tzH === 23 && min - tzM === 59 && sec === 60)); +} +validTimestamp.code = 'require("ajv/dist/runtime/timestamp").default'; +//# sourceMappingURL=timestamp.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/timestamp.js.map b/website/node_modules/ajv/dist/runtime/timestamp.js.map new file mode 100644 index 00000000..6b0eee03 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/timestamp.js.map @@ -0,0 +1 @@ +{"version":3,"file":"timestamp.js","sourceRoot":"","sources":["../../lib/runtime/timestamp.ts"],"names":[],"mappings":";;AAAA,MAAM,YAAY,GAAG,OAAO,CAAA;AAC5B,MAAM,IAAI,GAAG,4BAA4B,CAAA;AACzC,MAAM,IAAI,GAAG,gEAAgE,CAAA;AAC7E,MAAM,IAAI,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;AAEhE,SAAwB,cAAc,CAAC,GAAW,EAAE,SAAkB;IACpE,iDAAiD;IACjD,MAAM,EAAE,GAAa,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IAC5C,OAAO,CACL,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACzD,CAAC,SAAS,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CACnD,CAAA;AACH,CAAC;AAPD,iCAOC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,MAAM,OAAO,GAAoB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC/C,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAA;IAC1B,MAAM,CAAC,GAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7B,MAAM,CAAC,GAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7B,MAAM,CAAC,GAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7B,OAAO,CACL,CAAC,IAAI,CAAC;QACN,CAAC,IAAI,EAAE;QACP,CAAC,IAAI,CAAC;QACN,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;YACX,4DAA4D;YAC5D,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAC1E,CAAA;AACH,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,MAAM,OAAO,GAAoB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC/C,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAA;IAC1B,MAAM,EAAE,GAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC9B,MAAM,GAAG,GAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC/B,MAAM,GAAG,GAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC/B,MAAM,GAAG,GAAW,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IACtC,MAAM,GAAG,GAAW,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IACtC,OAAO,CACL,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,CAAC;QACpC,cAAc;QACd,CAAC,EAAE,GAAG,GAAG,KAAK,EAAE,IAAI,GAAG,GAAG,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,EAAE,CAAC,CACpD,CAAA;AACH,CAAC;AAED,cAAc,CAAC,IAAI,GAAG,+CAA+C,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/ucs2length.d.ts b/website/node_modules/ajv/dist/runtime/ucs2length.d.ts new file mode 100644 index 00000000..ecbee69c --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/ucs2length.d.ts @@ -0,0 +1,5 @@ +declare function ucs2length(str: string): number; +declare namespace ucs2length { + var code: string; +} +export default ucs2length; diff --git a/website/node_modules/ajv/dist/runtime/ucs2length.js b/website/node_modules/ajv/dist/runtime/ucs2length.js new file mode 100644 index 00000000..92ea0c08 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/ucs2length.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +// https://mathiasbynens.be/notes/javascript-encoding +// https://github.com/bestiejs/punycode.js - punycode.ucs2.decode +function ucs2length(str) { + const len = str.length; + let length = 0; + let pos = 0; + let value; + while (pos < len) { + length++; + value = str.charCodeAt(pos++); + if (value >= 0xd800 && value <= 0xdbff && pos < len) { + // high surrogate, and there is a next character + value = str.charCodeAt(pos); + if ((value & 0xfc00) === 0xdc00) + pos++; // low surrogate + } + } + return length; +} +exports.default = ucs2length; +ucs2length.code = 'require("ajv/dist/runtime/ucs2length").default'; +//# sourceMappingURL=ucs2length.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/ucs2length.js.map b/website/node_modules/ajv/dist/runtime/ucs2length.js.map new file mode 100644 index 00000000..7c7f7886 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/ucs2length.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ucs2length.js","sourceRoot":"","sources":["../../lib/runtime/ucs2length.ts"],"names":[],"mappings":";;AAAA,qDAAqD;AACrD,iEAAiE;AACjE,SAAwB,UAAU,CAAC,GAAW;IAC5C,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAA;IACtB,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,IAAI,GAAG,GAAG,CAAC,CAAA;IACX,IAAI,KAAa,CAAA;IACjB,OAAO,GAAG,GAAG,GAAG,EAAE;QAChB,MAAM,EAAE,CAAA;QACR,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,CAAA;QAC7B,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,IAAI,GAAG,GAAG,GAAG,EAAE;YACnD,gDAAgD;YAChD,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;YAC3B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,MAAM;gBAAE,GAAG,EAAE,CAAA,CAAC,gBAAgB;SACxD;KACF;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAfD,6BAeC;AAED,UAAU,CAAC,IAAI,GAAG,gDAAgD,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/uri.d.ts b/website/node_modules/ajv/dist/runtime/uri.d.ts new file mode 100644 index 00000000..4690868f --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/uri.d.ts @@ -0,0 +1,6 @@ +import * as uri from "uri-js"; +type URI = typeof uri & { + code: string; +}; +declare const _default: URI; +export default _default; diff --git a/website/node_modules/ajv/dist/runtime/uri.js b/website/node_modules/ajv/dist/runtime/uri.js new file mode 100644 index 00000000..fbd38151 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/uri.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const uri = require("uri-js"); +uri.code = 'require("ajv/dist/runtime/uri").default'; +exports.default = uri; +//# sourceMappingURL=uri.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/uri.js.map b/website/node_modules/ajv/dist/runtime/uri.js.map new file mode 100644 index 00000000..701bed95 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/uri.js.map @@ -0,0 +1 @@ +{"version":3,"file":"uri.js","sourceRoot":"","sources":["../../lib/runtime/uri.ts"],"names":[],"mappings":";;AAAA,8BAA6B;AAG3B,GAAW,CAAC,IAAI,GAAG,yCAAyC,CAAA;AAE9D,kBAAe,GAAU,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/validation_error.d.ts b/website/node_modules/ajv/dist/runtime/validation_error.d.ts new file mode 100644 index 00000000..b0ee9698 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/validation_error.d.ts @@ -0,0 +1,7 @@ +import type { ErrorObject } from "../types"; +export default class ValidationError extends Error { + readonly errors: Partial[]; + readonly ajv: true; + readonly validation: true; + constructor(errors: Partial[]); +} diff --git a/website/node_modules/ajv/dist/runtime/validation_error.js b/website/node_modules/ajv/dist/runtime/validation_error.js new file mode 100644 index 00000000..353502c0 --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/validation_error.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class ValidationError extends Error { + constructor(errors) { + super("validation failed"); + this.errors = errors; + this.ajv = this.validation = true; + } +} +exports.default = ValidationError; +//# sourceMappingURL=validation_error.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/runtime/validation_error.js.map b/website/node_modules/ajv/dist/runtime/validation_error.js.map new file mode 100644 index 00000000..70206fbc --- /dev/null +++ b/website/node_modules/ajv/dist/runtime/validation_error.js.map @@ -0,0 +1 @@ +{"version":3,"file":"validation_error.js","sourceRoot":"","sources":["../../lib/runtime/validation_error.ts"],"names":[],"mappings":";;AAEA,MAAqB,eAAgB,SAAQ,KAAK;IAKhD,YAAY,MAA8B;QACxC,KAAK,CAAC,mBAAmB,CAAC,CAAA;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;IACnC,CAAC;CACF;AAVD,kCAUC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/standalone/index.d.ts b/website/node_modules/ajv/dist/standalone/index.d.ts new file mode 100644 index 00000000..a9141c3d --- /dev/null +++ b/website/node_modules/ajv/dist/standalone/index.d.ts @@ -0,0 +1,6 @@ +import type AjvCore from "../core"; +import type { AnyValidateFunction } from "../types"; +declare function standaloneCode(ajv: AjvCore, refsOrFunc?: { + [K in string]?: string; +} | AnyValidateFunction): string; +export default standaloneCode; diff --git a/website/node_modules/ajv/dist/standalone/index.js b/website/node_modules/ajv/dist/standalone/index.js new file mode 100644 index 00000000..b44bb5db --- /dev/null +++ b/website/node_modules/ajv/dist/standalone/index.js @@ -0,0 +1,90 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const scope_1 = require("../compile/codegen/scope"); +const code_1 = require("../compile/codegen/code"); +function standaloneCode(ajv, refsOrFunc) { + if (!ajv.opts.code.source) { + throw new Error("moduleCode: ajv instance must have code.source option"); + } + const { _n } = ajv.scope.opts; + return typeof refsOrFunc == "function" + ? funcExportCode(refsOrFunc.source) + : refsOrFunc !== undefined + ? multiExportsCode(refsOrFunc, getValidate) + : multiExportsCode(ajv.schemas, (sch) => sch.meta ? undefined : ajv.compile(sch.schema)); + function getValidate(id) { + const v = ajv.getSchema(id); + if (!v) + throw new Error(`moduleCode: no schema with id ${id}`); + return v; + } + function funcExportCode(source) { + const usedValues = {}; + const n = source === null || source === void 0 ? void 0 : source.validateName; + const vCode = validateCode(usedValues, source); + if (ajv.opts.code.esm) { + // Always do named export as `validate` rather than the variable `n` which is `validateXX` for known export value + return `"use strict";${_n}export const validate = ${n};${_n}export default ${n};${_n}${vCode}`; + } + return `"use strict";${_n}module.exports = ${n};${_n}module.exports.default = ${n};${_n}${vCode}`; + } + function multiExportsCode(schemas, getValidateFunc) { + var _a; + const usedValues = {}; + let code = (0, code_1._) `"use strict";`; + for (const name in schemas) { + const v = getValidateFunc(schemas[name]); + if (v) { + const vCode = validateCode(usedValues, v.source); + const exportSyntax = ajv.opts.code.esm + ? (0, code_1._) `export const ${(0, code_1.getEsmExportName)(name)}` + : (0, code_1._) `exports${(0, code_1.getProperty)(name)}`; + code = (0, code_1._) `${code}${_n}${exportSyntax} = ${(_a = v.source) === null || _a === void 0 ? void 0 : _a.validateName};${_n}${vCode}`; + } + } + return `${code}`; + } + function validateCode(usedValues, s) { + if (!s) + throw new Error('moduleCode: function does not have "source" property'); + if (usedState(s.validateName) === scope_1.UsedValueState.Completed) + return code_1.nil; + setUsedState(s.validateName, scope_1.UsedValueState.Started); + const scopeCode = ajv.scope.scopeCode(s.scopeValues, usedValues, refValidateCode); + const code = new code_1._Code(`${scopeCode}${_n}${s.validateCode}`); + return s.evaluated ? (0, code_1._) `${code}${s.validateName}.evaluated = ${s.evaluated};${_n}` : code; + function refValidateCode(n) { + var _a; + const vRef = (_a = n.value) === null || _a === void 0 ? void 0 : _a.ref; + if (n.prefix === "validate" && typeof vRef == "function") { + const v = vRef; + return validateCode(usedValues, v.source); + } + else if ((n.prefix === "root" || n.prefix === "wrapper") && typeof vRef == "object") { + const { validate, validateName } = vRef; + if (!validateName) + throw new Error("ajv internal error"); + const def = ajv.opts.code.es5 ? scope_1.varKinds.var : scope_1.varKinds.const; + const wrapper = (0, code_1._) `${def} ${n} = {validate: ${validateName}};`; + if (usedState(validateName) === scope_1.UsedValueState.Started) + return wrapper; + const vCode = validateCode(usedValues, validate === null || validate === void 0 ? void 0 : validate.source); + return (0, code_1._) `${wrapper}${_n}${vCode}`; + } + return undefined; + } + function usedState(name) { + var _a; + return (_a = usedValues[name.prefix]) === null || _a === void 0 ? void 0 : _a.get(name); + } + function setUsedState(name, state) { + const { prefix } = name; + const names = (usedValues[prefix] = usedValues[prefix] || new Map()); + names.set(name, state); + } + } +} +module.exports = exports = standaloneCode; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = standaloneCode; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/standalone/index.js.map b/website/node_modules/ajv/dist/standalone/index.js.map new file mode 100644 index 00000000..11864d36 --- /dev/null +++ b/website/node_modules/ajv/dist/standalone/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/standalone/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/standalone/instance.d.ts b/website/node_modules/ajv/dist/standalone/instance.d.ts new file mode 100644 index 00000000..156ac322 --- /dev/null +++ b/website/node_modules/ajv/dist/standalone/instance.d.ts @@ -0,0 +1,12 @@ +import Ajv, { AnySchema, AnyValidateFunction, ErrorObject } from "../core"; +export default class AjvPack { + readonly ajv: Ajv; + errors?: ErrorObject[] | null; + constructor(ajv: Ajv); + validate(schemaKeyRef: AnySchema | string, data: unknown): boolean | Promise; + compile(schema: AnySchema, meta?: boolean): AnyValidateFunction; + getSchema(keyRef: string): AnyValidateFunction | undefined; + private getStandalone; + addSchema(...args: Parameters): AjvPack; + addKeyword(...args: Parameters): AjvPack; +} diff --git a/website/node_modules/ajv/dist/standalone/instance.js b/website/node_modules/ajv/dist/standalone/instance.js new file mode 100644 index 00000000..35e5c992 --- /dev/null +++ b/website/node_modules/ajv/dist/standalone/instance.js @@ -0,0 +1,35 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const core_1 = require("../core"); +const _1 = require("."); +const requireFromString = require("require-from-string"); +class AjvPack { + constructor(ajv) { + this.ajv = ajv; + } + validate(schemaKeyRef, data) { + return core_1.default.prototype.validate.call(this, schemaKeyRef, data); + } + compile(schema, meta) { + return this.getStandalone(this.ajv.compile(schema, meta)); + } + getSchema(keyRef) { + const v = this.ajv.getSchema(keyRef); + if (!v) + return undefined; + return this.getStandalone(v); + } + getStandalone(v) { + return requireFromString((0, _1.default)(this.ajv, v)); + } + addSchema(...args) { + this.ajv.addSchema.call(this.ajv, ...args); + return this; + } + addKeyword(...args) { + this.ajv.addKeyword.call(this.ajv, ...args); + return this; + } +} +exports.default = AjvPack; +//# sourceMappingURL=instance.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/standalone/instance.js.map b/website/node_modules/ajv/dist/standalone/instance.js.map new file mode 100644 index 00000000..6ac33b11 --- /dev/null +++ b/website/node_modules/ajv/dist/standalone/instance.js.map @@ -0,0 +1 @@ +{"version":3,"file":"instance.js","sourceRoot":"","sources":["../../lib/standalone/instance.ts"],"names":[],"mappings":";;AAAA,kCAAwE;AACxE,wBAA8B;AAC9B,yDAAwD;AAExD,MAAqB,OAAO;IAE1B,YAAqB,GAAQ;QAAR,QAAG,GAAH,GAAG,CAAK;IAAG,CAAC;IAEjC,QAAQ,CAAC,YAAgC,EAAE,IAAa;QACtD,OAAO,cAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,CAAA;IAC9D,CAAC;IAED,OAAO,CAAc,MAAiB,EAAE,IAAc;QACpD,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAI,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;IAC9D,CAAC;IAED,SAAS,CAAc,MAAc;QACnC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAI,MAAM,CAAC,CAAA;QACvC,IAAI,CAAC,CAAC;YAAE,OAAO,SAAS,CAAA;QACxB,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAA;IAC9B,CAAC;IAEO,aAAa,CAAc,CAAyB;QAC1D,OAAO,iBAAiB,CAAC,IAAA,UAAc,EAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAA2B,CAAA;IACjF,CAAC;IAED,SAAS,CAAC,GAAG,IAAgD;QAC3D,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;QAC1C,OAAO,IAAI,CAAA;IACb,CAAC;IAED,UAAU,CAAC,GAAG,IAAiD;QAC7D,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;QAC3C,OAAO,IAAI,CAAA;IACb,CAAC;CACF;AA/BD,0BA+BC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/types/index.d.ts b/website/node_modules/ajv/dist/types/index.d.ts new file mode 100644 index 00000000..485440e9 --- /dev/null +++ b/website/node_modules/ajv/dist/types/index.d.ts @@ -0,0 +1,183 @@ +import * as URI from "uri-js"; +import type { CodeGen, Code, Name, ScopeValueSets, ValueScopeName } from "../compile/codegen"; +import type { SchemaEnv, SchemaCxt, SchemaObjCxt } from "../compile"; +import type { JSONType } from "../compile/rules"; +import type { KeywordCxt } from "../compile/validate"; +import type Ajv from "../core"; +interface _SchemaObject { + id?: string; + $id?: string; + $schema?: string; + [x: string]: any; +} +export interface SchemaObject extends _SchemaObject { + id?: string; + $id?: string; + $schema?: string; + $async?: false; + [x: string]: any; +} +export interface AsyncSchema extends _SchemaObject { + $async: true; +} +export type AnySchemaObject = SchemaObject | AsyncSchema; +export type Schema = SchemaObject | boolean; +export type AnySchema = Schema | AsyncSchema; +export type SchemaMap = { + [Key in string]?: AnySchema; +}; +export interface SourceCode { + validateName: ValueScopeName; + validateCode: string; + scopeValues: ScopeValueSets; + evaluated?: Code; +} +export interface DataValidationCxt { + instancePath: string; + parentData: { + [K in T]: any; + }; + parentDataProperty: T; + rootData: Record | any[]; + dynamicAnchors: { + [Ref in string]?: ValidateFunction; + }; +} +export interface ValidateFunction { + (this: Ajv | any, data: any, dataCxt?: DataValidationCxt): data is T; + errors?: null | ErrorObject[]; + evaluated?: Evaluated; + schema: AnySchema; + schemaEnv: SchemaEnv; + source?: SourceCode; +} +export interface JTDParser { + (json: string): T | undefined; + message?: string; + position?: number; +} +export type EvaluatedProperties = { + [K in string]?: true; +} | true; +export type EvaluatedItems = number | true; +export interface Evaluated { + props?: EvaluatedProperties; + items?: EvaluatedItems; + dynamicProps: boolean; + dynamicItems: boolean; +} +export interface AsyncValidateFunction extends ValidateFunction { + (...args: Parameters>): Promise; + $async: true; +} +export type AnyValidateFunction = ValidateFunction | AsyncValidateFunction; +export interface ErrorObject, S = unknown> { + keyword: K; + instancePath: string; + schemaPath: string; + params: P; + propertyName?: string; + message?: string; + schema?: S; + parentSchema?: AnySchemaObject; + data?: unknown; +} +export type ErrorNoParams = ErrorObject, S>; +interface _KeywordDef { + keyword: string | string[]; + type?: JSONType | JSONType[]; + schemaType?: JSONType | JSONType[]; + allowUndefined?: boolean; + $data?: boolean; + implements?: string[]; + before?: string; + post?: boolean; + metaSchema?: AnySchemaObject; + validateSchema?: AnyValidateFunction; + dependencies?: string[]; + error?: KeywordErrorDefinition; + $dataError?: KeywordErrorDefinition; +} +export interface CodeKeywordDefinition extends _KeywordDef { + code: (cxt: KeywordCxt, ruleType?: string) => void; + trackErrors?: boolean; +} +export type MacroKeywordFunc = (schema: any, parentSchema: AnySchemaObject, it: SchemaCxt) => AnySchema; +export type CompileKeywordFunc = (schema: any, parentSchema: AnySchemaObject, it: SchemaObjCxt) => DataValidateFunction; +export interface DataValidateFunction { + (...args: Parameters): boolean | Promise; + errors?: Partial[]; +} +export interface SchemaValidateFunction { + (schema: any, data: any, parentSchema?: AnySchemaObject, dataCxt?: DataValidationCxt): boolean | Promise; + errors?: Partial[]; +} +export interface FuncKeywordDefinition extends _KeywordDef { + validate?: SchemaValidateFunction | DataValidateFunction; + compile?: CompileKeywordFunc; + schema?: boolean; + modifying?: boolean; + async?: boolean; + valid?: boolean; + errors?: boolean | "full"; +} +export interface MacroKeywordDefinition extends FuncKeywordDefinition { + macro: MacroKeywordFunc; +} +export type KeywordDefinition = CodeKeywordDefinition | FuncKeywordDefinition | MacroKeywordDefinition; +export type AddedKeywordDefinition = KeywordDefinition & { + type: JSONType[]; + schemaType: JSONType[]; +}; +export interface KeywordErrorDefinition { + message: string | Code | ((cxt: KeywordErrorCxt) => string | Code); + params?: Code | ((cxt: KeywordErrorCxt) => Code); +} +export type Vocabulary = (KeywordDefinition | string)[]; +export interface KeywordErrorCxt { + gen: CodeGen; + keyword: string; + data: Name; + $data?: string | false; + schema: any; + parentSchema?: AnySchemaObject; + schemaCode: Code | number | boolean; + schemaValue: Code | number | boolean; + schemaType?: JSONType[]; + errsCount?: Name; + params: KeywordCxtParams; + it: SchemaCxt; +} +export type KeywordCxtParams = { + [P in string]?: Code | string | number; +}; +export type FormatValidator = (data: T) => boolean; +export type FormatCompare = (data1: T, data2: T) => number | undefined; +export type AsyncFormatValidator = (data: T) => Promise; +export interface FormatDefinition { + type?: T extends string ? "string" | undefined : "number"; + validate: FormatValidator | (T extends string ? string | RegExp : never); + async?: false | undefined; + compare?: FormatCompare; +} +export interface AsyncFormatDefinition { + type?: T extends string ? "string" | undefined : "number"; + validate: AsyncFormatValidator; + async: true; + compare?: FormatCompare; +} +export type AddedFormat = true | RegExp | FormatValidator | FormatDefinition | FormatDefinition | AsyncFormatDefinition | AsyncFormatDefinition; +export type Format = AddedFormat | string; +export interface RegExpEngine { + (pattern: string, u: string): RegExpLike; + code: string; +} +export interface RegExpLike { + test: (s: string) => boolean; +} +export interface UriResolver { + parse(uri: string): URI.URIComponents; + resolve(base: string, path: string): string; + serialize(component: URI.URIComponents): string; +} +export {}; diff --git a/website/node_modules/ajv/dist/types/index.js b/website/node_modules/ajv/dist/types/index.js new file mode 100644 index 00000000..aa219d8f --- /dev/null +++ b/website/node_modules/ajv/dist/types/index.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/types/index.js.map b/website/node_modules/ajv/dist/types/index.js.map new file mode 100644 index 00000000..1f80f85e --- /dev/null +++ b/website/node_modules/ajv/dist/types/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/types/index.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/types/json-schema.d.ts b/website/node_modules/ajv/dist/types/json-schema.d.ts new file mode 100644 index 00000000..4774233f --- /dev/null +++ b/website/node_modules/ajv/dist/types/json-schema.d.ts @@ -0,0 +1,125 @@ +type StrictNullChecksWrapper = undefined extends null ? `strictNullChecks must be true in tsconfig to use ${Name}` : Type; +type UnionToIntersection = (U extends any ? (_: U) => void : never) extends (_: infer I) => void ? I : never; +export type SomeJSONSchema = UncheckedJSONSchemaType; +type UncheckedPartialSchema = Partial>; +export type PartialSchema = StrictNullChecksWrapper<"PartialSchema", UncheckedPartialSchema>; +type JSONType = IsPartial extends true ? T | undefined : T; +interface NumberKeywords { + minimum?: number; + maximum?: number; + exclusiveMinimum?: number; + exclusiveMaximum?: number; + multipleOf?: number; + format?: string; +} +interface StringKeywords { + minLength?: number; + maxLength?: number; + pattern?: string; + format?: string; +} +type UncheckedJSONSchemaType = (// these two unions allow arbitrary unions of types +{ + anyOf: readonly UncheckedJSONSchemaType[]; +} | { + oneOf: readonly UncheckedJSONSchemaType[]; +} | ({ + type: readonly (T extends number ? JSONType<"number" | "integer", IsPartial> : T extends string ? JSONType<"string", IsPartial> : T extends boolean ? JSONType<"boolean", IsPartial> : never)[]; +} & UnionToIntersection) | ((T extends number ? { + type: JSONType<"number" | "integer", IsPartial>; +} & NumberKeywords : T extends string ? { + type: JSONType<"string", IsPartial>; +} & StringKeywords : T extends boolean ? { + type: JSONType<"boolean", IsPartial>; +} : T extends readonly [any, ...any[]] ? { + type: JSONType<"array", IsPartial>; + items: { + readonly [K in keyof T]-?: UncheckedJSONSchemaType & Nullable; + } & { + length: T["length"]; + }; + minItems: T["length"]; +} & ({ + maxItems: T["length"]; +} | { + additionalItems: false; +}) : T extends readonly any[] ? { + type: JSONType<"array", IsPartial>; + items: UncheckedJSONSchemaType; + contains?: UncheckedPartialSchema; + minItems?: number; + maxItems?: number; + minContains?: number; + maxContains?: number; + uniqueItems?: true; + additionalItems?: never; +} : T extends Record ? { + type: JSONType<"object", IsPartial>; + additionalProperties?: boolean | UncheckedJSONSchemaType; + unevaluatedProperties?: boolean | UncheckedJSONSchemaType; + properties?: IsPartial extends true ? Partial> : UncheckedPropertiesSchema; + patternProperties?: Record>; + propertyNames?: Omit, "type"> & { + type?: "string"; + }; + dependencies?: { + [K in keyof T]?: Readonly<(keyof T)[]> | UncheckedPartialSchema; + }; + dependentRequired?: { + [K in keyof T]?: Readonly<(keyof T)[]>; + }; + dependentSchemas?: { + [K in keyof T]?: UncheckedPartialSchema; + }; + minProperties?: number; + maxProperties?: number; +} & (IsPartial extends true ? { + required: Readonly<(keyof T)[]>; +} : [UncheckedRequiredMembers] extends [never] ? { + required?: Readonly[]>; +} : { + required: Readonly[]>; +}) : T extends null ? { + type: JSONType<"null", IsPartial>; + nullable: true; +} : never) & { + allOf?: Readonly[]>; + anyOf?: Readonly[]>; + oneOf?: Readonly[]>; + if?: UncheckedPartialSchema; + then?: UncheckedPartialSchema; + else?: UncheckedPartialSchema; + not?: UncheckedPartialSchema; +})) & { + [keyword: string]: any; + $id?: string; + $ref?: string; + $defs?: Record>; + definitions?: Record>; +}; +export type JSONSchemaType = StrictNullChecksWrapper<"JSONSchemaType", UncheckedJSONSchemaType>; +type Known = { + [key: string]: Known; +} | [Known, ...Known[]] | Known[] | number | string | boolean | null; +type UncheckedPropertiesSchema = { + [K in keyof T]-?: (UncheckedJSONSchemaType & Nullable) | { + $ref: string; + }; +}; +export type PropertiesSchema = StrictNullChecksWrapper<"PropertiesSchema", UncheckedPropertiesSchema>; +type UncheckedRequiredMembers = { + [K in keyof T]-?: undefined extends T[K] ? never : K; +}[keyof T]; +export type RequiredMembers = StrictNullChecksWrapper<"RequiredMembers", UncheckedRequiredMembers>; +type Nullable = undefined extends T ? { + nullable: true; + const?: null; + enum?: Readonly<(T | null)[]>; + default?: T | null; +} : { + nullable?: false; + const?: T; + enum?: Readonly; + default?: T; +}; +export {}; diff --git a/website/node_modules/ajv/dist/types/json-schema.js b/website/node_modules/ajv/dist/types/json-schema.js new file mode 100644 index 00000000..2d8f98dc --- /dev/null +++ b/website/node_modules/ajv/dist/types/json-schema.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=json-schema.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/types/json-schema.js.map b/website/node_modules/ajv/dist/types/json-schema.js.map new file mode 100644 index 00000000..ae6b4d0f --- /dev/null +++ b/website/node_modules/ajv/dist/types/json-schema.js.map @@ -0,0 +1 @@ +{"version":3,"file":"json-schema.js","sourceRoot":"","sources":["../../lib/types/json-schema.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/types/jtd-schema.d.ts b/website/node_modules/ajv/dist/types/jtd-schema.d.ts new file mode 100644 index 00000000..80ad3129 --- /dev/null +++ b/website/node_modules/ajv/dist/types/jtd-schema.d.ts @@ -0,0 +1,174 @@ +/** numeric strings */ +type NumberType = "float32" | "float64" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32"; +/** string strings */ +type StringType = "string" | "timestamp"; +/** Generic JTD Schema without inference of the represented type */ +export type SomeJTDSchemaType = (// ref +{ + ref: string; +} | { + type: NumberType | StringType | "boolean"; +} | { + enum: string[]; +} | { + elements: SomeJTDSchemaType; +} | { + values: SomeJTDSchemaType; +} | { + properties: Record; + optionalProperties?: Record; + additionalProperties?: boolean; +} | { + properties?: Record; + optionalProperties: Record; + additionalProperties?: boolean; +} | { + discriminator: string; + mapping: Record; +} | {}) & { + nullable?: boolean; + metadata?: Record; + definitions?: Record; +}; +/** required keys of an object, not undefined */ +type RequiredKeys = { + [K in keyof T]-?: undefined extends T[K] ? never : K; +}[keyof T]; +/** optional or undifined-able keys of an object */ +type OptionalKeys = { + [K in keyof T]-?: undefined extends T[K] ? K : never; +}[keyof T]; +/** type is true if T is a union type */ +type IsUnion_ = false extends (T extends unknown ? ([U] extends [T] ? false : true) : never) ? false : true; +type IsUnion = IsUnion_; +/** type is true if T is identically E */ +type TypeEquality = [T] extends [E] ? ([E] extends [T] ? true : false) : false; +/** type is true if T or null is identically E or null*/ +type NullTypeEquality = TypeEquality; +/** gets only the string literals of a type or null if a type isn't a string literal */ +type EnumString = [T] extends [never] ? null : T extends string ? string extends T ? null : T : null; +/** true if type is a union of string literals */ +type IsEnum = null extends EnumString ? false : true; +/** true only if all types are array types (not tuples) */ +type IsElements = false extends IsUnion ? [T] extends [readonly unknown[]] ? undefined extends T[0.5] ? false : true : false : false; +/** true if the the type is a values type */ +type IsValues = false extends IsUnion ? TypeEquality : false; +/** true if type is a properties type and Union is false, or type is a discriminator type and Union is true */ +type IsRecord = Union extends IsUnion ? null extends EnumString ? false : true : false; +/** true if type represents an empty record */ +type IsEmptyRecord = [T] extends [Record] ? [T] extends [never] ? false : true : false; +/** actual schema */ +export type JTDSchemaType = Record> = (// refs - where null wasn't specified, must match exactly +(null extends EnumString ? never : ({ + [K in keyof D]: [T] extends [D[K]] ? { + ref: K; + } : never; +}[keyof D] & { + nullable?: false; +}) | (null extends T ? { + [K in keyof D]: [Exclude] extends [Exclude] ? { + ref: K; + } : never; +}[keyof D] & { + nullable: true; +} : never)) | (unknown extends T ? { + nullable?: boolean; +} : never) | ((true extends NullTypeEquality ? { + type: NumberType; +} : true extends NullTypeEquality ? { + type: "boolean"; +} : true extends NullTypeEquality ? { + type: StringType; +} : true extends NullTypeEquality ? { + type: "timestamp"; +} : true extends IsEnum> ? { + enum: EnumString>[]; +} : true extends IsElements> ? T extends readonly (infer E)[] ? { + elements: JTDSchemaType; +} : never : true extends IsEmptyRecord> ? { + properties: Record; + optionalProperties?: Record; +} | { + optionalProperties: Record; +} : true extends IsValues> ? T extends Record ? { + values: JTDSchemaType; +} : never : true extends IsRecord, false> ? ([RequiredKeys>] extends [never] ? { + properties?: Record; +} : { + properties: { + [K in RequiredKeys]: JTDSchemaType; + }; +}) & ([OptionalKeys>] extends [never] ? { + optionalProperties?: Record; +} : { + optionalProperties: { + [K in OptionalKeys]: JTDSchemaType, D>; + }; +}) & { + additionalProperties?: boolean; +} : true extends IsRecord, true> ? { + [K in keyof Exclude]-?: Exclude[K] extends string ? { + discriminator: K; + mapping: { + [M in Exclude[K]]: JTDSchemaType ? T : never, K>, D>; + }; + } : never; +}[keyof Exclude] : never) & (null extends T ? { + nullable: true; +} : { + nullable?: false; +}))) & { + metadata?: Record; + definitions?: { + [K in keyof D]: JTDSchemaType; + }; +}; +type JTDDataDef> = // ref +(S extends { + ref: string; +} ? D extends { + [K in S["ref"]]: infer V; +} ? JTDDataDef : never : S extends { + type: NumberType; +} ? number : S extends { + type: "boolean"; +} ? boolean : S extends { + type: "string"; +} ? string : S extends { + type: "timestamp"; +} ? string | Date : S extends { + enum: readonly (infer E)[]; +} ? string extends E ? never : [E] extends [string] ? E : never : S extends { + elements: infer E; +} ? JTDDataDef[] : S extends { + properties: Record; + optionalProperties?: Record; + additionalProperties?: boolean; +} ? { + -readonly [K in keyof S["properties"]]-?: JTDDataDef; +} & { + -readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef; +} & ([S["additionalProperties"]] extends [true] ? Record : unknown) : S extends { + properties?: Record; + optionalProperties: Record; + additionalProperties?: boolean; +} ? { + -readonly [K in keyof S["properties"]]-?: JTDDataDef; +} & { + -readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef; +} & ([S["additionalProperties"]] extends [true] ? Record : unknown) : S extends { + values: infer V; +} ? Record> : S extends { + discriminator: infer M; + mapping: Record; +} ? [M] extends [string] ? { + [K in keyof S["mapping"]]: JTDDataDef & { + [KM in M]: K; + }; +}[keyof S["mapping"]] : never : unknown) | (S extends { + nullable: true; +} ? null : never); +export type JTDDataType = S extends { + definitions: Record; +} ? JTDDataDef : JTDDataDef>; +export {}; diff --git a/website/node_modules/ajv/dist/types/jtd-schema.js b/website/node_modules/ajv/dist/types/jtd-schema.js new file mode 100644 index 00000000..11338aa8 --- /dev/null +++ b/website/node_modules/ajv/dist/types/jtd-schema.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=jtd-schema.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/types/jtd-schema.js.map b/website/node_modules/ajv/dist/types/jtd-schema.js.map new file mode 100644 index 00000000..add89bd7 --- /dev/null +++ b/website/node_modules/ajv/dist/types/jtd-schema.js.map @@ -0,0 +1 @@ +{"version":3,"file":"jtd-schema.js","sourceRoot":"","sources":["../../lib/types/jtd-schema.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/additionalItems.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/additionalItems.d.ts new file mode 100644 index 00000000..60751564 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/additionalItems.d.ts @@ -0,0 +1,8 @@ +import type { CodeKeywordDefinition, ErrorObject, AnySchema } from "../../types"; +import type { KeywordCxt } from "../../compile/validate"; +export type AdditionalItemsError = ErrorObject<"additionalItems", { + limit: number; +}, AnySchema>; +declare const def: CodeKeywordDefinition; +export declare function validateAdditionalItems(cxt: KeywordCxt, items: AnySchema[]): void; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js b/website/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js new file mode 100644 index 00000000..608d51eb --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js @@ -0,0 +1,49 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.validateAdditionalItems = void 0; +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const error = { + message: ({ params: { len } }) => (0, codegen_1.str) `must NOT have more than ${len} items`, + params: ({ params: { len } }) => (0, codegen_1._) `{limit: ${len}}`, +}; +const def = { + keyword: "additionalItems", + type: "array", + schemaType: ["boolean", "object"], + before: "uniqueItems", + error, + code(cxt) { + const { parentSchema, it } = cxt; + const { items } = parentSchema; + if (!Array.isArray(items)) { + (0, util_1.checkStrictMode)(it, '"additionalItems" is ignored when "items" is not an array of schemas'); + return; + } + validateAdditionalItems(cxt, items); + }, +}; +function validateAdditionalItems(cxt, items) { + const { gen, schema, data, keyword, it } = cxt; + it.items = true; + const len = gen.const("len", (0, codegen_1._) `${data}.length`); + if (schema === false) { + cxt.setParams({ len: items.length }); + cxt.pass((0, codegen_1._) `${len} <= ${items.length}`); + } + else if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it, schema)) { + const valid = gen.var("valid", (0, codegen_1._) `${len} <= ${items.length}`); // TODO var + gen.if((0, codegen_1.not)(valid), () => validateItems(valid)); + cxt.ok(valid); + } + function validateItems(valid) { + gen.forRange("i", items.length, len, (i) => { + cxt.subschema({ keyword, dataProp: i, dataPropType: util_1.Type.Num }, valid); + if (!it.allErrors) + gen.if((0, codegen_1.not)(valid), () => gen.break()); + }); + } +} +exports.validateAdditionalItems = validateAdditionalItems; +exports.default = def; +//# sourceMappingURL=additionalItems.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js.map new file mode 100644 index 00000000..b3d1479c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js.map @@ -0,0 +1 @@ +{"version":3,"file":"additionalItems.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/additionalItems.ts"],"names":[],"mappings":";;;AAOA,mDAAuD;AACvD,6CAA2E;AAI3E,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,EAAC,GAAG,EAAC,EAAC,EAAE,EAAE,CAAC,IAAA,aAAG,EAAA,2BAA2B,GAAG,QAAQ;IACvE,MAAM,EAAE,CAAC,EAAC,MAAM,EAAE,EAAC,GAAG,EAAC,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,WAAW,GAAG,GAAG;CAChD,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,iBAA0B;IACnC,IAAI,EAAE,OAAO;IACb,UAAU,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC;IACjC,MAAM,EAAE,aAAa;IACrB,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,YAAY,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QAC9B,MAAM,EAAC,KAAK,EAAC,GAAG,YAAY,CAAA;QAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;YACzB,IAAA,sBAAe,EAAC,EAAE,EAAE,sEAAsE,CAAC,CAAA;YAC3F,OAAM;SACP;QACD,uBAAuB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IACrC,CAAC;CACF,CAAA;AAED,SAAgB,uBAAuB,CAAC,GAAe,EAAE,KAAkB;IACzE,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;IAC5C,EAAE,CAAC,KAAK,GAAG,IAAI,CAAA;IACf,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,IAAA,WAAC,EAAA,GAAG,IAAI,SAAS,CAAC,CAAA;IAC/C,IAAI,MAAM,KAAK,KAAK,EAAE;QACpB,GAAG,CAAC,SAAS,CAAC,EAAC,GAAG,EAAE,KAAK,CAAC,MAAM,EAAC,CAAC,CAAA;QAClC,GAAG,CAAC,IAAI,CAAC,IAAA,WAAC,EAAA,GAAG,GAAG,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;KACvC;SAAM,IAAI,OAAO,MAAM,IAAI,QAAQ,IAAI,CAAC,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC,EAAE;QACtE,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,IAAA,WAAC,EAAA,GAAG,GAAG,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA,CAAC,WAAW;QACxE,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAA;QAC9C,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;KACd;IAED,SAAS,aAAa,CAAC,KAAW;QAChC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE;YACzC,GAAG,CAAC,SAAS,CAAC,EAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,YAAY,EAAE,WAAI,CAAC,GAAG,EAAC,EAAE,KAAK,CAAC,CAAA;YACpE,IAAI,CAAC,EAAE,CAAC,SAAS;gBAAE,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAA;QAC1D,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC;AAnBD,0DAmBC;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.d.ts new file mode 100644 index 00000000..74698c7a --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.d.ts @@ -0,0 +1,6 @@ +import type { CodeKeywordDefinition, AddedKeywordDefinition, ErrorObject, AnySchema } from "../../types"; +export type AdditionalPropertiesError = ErrorObject<"additionalProperties", { + additionalProperty: string; +}, AnySchema>; +declare const def: CodeKeywordDefinition & AddedKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js b/website/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js new file mode 100644 index 00000000..1d3374bb --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js @@ -0,0 +1,106 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const code_1 = require("../code"); +const codegen_1 = require("../../compile/codegen"); +const names_1 = require("../../compile/names"); +const util_1 = require("../../compile/util"); +const error = { + message: "must NOT have additional properties", + params: ({ params }) => (0, codegen_1._) `{additionalProperty: ${params.additionalProperty}}`, +}; +const def = { + keyword: "additionalProperties", + type: ["object"], + schemaType: ["boolean", "object"], + allowUndefined: true, + trackErrors: true, + error, + code(cxt) { + const { gen, schema, parentSchema, data, errsCount, it } = cxt; + /* istanbul ignore if */ + if (!errsCount) + throw new Error("ajv implementation error"); + const { allErrors, opts } = it; + it.props = true; + if (opts.removeAdditional !== "all" && (0, util_1.alwaysValidSchema)(it, schema)) + return; + const props = (0, code_1.allSchemaProperties)(parentSchema.properties); + const patProps = (0, code_1.allSchemaProperties)(parentSchema.patternProperties); + checkAdditionalProperties(); + cxt.ok((0, codegen_1._) `${errsCount} === ${names_1.default.errors}`); + function checkAdditionalProperties() { + gen.forIn("key", data, (key) => { + if (!props.length && !patProps.length) + additionalPropertyCode(key); + else + gen.if(isAdditional(key), () => additionalPropertyCode(key)); + }); + } + function isAdditional(key) { + let definedProp; + if (props.length > 8) { + // TODO maybe an option instead of hard-coded 8? + const propsSchema = (0, util_1.schemaRefOrVal)(it, parentSchema.properties, "properties"); + definedProp = (0, code_1.isOwnProperty)(gen, propsSchema, key); + } + else if (props.length) { + definedProp = (0, codegen_1.or)(...props.map((p) => (0, codegen_1._) `${key} === ${p}`)); + } + else { + definedProp = codegen_1.nil; + } + if (patProps.length) { + definedProp = (0, codegen_1.or)(definedProp, ...patProps.map((p) => (0, codegen_1._) `${(0, code_1.usePattern)(cxt, p)}.test(${key})`)); + } + return (0, codegen_1.not)(definedProp); + } + function deleteAdditional(key) { + gen.code((0, codegen_1._) `delete ${data}[${key}]`); + } + function additionalPropertyCode(key) { + if (opts.removeAdditional === "all" || (opts.removeAdditional && schema === false)) { + deleteAdditional(key); + return; + } + if (schema === false) { + cxt.setParams({ additionalProperty: key }); + cxt.error(); + if (!allErrors) + gen.break(); + return; + } + if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it, schema)) { + const valid = gen.name("valid"); + if (opts.removeAdditional === "failing") { + applyAdditionalSchema(key, valid, false); + gen.if((0, codegen_1.not)(valid), () => { + cxt.reset(); + deleteAdditional(key); + }); + } + else { + applyAdditionalSchema(key, valid); + if (!allErrors) + gen.if((0, codegen_1.not)(valid), () => gen.break()); + } + } + } + function applyAdditionalSchema(key, valid, errors) { + const subschema = { + keyword: "additionalProperties", + dataProp: key, + dataPropType: util_1.Type.Str, + }; + if (errors === false) { + Object.assign(subschema, { + compositeRule: true, + createErrors: false, + allErrors: false, + }); + } + cxt.subschema(subschema, valid); + } + }, +}; +exports.default = def; +//# sourceMappingURL=additionalProperties.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js.map new file mode 100644 index 00000000..d2f249f6 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js.map @@ -0,0 +1 @@ +{"version":3,"file":"additionalProperties.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/additionalProperties.ts"],"names":[],"mappings":";;AAOA,kCAAsE;AACtE,mDAAiE;AACjE,+CAAmC;AAEnC,6CAA0E;AAQ1E,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,qCAAqC;IAC9C,MAAM,EAAE,CAAC,EAAC,MAAM,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,wBAAwB,MAAM,CAAC,kBAAkB,GAAG;CAC5E,CAAA;AAED,MAAM,GAAG,GAAmD;IAC1D,OAAO,EAAE,sBAAsB;IAC/B,IAAI,EAAE,CAAC,QAAQ,CAAC;IAChB,UAAU,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC;IACjC,cAAc,EAAE,IAAI;IACpB,WAAW,EAAE,IAAI;IACjB,KAAK;IACL,IAAI,CAAC,GAAG;QACN,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QAC5D,wBAAwB;QACxB,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;QAC3D,MAAM,EAAC,SAAS,EAAE,IAAI,EAAC,GAAG,EAAE,CAAA;QAC5B,EAAE,CAAC,KAAK,GAAG,IAAI,CAAA;QACf,IAAI,IAAI,CAAC,gBAAgB,KAAK,KAAK,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC;YAAE,OAAM;QAC5E,MAAM,KAAK,GAAG,IAAA,0BAAmB,EAAC,YAAY,CAAC,UAAU,CAAC,CAAA;QAC1D,MAAM,QAAQ,GAAG,IAAA,0BAAmB,EAAC,YAAY,CAAC,iBAAiB,CAAC,CAAA;QACpE,yBAAyB,EAAE,CAAA;QAC3B,GAAG,CAAC,EAAE,CAAC,IAAA,WAAC,EAAA,GAAG,SAAS,QAAQ,eAAC,CAAC,MAAM,EAAE,CAAC,CAAA;QAEvC,SAAS,yBAAyB;YAChC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,GAAS,EAAE,EAAE;gBACnC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM;oBAAE,sBAAsB,CAAC,GAAG,CAAC,CAAA;;oBAC7D,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAA;YACnE,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,SAAS,YAAY,CAAC,GAAS;YAC7B,IAAI,WAAiB,CAAA;YACrB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;gBACpB,gDAAgD;gBAChD,MAAM,WAAW,GAAG,IAAA,qBAAc,EAAC,EAAE,EAAE,YAAY,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;gBAC7E,WAAW,GAAG,IAAA,oBAAa,EAAC,GAAG,EAAE,WAAmB,EAAE,GAAG,CAAC,CAAA;aAC3D;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE;gBACvB,WAAW,GAAG,IAAA,YAAE,EAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;aAC1D;iBAAM;gBACL,WAAW,GAAG,aAAG,CAAA;aAClB;YACD,IAAI,QAAQ,CAAC,MAAM,EAAE;gBACnB,WAAW,GAAG,IAAA,YAAE,EAAC,WAAW,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,GAAG,IAAA,iBAAU,EAAC,GAAG,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAA;aAC7F;YACD,OAAO,IAAA,aAAG,EAAC,WAAW,CAAC,CAAA;QACzB,CAAC;QAED,SAAS,gBAAgB,CAAC,GAAS;YACjC,GAAG,CAAC,IAAI,CAAC,IAAA,WAAC,EAAA,UAAU,IAAI,IAAI,GAAG,GAAG,CAAC,CAAA;QACrC,CAAC;QAED,SAAS,sBAAsB,CAAC,GAAS;YACvC,IAAI,IAAI,CAAC,gBAAgB,KAAK,KAAK,IAAI,CAAC,IAAI,CAAC,gBAAgB,IAAI,MAAM,KAAK,KAAK,CAAC,EAAE;gBAClF,gBAAgB,CAAC,GAAG,CAAC,CAAA;gBACrB,OAAM;aACP;YAED,IAAI,MAAM,KAAK,KAAK,EAAE;gBACpB,GAAG,CAAC,SAAS,CAAC,EAAC,kBAAkB,EAAE,GAAG,EAAC,CAAC,CAAA;gBACxC,GAAG,CAAC,KAAK,EAAE,CAAA;gBACX,IAAI,CAAC,SAAS;oBAAE,GAAG,CAAC,KAAK,EAAE,CAAA;gBAC3B,OAAM;aACP;YAED,IAAI,OAAO,MAAM,IAAI,QAAQ,IAAI,CAAC,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC,EAAE;gBAC/D,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;gBAC/B,IAAI,IAAI,CAAC,gBAAgB,KAAK,SAAS,EAAE;oBACvC,qBAAqB,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;oBACxC,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,KAAK,CAAC,EAAE,GAAG,EAAE;wBACtB,GAAG,CAAC,KAAK,EAAE,CAAA;wBACX,gBAAgB,CAAC,GAAG,CAAC,CAAA;oBACvB,CAAC,CAAC,CAAA;iBACH;qBAAM;oBACL,qBAAqB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;oBACjC,IAAI,CAAC,SAAS;wBAAE,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAA;iBACtD;aACF;QACH,CAAC;QAED,SAAS,qBAAqB,CAAC,GAAS,EAAE,KAAW,EAAE,MAAc;YACnE,MAAM,SAAS,GAAkB;gBAC/B,OAAO,EAAE,sBAAsB;gBAC/B,QAAQ,EAAE,GAAG;gBACb,YAAY,EAAE,WAAI,CAAC,GAAG;aACvB,CAAA;YACD,IAAI,MAAM,KAAK,KAAK,EAAE;gBACpB,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE;oBACvB,aAAa,EAAE,IAAI;oBACnB,YAAY,EAAE,KAAK;oBACnB,SAAS,EAAE,KAAK;iBACjB,CAAC,CAAA;aACH;YACD,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/allOf.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/allOf.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/allOf.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/allOf.js b/website/node_modules/ajv/dist/vocabularies/applicator/allOf.js new file mode 100644 index 00000000..1b1ae737 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/allOf.js @@ -0,0 +1,23 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const util_1 = require("../../compile/util"); +const def = { + keyword: "allOf", + schemaType: "array", + code(cxt) { + const { gen, schema, it } = cxt; + /* istanbul ignore if */ + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + const valid = gen.name("valid"); + schema.forEach((sch, i) => { + if ((0, util_1.alwaysValidSchema)(it, sch)) + return; + const schCxt = cxt.subschema({ keyword: "allOf", schemaProp: i }, valid); + cxt.ok(valid); + cxt.mergeEvaluated(schCxt); + }); + }, +}; +exports.default = def; +//# sourceMappingURL=allOf.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/allOf.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/allOf.js.map new file mode 100644 index 00000000..d119d0ea --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/allOf.js.map @@ -0,0 +1 @@ +{"version":3,"file":"allOf.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/allOf.ts"],"names":[],"mappings":";;AAEA,6CAAoD;AAEpD,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,OAAO;IAChB,UAAU,EAAE,OAAO;IACnB,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QAC7B,wBAAwB;QACxB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;QACvE,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,GAAc,EAAE,CAAS,EAAE,EAAE;YAC3C,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,GAAG,CAAC;gBAAE,OAAM;YACtC,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,EAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,EAAC,EAAE,KAAK,CAAC,CAAA;YACtE,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;YACb,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;QAC5B,CAAC,CAAC,CAAA;IACJ,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/anyOf.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/anyOf.d.ts new file mode 100644 index 00000000..61bca56e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/anyOf.d.ts @@ -0,0 +1,4 @@ +import type { CodeKeywordDefinition, ErrorNoParams, AnySchema } from "../../types"; +export type AnyOfError = ErrorNoParams<"anyOf", AnySchema[]>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/anyOf.js b/website/node_modules/ajv/dist/vocabularies/applicator/anyOf.js new file mode 100644 index 00000000..66cfce2c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/anyOf.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const code_1 = require("../code"); +const def = { + keyword: "anyOf", + schemaType: "array", + trackErrors: true, + code: code_1.validateUnion, + error: { message: "must match a schema in anyOf" }, +}; +exports.default = def; +//# sourceMappingURL=anyOf.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/anyOf.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/anyOf.js.map new file mode 100644 index 00000000..537ffcbe --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/anyOf.js.map @@ -0,0 +1 @@ +{"version":3,"file":"anyOf.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/anyOf.ts"],"names":[],"mappings":";;AACA,kCAAqC;AAIrC,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,OAAO;IAChB,UAAU,EAAE,OAAO;IACnB,WAAW,EAAE,IAAI;IACjB,IAAI,EAAE,oBAAa;IACnB,KAAK,EAAE,EAAC,OAAO,EAAE,8BAA8B,EAAC;CACjD,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/contains.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/contains.d.ts new file mode 100644 index 00000000..5493e844 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/contains.d.ts @@ -0,0 +1,7 @@ +import type { CodeKeywordDefinition, ErrorObject, AnySchema } from "../../types"; +export type ContainsError = ErrorObject<"contains", { + minContains: number; + maxContains?: number; +}, AnySchema>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/contains.js b/website/node_modules/ajv/dist/vocabularies/applicator/contains.js new file mode 100644 index 00000000..6c5473f5 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/contains.js @@ -0,0 +1,95 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const error = { + message: ({ params: { min, max } }) => max === undefined + ? (0, codegen_1.str) `must contain at least ${min} valid item(s)` + : (0, codegen_1.str) `must contain at least ${min} and no more than ${max} valid item(s)`, + params: ({ params: { min, max } }) => max === undefined ? (0, codegen_1._) `{minContains: ${min}}` : (0, codegen_1._) `{minContains: ${min}, maxContains: ${max}}`, +}; +const def = { + keyword: "contains", + type: "array", + schemaType: ["object", "boolean"], + before: "uniqueItems", + trackErrors: true, + error, + code(cxt) { + const { gen, schema, parentSchema, data, it } = cxt; + let min; + let max; + const { minContains, maxContains } = parentSchema; + if (it.opts.next) { + min = minContains === undefined ? 1 : minContains; + max = maxContains; + } + else { + min = 1; + } + const len = gen.const("len", (0, codegen_1._) `${data}.length`); + cxt.setParams({ min, max }); + if (max === undefined && min === 0) { + (0, util_1.checkStrictMode)(it, `"minContains" == 0 without "maxContains": "contains" keyword ignored`); + return; + } + if (max !== undefined && min > max) { + (0, util_1.checkStrictMode)(it, `"minContains" > "maxContains" is always invalid`); + cxt.fail(); + return; + } + if ((0, util_1.alwaysValidSchema)(it, schema)) { + let cond = (0, codegen_1._) `${len} >= ${min}`; + if (max !== undefined) + cond = (0, codegen_1._) `${cond} && ${len} <= ${max}`; + cxt.pass(cond); + return; + } + it.items = true; + const valid = gen.name("valid"); + if (max === undefined && min === 1) { + validateItems(valid, () => gen.if(valid, () => gen.break())); + } + else if (min === 0) { + gen.let(valid, true); + if (max !== undefined) + gen.if((0, codegen_1._) `${data}.length > 0`, validateItemsWithCount); + } + else { + gen.let(valid, false); + validateItemsWithCount(); + } + cxt.result(valid, () => cxt.reset()); + function validateItemsWithCount() { + const schValid = gen.name("_valid"); + const count = gen.let("count", 0); + validateItems(schValid, () => gen.if(schValid, () => checkLimits(count))); + } + function validateItems(_valid, block) { + gen.forRange("i", 0, len, (i) => { + cxt.subschema({ + keyword: "contains", + dataProp: i, + dataPropType: util_1.Type.Num, + compositeRule: true, + }, _valid); + block(); + }); + } + function checkLimits(count) { + gen.code((0, codegen_1._) `${count}++`); + if (max === undefined) { + gen.if((0, codegen_1._) `${count} >= ${min}`, () => gen.assign(valid, true).break()); + } + else { + gen.if((0, codegen_1._) `${count} > ${max}`, () => gen.assign(valid, false).break()); + if (min === 1) + gen.assign(valid, true); + else + gen.if((0, codegen_1._) `${count} >= ${min}`, () => gen.assign(valid, true)); + } + } + }, +}; +exports.default = def; +//# sourceMappingURL=contains.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/contains.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/contains.js.map new file mode 100644 index 00000000..6a0bd39c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/contains.js.map @@ -0,0 +1 @@ +{"version":3,"file":"contains.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/contains.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/dependencies.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/dependencies.d.ts new file mode 100644 index 00000000..05900e0c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/dependencies.d.ts @@ -0,0 +1,21 @@ +import type { CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition, SchemaMap, AnySchema } from "../../types"; +import type { KeywordCxt } from "../../compile/validate"; +export type PropertyDependencies = { + [K in string]?: string[]; +}; +export interface DependenciesErrorParams { + property: string; + missingProperty: string; + depsCount: number; + deps: string; +} +export type DependenciesError = ErrorObject<"dependencies", DependenciesErrorParams, { + [K in string]?: string[] | AnySchema; +}>; +export declare const error: KeywordErrorDefinition; +declare const def: CodeKeywordDefinition; +export declare function validatePropertyDeps(cxt: KeywordCxt, propertyDeps?: { + [K in string]?: string[]; +}): void; +export declare function validateSchemaDeps(cxt: KeywordCxt, schemaDeps?: SchemaMap): void; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/dependencies.js b/website/node_modules/ajv/dist/vocabularies/applicator/dependencies.js new file mode 100644 index 00000000..e81f86d1 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/dependencies.js @@ -0,0 +1,85 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.validateSchemaDeps = exports.validatePropertyDeps = exports.error = void 0; +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const code_1 = require("../code"); +exports.error = { + message: ({ params: { property, depsCount, deps } }) => { + const property_ies = depsCount === 1 ? "property" : "properties"; + return (0, codegen_1.str) `must have ${property_ies} ${deps} when property ${property} is present`; + }, + params: ({ params: { property, depsCount, deps, missingProperty } }) => (0, codegen_1._) `{property: ${property}, + missingProperty: ${missingProperty}, + depsCount: ${depsCount}, + deps: ${deps}}`, // TODO change to reference +}; +const def = { + keyword: "dependencies", + type: "object", + schemaType: "object", + error: exports.error, + code(cxt) { + const [propDeps, schDeps] = splitDependencies(cxt); + validatePropertyDeps(cxt, propDeps); + validateSchemaDeps(cxt, schDeps); + }, +}; +function splitDependencies({ schema }) { + const propertyDeps = {}; + const schemaDeps = {}; + for (const key in schema) { + if (key === "__proto__") + continue; + const deps = Array.isArray(schema[key]) ? propertyDeps : schemaDeps; + deps[key] = schema[key]; + } + return [propertyDeps, schemaDeps]; +} +function validatePropertyDeps(cxt, propertyDeps = cxt.schema) { + const { gen, data, it } = cxt; + if (Object.keys(propertyDeps).length === 0) + return; + const missing = gen.let("missing"); + for (const prop in propertyDeps) { + const deps = propertyDeps[prop]; + if (deps.length === 0) + continue; + const hasProperty = (0, code_1.propertyInData)(gen, data, prop, it.opts.ownProperties); + cxt.setParams({ + property: prop, + depsCount: deps.length, + deps: deps.join(", "), + }); + if (it.allErrors) { + gen.if(hasProperty, () => { + for (const depProp of deps) { + (0, code_1.checkReportMissingProp)(cxt, depProp); + } + }); + } + else { + gen.if((0, codegen_1._) `${hasProperty} && (${(0, code_1.checkMissingProp)(cxt, deps, missing)})`); + (0, code_1.reportMissingProp)(cxt, missing); + gen.else(); + } + } +} +exports.validatePropertyDeps = validatePropertyDeps; +function validateSchemaDeps(cxt, schemaDeps = cxt.schema) { + const { gen, data, keyword, it } = cxt; + const valid = gen.name("valid"); + for (const prop in schemaDeps) { + if ((0, util_1.alwaysValidSchema)(it, schemaDeps[prop])) + continue; + gen.if((0, code_1.propertyInData)(gen, data, prop, it.opts.ownProperties), () => { + const schCxt = cxt.subschema({ keyword, schemaProp: prop }, valid); + cxt.mergeValidEvaluated(schCxt, valid); + }, () => gen.var(valid, true) // TODO var + ); + cxt.ok(valid); + } +} +exports.validateSchemaDeps = validateSchemaDeps; +exports.default = def; +//# sourceMappingURL=dependencies.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/dependencies.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/dependencies.js.map new file mode 100644 index 00000000..dafffd16 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/dependencies.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dependencies.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/dependencies.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.js b/website/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.js new file mode 100644 index 00000000..66ef2e84 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const dependencies_1 = require("./dependencies"); +const def = { + keyword: "dependentSchemas", + type: "object", + schemaType: "object", + code: (cxt) => (0, dependencies_1.validateSchemaDeps)(cxt), +}; +exports.default = def; +//# sourceMappingURL=dependentSchemas.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.js.map new file mode 100644 index 00000000..17712e6f --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dependentSchemas.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/dependentSchemas.ts"],"names":[],"mappings":";;AACA,iDAAiD;AAEjD,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,kBAAkB;IAC3B,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,QAAQ;IACpB,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAA,iCAAkB,EAAC,GAAG,CAAC;CACvC,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/if.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/if.d.ts new file mode 100644 index 00000000..8f602e33 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/if.d.ts @@ -0,0 +1,6 @@ +import type { CodeKeywordDefinition, ErrorObject, AnySchema } from "../../types"; +export type IfKeywordError = ErrorObject<"if", { + failingKeyword: string; +}, AnySchema>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/if.js b/website/node_modules/ajv/dist/vocabularies/applicator/if.js new file mode 100644 index 00000000..2c42a3e9 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/if.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const error = { + message: ({ params }) => (0, codegen_1.str) `must match "${params.ifClause}" schema`, + params: ({ params }) => (0, codegen_1._) `{failingKeyword: ${params.ifClause}}`, +}; +const def = { + keyword: "if", + schemaType: ["object", "boolean"], + trackErrors: true, + error, + code(cxt) { + const { gen, parentSchema, it } = cxt; + if (parentSchema.then === undefined && parentSchema.else === undefined) { + (0, util_1.checkStrictMode)(it, '"if" without "then" and "else" is ignored'); + } + const hasThen = hasSchema(it, "then"); + const hasElse = hasSchema(it, "else"); + if (!hasThen && !hasElse) + return; + const valid = gen.let("valid", true); + const schValid = gen.name("_valid"); + validateIf(); + cxt.reset(); + if (hasThen && hasElse) { + const ifClause = gen.let("ifClause"); + cxt.setParams({ ifClause }); + gen.if(schValid, validateClause("then", ifClause), validateClause("else", ifClause)); + } + else if (hasThen) { + gen.if(schValid, validateClause("then")); + } + else { + gen.if((0, codegen_1.not)(schValid), validateClause("else")); + } + cxt.pass(valid, () => cxt.error(true)); + function validateIf() { + const schCxt = cxt.subschema({ + keyword: "if", + compositeRule: true, + createErrors: false, + allErrors: false, + }, schValid); + cxt.mergeEvaluated(schCxt); + } + function validateClause(keyword, ifClause) { + return () => { + const schCxt = cxt.subschema({ keyword }, schValid); + gen.assign(valid, schValid); + cxt.mergeValidEvaluated(schCxt, valid); + if (ifClause) + gen.assign(ifClause, (0, codegen_1._) `${keyword}`); + else + cxt.setParams({ ifClause: keyword }); + }; + } + }, +}; +function hasSchema(it, keyword) { + const schema = it.schema[keyword]; + return schema !== undefined && !(0, util_1.alwaysValidSchema)(it, schema); +} +exports.default = def; +//# sourceMappingURL=if.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/if.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/if.js.map new file mode 100644 index 00000000..34b3a553 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/if.js.map @@ -0,0 +1 @@ +{"version":3,"file":"if.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/if.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/index.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/index.d.ts new file mode 100644 index 00000000..b9cc5f5c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/index.d.ts @@ -0,0 +1,13 @@ +import type { ErrorNoParams, Vocabulary } from "../../types"; +import { AdditionalItemsError } from "./additionalItems"; +import { ItemsError } from "./items2020"; +import { ContainsError } from "./contains"; +import { DependenciesError } from "./dependencies"; +import { PropertyNamesError } from "./propertyNames"; +import { AdditionalPropertiesError } from "./additionalProperties"; +import { NotKeywordError } from "./not"; +import { AnyOfError } from "./anyOf"; +import { OneOfError } from "./oneOf"; +import { IfKeywordError } from "./if"; +export default function getApplicator(draft2020?: boolean): Vocabulary; +export type ApplicatorKeywordError = ErrorNoParams<"false schema"> | AdditionalItemsError | ItemsError | ContainsError | AdditionalPropertiesError | DependenciesError | IfKeywordError | AnyOfError | OneOfError | NotKeywordError | PropertyNamesError; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/index.js b/website/node_modules/ajv/dist/vocabularies/applicator/index.js new file mode 100644 index 00000000..cf592f27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/index.js @@ -0,0 +1,44 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const additionalItems_1 = require("./additionalItems"); +const prefixItems_1 = require("./prefixItems"); +const items_1 = require("./items"); +const items2020_1 = require("./items2020"); +const contains_1 = require("./contains"); +const dependencies_1 = require("./dependencies"); +const propertyNames_1 = require("./propertyNames"); +const additionalProperties_1 = require("./additionalProperties"); +const properties_1 = require("./properties"); +const patternProperties_1 = require("./patternProperties"); +const not_1 = require("./not"); +const anyOf_1 = require("./anyOf"); +const oneOf_1 = require("./oneOf"); +const allOf_1 = require("./allOf"); +const if_1 = require("./if"); +const thenElse_1 = require("./thenElse"); +function getApplicator(draft2020 = false) { + const applicator = [ + // any + not_1.default, + anyOf_1.default, + oneOf_1.default, + allOf_1.default, + if_1.default, + thenElse_1.default, + // object + propertyNames_1.default, + additionalProperties_1.default, + dependencies_1.default, + properties_1.default, + patternProperties_1.default, + ]; + // array + if (draft2020) + applicator.push(prefixItems_1.default, items2020_1.default); + else + applicator.push(additionalItems_1.default, items_1.default); + applicator.push(contains_1.default); + return applicator; +} +exports.default = getApplicator; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/index.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/index.js.map new file mode 100644 index 00000000..ebcb8534 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/index.ts"],"names":[],"mappings":";;AACA,uDAAuE;AACvE,+CAAuC;AACvC,mCAA2B;AAC3B,2CAAiD;AACjD,yCAAkD;AAClD,iDAA8D;AAC9D,mDAAiE;AACjE,iEAAsF;AACtF,6CAAqC;AACrC,2DAAmD;AACnD,+BAAiD;AACjD,mCAAyC;AACzC,mCAAyC;AACzC,mCAA2B;AAC3B,6BAA8C;AAC9C,yCAAiC;AAEjC,SAAwB,aAAa,CAAC,SAAS,GAAG,KAAK;IACrD,MAAM,UAAU,GAAG;QACjB,MAAM;QACN,aAAU;QACV,eAAK;QACL,eAAK;QACL,eAAK;QACL,YAAS;QACT,kBAAQ;QACR,SAAS;QACT,uBAAa;QACb,8BAAoB;QACpB,sBAAY;QACZ,oBAAU;QACV,2BAAiB;KAClB,CAAA;IACD,QAAQ;IACR,IAAI,SAAS;QAAE,UAAU,CAAC,IAAI,CAAC,qBAAW,EAAE,mBAAS,CAAC,CAAA;;QACjD,UAAU,CAAC,IAAI,CAAC,yBAAe,EAAE,eAAK,CAAC,CAAA;IAC5C,UAAU,CAAC,IAAI,CAAC,kBAAQ,CAAC,CAAA;IACzB,OAAO,UAAU,CAAA;AACnB,CAAC;AArBD,gCAqBC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/items.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/items.d.ts new file mode 100644 index 00000000..8e608ca9 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/items.d.ts @@ -0,0 +1,5 @@ +import type { CodeKeywordDefinition, AnySchema } from "../../types"; +import type { KeywordCxt } from "../../compile/validate"; +declare const def: CodeKeywordDefinition; +export declare function validateTuple(cxt: KeywordCxt, extraItems: string, schArr?: AnySchema[]): void; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/items.js b/website/node_modules/ajv/dist/vocabularies/applicator/items.js new file mode 100644 index 00000000..26f527bc --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/items.js @@ -0,0 +1,52 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.validateTuple = void 0; +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const code_1 = require("../code"); +const def = { + keyword: "items", + type: "array", + schemaType: ["object", "array", "boolean"], + before: "uniqueItems", + code(cxt) { + const { schema, it } = cxt; + if (Array.isArray(schema)) + return validateTuple(cxt, "additionalItems", schema); + it.items = true; + if ((0, util_1.alwaysValidSchema)(it, schema)) + return; + cxt.ok((0, code_1.validateArray)(cxt)); + }, +}; +function validateTuple(cxt, extraItems, schArr = cxt.schema) { + const { gen, parentSchema, data, keyword, it } = cxt; + checkStrictTuple(parentSchema); + if (it.opts.unevaluated && schArr.length && it.items !== true) { + it.items = util_1.mergeEvaluated.items(gen, schArr.length, it.items); + } + const valid = gen.name("valid"); + const len = gen.const("len", (0, codegen_1._) `${data}.length`); + schArr.forEach((sch, i) => { + if ((0, util_1.alwaysValidSchema)(it, sch)) + return; + gen.if((0, codegen_1._) `${len} > ${i}`, () => cxt.subschema({ + keyword, + schemaProp: i, + dataProp: i, + }, valid)); + cxt.ok(valid); + }); + function checkStrictTuple(sch) { + const { opts, errSchemaPath } = it; + const l = schArr.length; + const fullTuple = l === sch.minItems && (l === sch.maxItems || sch[extraItems] === false); + if (opts.strictTuples && !fullTuple) { + const msg = `"${keyword}" is ${l}-tuple, but minItems or maxItems/${extraItems} are not specified or different at path "${errSchemaPath}"`; + (0, util_1.checkStrictMode)(it, msg, opts.strictTuples); + } + } +} +exports.validateTuple = validateTuple; +exports.default = def; +//# sourceMappingURL=items.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/items.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/items.js.map new file mode 100644 index 00000000..3c4ce614 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/items.js.map @@ -0,0 +1 @@ +{"version":3,"file":"items.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/items.ts"],"names":[],"mappings":";;;AAEA,mDAAuC;AACvC,6CAAqF;AACrF,kCAAqC;AAErC,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,OAAO;IAChB,IAAI,EAAE,OAAO;IACb,UAAU,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC;IAC1C,MAAM,EAAE,aAAa;IACrB,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,MAAM,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACxB,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,aAAa,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,CAAC,CAAA;QAC/E,EAAE,CAAC,KAAK,GAAG,IAAI,CAAA;QACf,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC;YAAE,OAAM;QACzC,GAAG,CAAC,EAAE,CAAC,IAAA,oBAAa,EAAC,GAAG,CAAC,CAAC,CAAA;IAC5B,CAAC;CACF,CAAA;AAED,SAAgB,aAAa,CAC3B,GAAe,EACf,UAAkB,EAClB,SAAsB,GAAG,CAAC,MAAM;IAEhC,MAAM,EAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;IAClD,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAC9B,IAAI,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,KAAK,KAAK,IAAI,EAAE;QAC7D,EAAE,CAAC,KAAK,GAAG,qBAAc,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,CAAA;KAC9D;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,IAAA,WAAC,EAAA,GAAG,IAAI,SAAS,CAAC,CAAA;IAC/C,MAAM,CAAC,OAAO,CAAC,CAAC,GAAc,EAAE,CAAS,EAAE,EAAE;QAC3C,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,GAAG,CAAC;YAAE,OAAM;QACtC,GAAG,CAAC,EAAE,CAAC,IAAA,WAAC,EAAA,GAAG,GAAG,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,CAC5B,GAAG,CAAC,SAAS,CACX;YACE,OAAO;YACP,UAAU,EAAE,CAAC;YACb,QAAQ,EAAE,CAAC;SACZ,EACD,KAAK,CACN,CACF,CAAA;QACD,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;IACf,CAAC,CAAC,CAAA;IAEF,SAAS,gBAAgB,CAAC,GAAoB;QAC5C,MAAM,EAAC,IAAI,EAAE,aAAa,EAAC,GAAG,EAAE,CAAA;QAChC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAA;QACvB,MAAM,SAAS,GAAG,CAAC,KAAK,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,KAAK,CAAC,CAAA;QACzF,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,EAAE;YACnC,MAAM,GAAG,GAAG,IAAI,OAAO,QAAQ,CAAC,oCAAoC,UAAU,4CAA4C,aAAa,GAAG,CAAA;YAC1I,IAAA,sBAAe,EAAC,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;SAC5C;IACH,CAAC;AACH,CAAC;AApCD,sCAoCC;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/items2020.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/items2020.d.ts new file mode 100644 index 00000000..a2565b2c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/items2020.d.ts @@ -0,0 +1,6 @@ +import type { CodeKeywordDefinition, ErrorObject, AnySchema } from "../../types"; +export type ItemsError = ErrorObject<"items", { + limit: number; +}, AnySchema>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/items2020.js b/website/node_modules/ajv/dist/vocabularies/applicator/items2020.js new file mode 100644 index 00000000..f2387d7d --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/items2020.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const code_1 = require("../code"); +const additionalItems_1 = require("./additionalItems"); +const error = { + message: ({ params: { len } }) => (0, codegen_1.str) `must NOT have more than ${len} items`, + params: ({ params: { len } }) => (0, codegen_1._) `{limit: ${len}}`, +}; +const def = { + keyword: "items", + type: "array", + schemaType: ["object", "boolean"], + before: "uniqueItems", + error, + code(cxt) { + const { schema, parentSchema, it } = cxt; + const { prefixItems } = parentSchema; + it.items = true; + if ((0, util_1.alwaysValidSchema)(it, schema)) + return; + if (prefixItems) + (0, additionalItems_1.validateAdditionalItems)(cxt, prefixItems); + else + cxt.ok((0, code_1.validateArray)(cxt)); + }, +}; +exports.default = def; +//# sourceMappingURL=items2020.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/items2020.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/items2020.js.map new file mode 100644 index 00000000..5034acda --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/items2020.js.map @@ -0,0 +1 @@ +{"version":3,"file":"items2020.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/items2020.ts"],"names":[],"mappings":";;AAOA,mDAA4C;AAC5C,6CAAoD;AACpD,kCAAqC;AACrC,uDAAyD;AAIzD,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,EAAC,GAAG,EAAC,EAAC,EAAE,EAAE,CAAC,IAAA,aAAG,EAAA,2BAA2B,GAAG,QAAQ;IACvE,MAAM,EAAE,CAAC,EAAC,MAAM,EAAE,EAAC,GAAG,EAAC,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,WAAW,GAAG,GAAG;CAChD,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,OAAO;IAChB,IAAI,EAAE,OAAO;IACb,UAAU,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC;IACjC,MAAM,EAAE,aAAa;IACrB,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,MAAM,EAAE,YAAY,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACtC,MAAM,EAAC,WAAW,EAAC,GAAG,YAAY,CAAA;QAClC,EAAE,CAAC,KAAK,GAAG,IAAI,CAAA;QACf,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC;YAAE,OAAM;QACzC,IAAI,WAAW;YAAE,IAAA,yCAAuB,EAAC,GAAG,EAAE,WAAW,CAAC,CAAA;;YACrD,GAAG,CAAC,EAAE,CAAC,IAAA,oBAAa,EAAC,GAAG,CAAC,CAAC,CAAA;IACjC,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/not.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/not.d.ts new file mode 100644 index 00000000..d2f4888f --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/not.d.ts @@ -0,0 +1,4 @@ +import type { CodeKeywordDefinition, ErrorNoParams, AnySchema } from "../../types"; +export type NotKeywordError = ErrorNoParams<"not", AnySchema>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/not.js b/website/node_modules/ajv/dist/vocabularies/applicator/not.js new file mode 100644 index 00000000..89f6fdda --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/not.js @@ -0,0 +1,26 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const util_1 = require("../../compile/util"); +const def = { + keyword: "not", + schemaType: ["object", "boolean"], + trackErrors: true, + code(cxt) { + const { gen, schema, it } = cxt; + if ((0, util_1.alwaysValidSchema)(it, schema)) { + cxt.fail(); + return; + } + const valid = gen.name("valid"); + cxt.subschema({ + keyword: "not", + compositeRule: true, + createErrors: false, + allErrors: false, + }, valid); + cxt.failResult(valid, () => cxt.reset(), () => cxt.error()); + }, + error: { message: "must NOT be valid" }, +}; +exports.default = def; +//# sourceMappingURL=not.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/not.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/not.js.map new file mode 100644 index 00000000..5e2a1ca1 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/not.js.map @@ -0,0 +1 @@ +{"version":3,"file":"not.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/not.ts"],"names":[],"mappings":";;AAEA,6CAAoD;AAIpD,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,KAAK;IACd,UAAU,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC;IACjC,WAAW,EAAE,IAAI;IACjB,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QAC7B,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC,EAAE;YACjC,GAAG,CAAC,IAAI,EAAE,CAAA;YACV,OAAM;SACP;QAED,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC/B,GAAG,CAAC,SAAS,CACX;YACE,OAAO,EAAE,KAAK;YACd,aAAa,EAAE,IAAI;YACnB,YAAY,EAAE,KAAK;YACnB,SAAS,EAAE,KAAK;SACjB,EACD,KAAK,CACN,CAAA;QAED,GAAG,CAAC,UAAU,CACZ,KAAK,EACL,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EACjB,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAClB,CAAA;IACH,CAAC;IACD,KAAK,EAAE,EAAC,OAAO,EAAE,mBAAmB,EAAC;CACtC,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/oneOf.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/oneOf.d.ts new file mode 100644 index 00000000..1e1d34f7 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/oneOf.d.ts @@ -0,0 +1,6 @@ +import type { CodeKeywordDefinition, ErrorObject, AnySchema } from "../../types"; +export type OneOfError = ErrorObject<"oneOf", { + passingSchemas: [number, number] | null; +}, AnySchema[]>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/oneOf.js b/website/node_modules/ajv/dist/vocabularies/applicator/oneOf.js new file mode 100644 index 00000000..441db2ac --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/oneOf.js @@ -0,0 +1,60 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const error = { + message: "must match exactly one schema in oneOf", + params: ({ params }) => (0, codegen_1._) `{passingSchemas: ${params.passing}}`, +}; +const def = { + keyword: "oneOf", + schemaType: "array", + trackErrors: true, + error, + code(cxt) { + const { gen, schema, parentSchema, it } = cxt; + /* istanbul ignore if */ + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + if (it.opts.discriminator && parentSchema.discriminator) + return; + const schArr = schema; + const valid = gen.let("valid", false); + const passing = gen.let("passing", null); + const schValid = gen.name("_valid"); + cxt.setParams({ passing }); + // TODO possibly fail straight away (with warning or exception) if there are two empty always valid schemas + gen.block(validateOneOf); + cxt.result(valid, () => cxt.reset(), () => cxt.error(true)); + function validateOneOf() { + schArr.forEach((sch, i) => { + let schCxt; + if ((0, util_1.alwaysValidSchema)(it, sch)) { + gen.var(schValid, true); + } + else { + schCxt = cxt.subschema({ + keyword: "oneOf", + schemaProp: i, + compositeRule: true, + }, schValid); + } + if (i > 0) { + gen + .if((0, codegen_1._) `${schValid} && ${valid}`) + .assign(valid, false) + .assign(passing, (0, codegen_1._) `[${passing}, ${i}]`) + .else(); + } + gen.if(schValid, () => { + gen.assign(valid, true); + gen.assign(passing, i); + if (schCxt) + cxt.mergeEvaluated(schCxt, codegen_1.Name); + }); + }); + } + }, +}; +exports.default = def; +//# sourceMappingURL=oneOf.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/oneOf.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/oneOf.js.map new file mode 100644 index 00000000..6cc54de6 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/oneOf.js.map @@ -0,0 +1 @@ +{"version":3,"file":"oneOf.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/oneOf.ts"],"names":[],"mappings":";;AAOA,mDAA6C;AAC7C,6CAAoD;AASpD,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,wCAAwC;IACjD,MAAM,EAAE,CAAC,EAAC,MAAM,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,oBAAoB,MAAM,CAAC,OAAO,GAAG;CAC7D,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,OAAO;IAChB,UAAU,EAAE,OAAO;IACnB,WAAW,EAAE,IAAI;IACjB,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QAC3C,wBAAwB;QACxB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;QACvE,IAAI,EAAE,CAAC,IAAI,CAAC,aAAa,IAAI,YAAY,CAAC,aAAa;YAAE,OAAM;QAC/D,MAAM,MAAM,GAAgB,MAAM,CAAA;QAClC,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACrC,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;QACxC,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACnC,GAAG,CAAC,SAAS,CAAC,EAAC,OAAO,EAAC,CAAC,CAAA;QACxB,2GAA2G;QAE3G,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;QAExB,GAAG,CAAC,MAAM,CACR,KAAK,EACL,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EACjB,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CACtB,CAAA;QAED,SAAS,aAAa;YACpB,MAAM,CAAC,OAAO,CAAC,CAAC,GAAc,EAAE,CAAS,EAAE,EAAE;gBAC3C,IAAI,MAA6B,CAAA;gBACjC,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,GAAG,CAAC,EAAE;oBAC9B,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;iBACxB;qBAAM;oBACL,MAAM,GAAG,GAAG,CAAC,SAAS,CACpB;wBACE,OAAO,EAAE,OAAO;wBAChB,UAAU,EAAE,CAAC;wBACb,aAAa,EAAE,IAAI;qBACpB,EACD,QAAQ,CACT,CAAA;iBACF;gBAED,IAAI,CAAC,GAAG,CAAC,EAAE;oBACT,GAAG;yBACA,EAAE,CAAC,IAAA,WAAC,EAAA,GAAG,QAAQ,OAAO,KAAK,EAAE,CAAC;yBAC9B,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC;yBACpB,MAAM,CAAC,OAAO,EAAE,IAAA,WAAC,EAAA,IAAI,OAAO,KAAK,CAAC,GAAG,CAAC;yBACtC,IAAI,EAAE,CAAA;iBACV;gBAED,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;oBACpB,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;oBACvB,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;oBACtB,IAAI,MAAM;wBAAE,GAAG,CAAC,cAAc,CAAC,MAAM,EAAE,cAAI,CAAC,CAAA;gBAC9C,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/patternProperties.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/patternProperties.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/patternProperties.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js b/website/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js new file mode 100644 index 00000000..48501c6e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js @@ -0,0 +1,75 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const code_1 = require("../code"); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const util_2 = require("../../compile/util"); +const def = { + keyword: "patternProperties", + type: "object", + schemaType: "object", + code(cxt) { + const { gen, schema, data, parentSchema, it } = cxt; + const { opts } = it; + const patterns = (0, code_1.allSchemaProperties)(schema); + const alwaysValidPatterns = patterns.filter((p) => (0, util_1.alwaysValidSchema)(it, schema[p])); + if (patterns.length === 0 || + (alwaysValidPatterns.length === patterns.length && + (!it.opts.unevaluated || it.props === true))) { + return; + } + const checkProperties = opts.strictSchema && !opts.allowMatchingProperties && parentSchema.properties; + const valid = gen.name("valid"); + if (it.props !== true && !(it.props instanceof codegen_1.Name)) { + it.props = (0, util_2.evaluatedPropsToName)(gen, it.props); + } + const { props } = it; + validatePatternProperties(); + function validatePatternProperties() { + for (const pat of patterns) { + if (checkProperties) + checkMatchingProperties(pat); + if (it.allErrors) { + validateProperties(pat); + } + else { + gen.var(valid, true); // TODO var + validateProperties(pat); + gen.if(valid); + } + } + } + function checkMatchingProperties(pat) { + for (const prop in checkProperties) { + if (new RegExp(pat).test(prop)) { + (0, util_1.checkStrictMode)(it, `property ${prop} matches pattern ${pat} (use allowMatchingProperties)`); + } + } + } + function validateProperties(pat) { + gen.forIn("key", data, (key) => { + gen.if((0, codegen_1._) `${(0, code_1.usePattern)(cxt, pat)}.test(${key})`, () => { + const alwaysValid = alwaysValidPatterns.includes(pat); + if (!alwaysValid) { + cxt.subschema({ + keyword: "patternProperties", + schemaProp: pat, + dataProp: key, + dataPropType: util_2.Type.Str, + }, valid); + } + if (it.opts.unevaluated && props !== true) { + gen.assign((0, codegen_1._) `${props}[${key}]`, true); + } + else if (!alwaysValid && !it.allErrors) { + // can short-circuit if `unevaluatedProperties` is not supported (opts.next === false) + // or if all properties were evaluated (props === true) + gen.if((0, codegen_1.not)(valid), () => gen.break()); + } + }); + }); + } + }, +}; +exports.default = def; +//# sourceMappingURL=patternProperties.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js.map new file mode 100644 index 00000000..2a60b74a --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js.map @@ -0,0 +1 @@ +{"version":3,"file":"patternProperties.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/patternProperties.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/prefixItems.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/prefixItems.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/prefixItems.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js b/website/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js new file mode 100644 index 00000000..727bc23c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const items_1 = require("./items"); +const def = { + keyword: "prefixItems", + type: "array", + schemaType: ["array"], + before: "uniqueItems", + code: (cxt) => (0, items_1.validateTuple)(cxt, "items"), +}; +exports.default = def; +//# sourceMappingURL=prefixItems.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js.map new file mode 100644 index 00000000..deef718e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prefixItems.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/prefixItems.ts"],"names":[],"mappings":";;AACA,mCAAqC;AAErC,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,aAAa;IACtB,IAAI,EAAE,OAAO;IACb,UAAU,EAAE,CAAC,OAAO,CAAC;IACrB,MAAM,EAAE,aAAa;IACrB,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAA,qBAAa,EAAC,GAAG,EAAE,OAAO,CAAC;CAC3C,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/properties.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/properties.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/properties.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/properties.js b/website/node_modules/ajv/dist/vocabularies/applicator/properties.js new file mode 100644 index 00000000..7347358e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/properties.js @@ -0,0 +1,54 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const validate_1 = require("../../compile/validate"); +const code_1 = require("../code"); +const util_1 = require("../../compile/util"); +const additionalProperties_1 = require("./additionalProperties"); +const def = { + keyword: "properties", + type: "object", + schemaType: "object", + code(cxt) { + const { gen, schema, parentSchema, data, it } = cxt; + if (it.opts.removeAdditional === "all" && parentSchema.additionalProperties === undefined) { + additionalProperties_1.default.code(new validate_1.KeywordCxt(it, additionalProperties_1.default, "additionalProperties")); + } + const allProps = (0, code_1.allSchemaProperties)(schema); + for (const prop of allProps) { + it.definedProperties.add(prop); + } + if (it.opts.unevaluated && allProps.length && it.props !== true) { + it.props = util_1.mergeEvaluated.props(gen, (0, util_1.toHash)(allProps), it.props); + } + const properties = allProps.filter((p) => !(0, util_1.alwaysValidSchema)(it, schema[p])); + if (properties.length === 0) + return; + const valid = gen.name("valid"); + for (const prop of properties) { + if (hasDefault(prop)) { + applyPropertySchema(prop); + } + else { + gen.if((0, code_1.propertyInData)(gen, data, prop, it.opts.ownProperties)); + applyPropertySchema(prop); + if (!it.allErrors) + gen.else().var(valid, true); + gen.endIf(); + } + cxt.it.definedProperties.add(prop); + cxt.ok(valid); + } + function hasDefault(prop) { + return it.opts.useDefaults && !it.compositeRule && schema[prop].default !== undefined; + } + function applyPropertySchema(prop) { + cxt.subschema({ + keyword: "properties", + schemaProp: prop, + dataProp: prop, + }, valid); + } + }, +}; +exports.default = def; +//# sourceMappingURL=properties.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/properties.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/properties.js.map new file mode 100644 index 00000000..27f8148c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/properties.js.map @@ -0,0 +1 @@ +{"version":3,"file":"properties.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/properties.ts"],"names":[],"mappings":";;AACA,qDAAiD;AACjD,kCAA2D;AAC3D,6CAA4E;AAC5E,iEAA0C;AAE1C,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,YAAY;IACrB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,QAAQ;IACpB,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACjD,IAAI,EAAE,CAAC,IAAI,CAAC,gBAAgB,KAAK,KAAK,IAAI,YAAY,CAAC,oBAAoB,KAAK,SAAS,EAAE;YACzF,8BAAK,CAAC,IAAI,CAAC,IAAI,qBAAU,CAAC,EAAE,EAAE,8BAAK,EAAE,sBAAsB,CAAC,CAAC,CAAA;SAC9D;QACD,MAAM,QAAQ,GAAG,IAAA,0BAAmB,EAAC,MAAM,CAAC,CAAA;QAC5C,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE;YAC3B,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;SAC/B;QACD,IAAI,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,KAAK,KAAK,IAAI,EAAE;YAC/D,EAAE,CAAC,KAAK,GAAG,qBAAc,CAAC,KAAK,CAAC,GAAG,EAAE,IAAA,aAAM,EAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAA;SACjE;QACD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC5E,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAM;QACnC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAE/B,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE;YAC7B,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE;gBACpB,mBAAmB,CAAC,IAAI,CAAC,CAAA;aAC1B;iBAAM;gBACL,GAAG,CAAC,EAAE,CAAC,IAAA,qBAAc,EAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAA;gBAC9D,mBAAmB,CAAC,IAAI,CAAC,CAAA;gBACzB,IAAI,CAAC,EAAE,CAAC,SAAS;oBAAE,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;gBAC9C,GAAG,CAAC,KAAK,EAAE,CAAA;aACZ;YACD,GAAG,CAAC,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAClC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;SACd;QAED,SAAS,UAAU,CAAC,IAAY;YAC9B,OAAO,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,EAAE,CAAC,aAAa,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,CAAA;QACvF,CAAC;QAED,SAAS,mBAAmB,CAAC,IAAY;YACvC,GAAG,CAAC,SAAS,CACX;gBACE,OAAO,EAAE,YAAY;gBACrB,UAAU,EAAE,IAAI;gBAChB,QAAQ,EAAE,IAAI;aACf,EACD,KAAK,CACN,CAAA;QACH,CAAC;IACH,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/propertyNames.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/propertyNames.d.ts new file mode 100644 index 00000000..a806da32 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/propertyNames.d.ts @@ -0,0 +1,6 @@ +import type { CodeKeywordDefinition, ErrorObject, AnySchema } from "../../types"; +export type PropertyNamesError = ErrorObject<"propertyNames", { + propertyName: string; +}, AnySchema>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js b/website/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js new file mode 100644 index 00000000..f3871152 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js @@ -0,0 +1,38 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const error = { + message: "property name must be valid", + params: ({ params }) => (0, codegen_1._) `{propertyName: ${params.propertyName}}`, +}; +const def = { + keyword: "propertyNames", + type: "object", + schemaType: ["object", "boolean"], + error, + code(cxt) { + const { gen, schema, data, it } = cxt; + if ((0, util_1.alwaysValidSchema)(it, schema)) + return; + const valid = gen.name("valid"); + gen.forIn("key", data, (key) => { + cxt.setParams({ propertyName: key }); + cxt.subschema({ + keyword: "propertyNames", + data: key, + dataTypes: ["string"], + propertyName: key, + compositeRule: true, + }, valid); + gen.if((0, codegen_1.not)(valid), () => { + cxt.error(true); + if (!it.allErrors) + gen.break(); + }); + }); + cxt.ok(valid); + }, +}; +exports.default = def; +//# sourceMappingURL=propertyNames.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js.map new file mode 100644 index 00000000..835b2bb1 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js.map @@ -0,0 +1 @@ +{"version":3,"file":"propertyNames.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/propertyNames.ts"],"names":[],"mappings":";;AAOA,mDAA4C;AAC5C,6CAAoD;AAIpD,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,6BAA6B;IACtC,MAAM,EAAE,CAAC,EAAC,MAAM,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,kBAAkB,MAAM,CAAC,YAAY,GAAG;CAChE,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,eAAe;IACxB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC;IACjC,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACnC,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC;YAAE,OAAM;QACzC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAE/B,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE;YAC7B,GAAG,CAAC,SAAS,CAAC,EAAC,YAAY,EAAE,GAAG,EAAC,CAAC,CAAA;YAClC,GAAG,CAAC,SAAS,CACX;gBACE,OAAO,EAAE,eAAe;gBACxB,IAAI,EAAE,GAAG;gBACT,SAAS,EAAE,CAAC,QAAQ,CAAC;gBACrB,YAAY,EAAE,GAAG;gBACjB,aAAa,EAAE,IAAI;aACpB,EACD,KAAK,CACN,CAAA;YACD,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,KAAK,CAAC,EAAE,GAAG,EAAE;gBACtB,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBACf,IAAI,CAAC,EAAE,CAAC,SAAS;oBAAE,GAAG,CAAC,KAAK,EAAE,CAAA;YAChC,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;QAEF,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;IACf,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/thenElse.d.ts b/website/node_modules/ajv/dist/vocabularies/applicator/thenElse.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/thenElse.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/thenElse.js b/website/node_modules/ajv/dist/vocabularies/applicator/thenElse.js new file mode 100644 index 00000000..1ae63902 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/thenElse.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const util_1 = require("../../compile/util"); +const def = { + keyword: ["then", "else"], + schemaType: ["object", "boolean"], + code({ keyword, parentSchema, it }) { + if (parentSchema.if === undefined) + (0, util_1.checkStrictMode)(it, `"${keyword}" without "if" is ignored`); + }, +}; +exports.default = def; +//# sourceMappingURL=thenElse.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/applicator/thenElse.js.map b/website/node_modules/ajv/dist/vocabularies/applicator/thenElse.js.map new file mode 100644 index 00000000..2629f4fc --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/applicator/thenElse.js.map @@ -0,0 +1 @@ +{"version":3,"file":"thenElse.js","sourceRoot":"","sources":["../../../lib/vocabularies/applicator/thenElse.ts"],"names":[],"mappings":";;AAEA,6CAAkD;AAElD,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC;IACjC,IAAI,CAAC,EAAC,OAAO,EAAE,YAAY,EAAE,EAAE,EAAa;QAC1C,IAAI,YAAY,CAAC,EAAE,KAAK,SAAS;YAAE,IAAA,sBAAe,EAAC,EAAE,EAAE,IAAI,OAAO,2BAA2B,CAAC,CAAA;IAChG,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/code.d.ts b/website/node_modules/ajv/dist/vocabularies/code.d.ts new file mode 100644 index 00000000..f914baca --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/code.d.ts @@ -0,0 +1,17 @@ +import type { SchemaMap } from "../types"; +import type { SchemaCxt } from "../compile"; +import type { KeywordCxt } from "../compile/validate"; +import { CodeGen, Code, Name } from "../compile/codegen"; +export declare function checkReportMissingProp(cxt: KeywordCxt, prop: string): void; +export declare function checkMissingProp({ gen, data, it: { opts } }: KeywordCxt, properties: string[], missing: Name): Code; +export declare function reportMissingProp(cxt: KeywordCxt, missing: Name): void; +export declare function hasPropFunc(gen: CodeGen): Name; +export declare function isOwnProperty(gen: CodeGen, data: Name, property: Name | string): Code; +export declare function propertyInData(gen: CodeGen, data: Name, property: Name | string, ownProperties?: boolean): Code; +export declare function noPropertyInData(gen: CodeGen, data: Name, property: Name | string, ownProperties?: boolean): Code; +export declare function allSchemaProperties(schemaMap?: SchemaMap): string[]; +export declare function schemaProperties(it: SchemaCxt, schemaMap: SchemaMap): string[]; +export declare function callValidateCode({ schemaCode, data, it: { gen, topSchemaRef, schemaPath, errorPath }, it }: KeywordCxt, func: Code, context: Code, passSchema?: boolean): Code; +export declare function usePattern({ gen, it: { opts } }: KeywordCxt, pattern: string): Name; +export declare function validateArray(cxt: KeywordCxt): Name; +export declare function validateUnion(cxt: KeywordCxt): void; diff --git a/website/node_modules/ajv/dist/vocabularies/code.js b/website/node_modules/ajv/dist/vocabularies/code.js new file mode 100644 index 00000000..8cb89932 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/code.js @@ -0,0 +1,131 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.validateUnion = exports.validateArray = exports.usePattern = exports.callValidateCode = exports.schemaProperties = exports.allSchemaProperties = exports.noPropertyInData = exports.propertyInData = exports.isOwnProperty = exports.hasPropFunc = exports.reportMissingProp = exports.checkMissingProp = exports.checkReportMissingProp = void 0; +const codegen_1 = require("../compile/codegen"); +const util_1 = require("../compile/util"); +const names_1 = require("../compile/names"); +const util_2 = require("../compile/util"); +function checkReportMissingProp(cxt, prop) { + const { gen, data, it } = cxt; + gen.if(noPropertyInData(gen, data, prop, it.opts.ownProperties), () => { + cxt.setParams({ missingProperty: (0, codegen_1._) `${prop}` }, true); + cxt.error(); + }); +} +exports.checkReportMissingProp = checkReportMissingProp; +function checkMissingProp({ gen, data, it: { opts } }, properties, missing) { + return (0, codegen_1.or)(...properties.map((prop) => (0, codegen_1.and)(noPropertyInData(gen, data, prop, opts.ownProperties), (0, codegen_1._) `${missing} = ${prop}`))); +} +exports.checkMissingProp = checkMissingProp; +function reportMissingProp(cxt, missing) { + cxt.setParams({ missingProperty: missing }, true); + cxt.error(); +} +exports.reportMissingProp = reportMissingProp; +function hasPropFunc(gen) { + return gen.scopeValue("func", { + // eslint-disable-next-line @typescript-eslint/unbound-method + ref: Object.prototype.hasOwnProperty, + code: (0, codegen_1._) `Object.prototype.hasOwnProperty`, + }); +} +exports.hasPropFunc = hasPropFunc; +function isOwnProperty(gen, data, property) { + return (0, codegen_1._) `${hasPropFunc(gen)}.call(${data}, ${property})`; +} +exports.isOwnProperty = isOwnProperty; +function propertyInData(gen, data, property, ownProperties) { + const cond = (0, codegen_1._) `${data}${(0, codegen_1.getProperty)(property)} !== undefined`; + return ownProperties ? (0, codegen_1._) `${cond} && ${isOwnProperty(gen, data, property)}` : cond; +} +exports.propertyInData = propertyInData; +function noPropertyInData(gen, data, property, ownProperties) { + const cond = (0, codegen_1._) `${data}${(0, codegen_1.getProperty)(property)} === undefined`; + return ownProperties ? (0, codegen_1.or)(cond, (0, codegen_1.not)(isOwnProperty(gen, data, property))) : cond; +} +exports.noPropertyInData = noPropertyInData; +function allSchemaProperties(schemaMap) { + return schemaMap ? Object.keys(schemaMap).filter((p) => p !== "__proto__") : []; +} +exports.allSchemaProperties = allSchemaProperties; +function schemaProperties(it, schemaMap) { + return allSchemaProperties(schemaMap).filter((p) => !(0, util_1.alwaysValidSchema)(it, schemaMap[p])); +} +exports.schemaProperties = schemaProperties; +function callValidateCode({ schemaCode, data, it: { gen, topSchemaRef, schemaPath, errorPath }, it }, func, context, passSchema) { + const dataAndSchema = passSchema ? (0, codegen_1._) `${schemaCode}, ${data}, ${topSchemaRef}${schemaPath}` : data; + const valCxt = [ + [names_1.default.instancePath, (0, codegen_1.strConcat)(names_1.default.instancePath, errorPath)], + [names_1.default.parentData, it.parentData], + [names_1.default.parentDataProperty, it.parentDataProperty], + [names_1.default.rootData, names_1.default.rootData], + ]; + if (it.opts.dynamicRef) + valCxt.push([names_1.default.dynamicAnchors, names_1.default.dynamicAnchors]); + const args = (0, codegen_1._) `${dataAndSchema}, ${gen.object(...valCxt)}`; + return context !== codegen_1.nil ? (0, codegen_1._) `${func}.call(${context}, ${args})` : (0, codegen_1._) `${func}(${args})`; +} +exports.callValidateCode = callValidateCode; +const newRegExp = (0, codegen_1._) `new RegExp`; +function usePattern({ gen, it: { opts } }, pattern) { + const u = opts.unicodeRegExp ? "u" : ""; + const { regExp } = opts.code; + const rx = regExp(pattern, u); + return gen.scopeValue("pattern", { + key: rx.toString(), + ref: rx, + code: (0, codegen_1._) `${regExp.code === "new RegExp" ? newRegExp : (0, util_2.useFunc)(gen, regExp)}(${pattern}, ${u})`, + }); +} +exports.usePattern = usePattern; +function validateArray(cxt) { + const { gen, data, keyword, it } = cxt; + const valid = gen.name("valid"); + if (it.allErrors) { + const validArr = gen.let("valid", true); + validateItems(() => gen.assign(validArr, false)); + return validArr; + } + gen.var(valid, true); + validateItems(() => gen.break()); + return valid; + function validateItems(notValid) { + const len = gen.const("len", (0, codegen_1._) `${data}.length`); + gen.forRange("i", 0, len, (i) => { + cxt.subschema({ + keyword, + dataProp: i, + dataPropType: util_1.Type.Num, + }, valid); + gen.if((0, codegen_1.not)(valid), notValid); + }); + } +} +exports.validateArray = validateArray; +function validateUnion(cxt) { + const { gen, schema, keyword, it } = cxt; + /* istanbul ignore if */ + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + const alwaysValid = schema.some((sch) => (0, util_1.alwaysValidSchema)(it, sch)); + if (alwaysValid && !it.opts.unevaluated) + return; + const valid = gen.let("valid", false); + const schValid = gen.name("_valid"); + gen.block(() => schema.forEach((_sch, i) => { + const schCxt = cxt.subschema({ + keyword, + schemaProp: i, + compositeRule: true, + }, schValid); + gen.assign(valid, (0, codegen_1._) `${valid} || ${schValid}`); + const merged = cxt.mergeValidEvaluated(schCxt, schValid); + // can short-circuit if `unevaluatedProperties/Items` not supported (opts.unevaluated !== true) + // or if all properties and items were evaluated (it.props === true && it.items === true) + if (!merged) + gen.if((0, codegen_1.not)(valid)); + })); + cxt.result(valid, () => cxt.reset(), () => cxt.error(true)); +} +exports.validateUnion = validateUnion; +//# sourceMappingURL=code.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/code.js.map b/website/node_modules/ajv/dist/vocabularies/code.js.map new file mode 100644 index 00000000..62d7ee12 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/code.js.map @@ -0,0 +1 @@ +{"version":3,"file":"code.js","sourceRoot":"","sources":["../../lib/vocabularies/code.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/core/id.d.ts b/website/node_modules/ajv/dist/vocabularies/core/id.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/core/id.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/core/id.js b/website/node_modules/ajv/dist/vocabularies/core/id.js new file mode 100644 index 00000000..313598aa --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/core/id.js @@ -0,0 +1,10 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const def = { + keyword: "id", + code() { + throw new Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID'); + }, +}; +exports.default = def; +//# sourceMappingURL=id.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/core/id.js.map b/website/node_modules/ajv/dist/vocabularies/core/id.js.map new file mode 100644 index 00000000..4eb27eb3 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/core/id.js.map @@ -0,0 +1 @@ +{"version":3,"file":"id.js","sourceRoot":"","sources":["../../../lib/vocabularies/core/id.ts"],"names":[],"mappings":";;AAEA,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,IAAI;IACb,IAAI;QACF,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAA;IACzE,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/core/index.d.ts b/website/node_modules/ajv/dist/vocabularies/core/index.d.ts new file mode 100644 index 00000000..f2e34ee3 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/core/index.d.ts @@ -0,0 +1,3 @@ +import type { Vocabulary } from "../../types"; +declare const core: Vocabulary; +export default core; diff --git a/website/node_modules/ajv/dist/vocabularies/core/index.js b/website/node_modules/ajv/dist/vocabularies/core/index.js new file mode 100644 index 00000000..87656d74 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/core/index.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const id_1 = require("./id"); +const ref_1 = require("./ref"); +const core = [ + "$schema", + "$id", + "$defs", + "$vocabulary", + { keyword: "$comment" }, + "definitions", + id_1.default, + ref_1.default, +]; +exports.default = core; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/core/index.js.map b/website/node_modules/ajv/dist/vocabularies/core/index.js.map new file mode 100644 index 00000000..5bf65f94 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/core/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/vocabularies/core/index.ts"],"names":[],"mappings":";;AACA,6BAA4B;AAC5B,+BAA8B;AAE9B,MAAM,IAAI,GAAe;IACvB,SAAS;IACT,KAAK;IACL,OAAO;IACP,aAAa;IACb,EAAC,OAAO,EAAE,UAAU,EAAC;IACrB,aAAa;IACb,YAAS;IACT,aAAU;CACX,CAAA;AAED,kBAAe,IAAI,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/core/ref.d.ts b/website/node_modules/ajv/dist/vocabularies/core/ref.d.ts new file mode 100644 index 00000000..6a0967d1 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/core/ref.d.ts @@ -0,0 +1,8 @@ +import type { CodeKeywordDefinition } from "../../types"; +import type { KeywordCxt } from "../../compile/validate"; +import { Code } from "../../compile/codegen"; +import { SchemaEnv } from "../../compile"; +declare const def: CodeKeywordDefinition; +export declare function getValidate(cxt: KeywordCxt, sch: SchemaEnv): Code; +export declare function callRef(cxt: KeywordCxt, v: Code, sch?: SchemaEnv, $async?: boolean): void; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/core/ref.js b/website/node_modules/ajv/dist/vocabularies/core/ref.js new file mode 100644 index 00000000..bac1ae85 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/core/ref.js @@ -0,0 +1,122 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.callRef = exports.getValidate = void 0; +const ref_error_1 = require("../../compile/ref_error"); +const code_1 = require("../code"); +const codegen_1 = require("../../compile/codegen"); +const names_1 = require("../../compile/names"); +const compile_1 = require("../../compile"); +const util_1 = require("../../compile/util"); +const def = { + keyword: "$ref", + schemaType: "string", + code(cxt) { + const { gen, schema: $ref, it } = cxt; + const { baseId, schemaEnv: env, validateName, opts, self } = it; + const { root } = env; + if (($ref === "#" || $ref === "#/") && baseId === root.baseId) + return callRootRef(); + const schOrEnv = compile_1.resolveRef.call(self, root, baseId, $ref); + if (schOrEnv === undefined) + throw new ref_error_1.default(it.opts.uriResolver, baseId, $ref); + if (schOrEnv instanceof compile_1.SchemaEnv) + return callValidate(schOrEnv); + return inlineRefSchema(schOrEnv); + function callRootRef() { + if (env === root) + return callRef(cxt, validateName, env, env.$async); + const rootName = gen.scopeValue("root", { ref: root }); + return callRef(cxt, (0, codegen_1._) `${rootName}.validate`, root, root.$async); + } + function callValidate(sch) { + const v = getValidate(cxt, sch); + callRef(cxt, v, sch, sch.$async); + } + function inlineRefSchema(sch) { + const schName = gen.scopeValue("schema", opts.code.source === true ? { ref: sch, code: (0, codegen_1.stringify)(sch) } : { ref: sch }); + const valid = gen.name("valid"); + const schCxt = cxt.subschema({ + schema: sch, + dataTypes: [], + schemaPath: codegen_1.nil, + topSchemaRef: schName, + errSchemaPath: $ref, + }, valid); + cxt.mergeEvaluated(schCxt); + cxt.ok(valid); + } + }, +}; +function getValidate(cxt, sch) { + const { gen } = cxt; + return sch.validate + ? gen.scopeValue("validate", { ref: sch.validate }) + : (0, codegen_1._) `${gen.scopeValue("wrapper", { ref: sch })}.validate`; +} +exports.getValidate = getValidate; +function callRef(cxt, v, sch, $async) { + const { gen, it } = cxt; + const { allErrors, schemaEnv: env, opts } = it; + const passCxt = opts.passContext ? names_1.default.this : codegen_1.nil; + if ($async) + callAsyncRef(); + else + callSyncRef(); + function callAsyncRef() { + if (!env.$async) + throw new Error("async schema referenced by sync schema"); + const valid = gen.let("valid"); + gen.try(() => { + gen.code((0, codegen_1._) `await ${(0, code_1.callValidateCode)(cxt, v, passCxt)}`); + addEvaluatedFrom(v); // TODO will not work with async, it has to be returned with the result + if (!allErrors) + gen.assign(valid, true); + }, (e) => { + gen.if((0, codegen_1._) `!(${e} instanceof ${it.ValidationError})`, () => gen.throw(e)); + addErrorsFrom(e); + if (!allErrors) + gen.assign(valid, false); + }); + cxt.ok(valid); + } + function callSyncRef() { + cxt.result((0, code_1.callValidateCode)(cxt, v, passCxt), () => addEvaluatedFrom(v), () => addErrorsFrom(v)); + } + function addErrorsFrom(source) { + const errs = (0, codegen_1._) `${source}.errors`; + gen.assign(names_1.default.vErrors, (0, codegen_1._) `${names_1.default.vErrors} === null ? ${errs} : ${names_1.default.vErrors}.concat(${errs})`); // TODO tagged + gen.assign(names_1.default.errors, (0, codegen_1._) `${names_1.default.vErrors}.length`); + } + function addEvaluatedFrom(source) { + var _a; + if (!it.opts.unevaluated) + return; + const schEvaluated = (_a = sch === null || sch === void 0 ? void 0 : sch.validate) === null || _a === void 0 ? void 0 : _a.evaluated; + // TODO refactor + if (it.props !== true) { + if (schEvaluated && !schEvaluated.dynamicProps) { + if (schEvaluated.props !== undefined) { + it.props = util_1.mergeEvaluated.props(gen, schEvaluated.props, it.props); + } + } + else { + const props = gen.var("props", (0, codegen_1._) `${source}.evaluated.props`); + it.props = util_1.mergeEvaluated.props(gen, props, it.props, codegen_1.Name); + } + } + if (it.items !== true) { + if (schEvaluated && !schEvaluated.dynamicItems) { + if (schEvaluated.items !== undefined) { + it.items = util_1.mergeEvaluated.items(gen, schEvaluated.items, it.items); + } + } + else { + const items = gen.var("items", (0, codegen_1._) `${source}.evaluated.items`); + it.items = util_1.mergeEvaluated.items(gen, items, it.items, codegen_1.Name); + } + } + } +} +exports.callRef = callRef; +exports.default = def; +//# sourceMappingURL=ref.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/core/ref.js.map b/website/node_modules/ajv/dist/vocabularies/core/ref.js.map new file mode 100644 index 00000000..88ac106a --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/core/ref.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ref.js","sourceRoot":"","sources":["../../../lib/vocabularies/core/ref.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/discriminator/index.d.ts b/website/node_modules/ajv/dist/vocabularies/discriminator/index.d.ts new file mode 100644 index 00000000..ab3669a4 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/discriminator/index.d.ts @@ -0,0 +1,5 @@ +import type { CodeKeywordDefinition } from "../../types"; +import { DiscrError, DiscrErrorObj } from "../discriminator/types"; +export type DiscriminatorError = DiscrErrorObj | DiscrErrorObj; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/discriminator/index.js b/website/node_modules/ajv/dist/vocabularies/discriminator/index.js new file mode 100644 index 00000000..ccdb340b --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/discriminator/index.js @@ -0,0 +1,100 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const types_1 = require("../discriminator/types"); +const compile_1 = require("../../compile"); +const util_1 = require("../../compile/util"); +const error = { + message: ({ params: { discrError, tagName } }) => discrError === types_1.DiscrError.Tag + ? `tag "${tagName}" must be string` + : `value of tag "${tagName}" must be in oneOf`, + params: ({ params: { discrError, tag, tagName } }) => (0, codegen_1._) `{error: ${discrError}, tag: ${tagName}, tagValue: ${tag}}`, +}; +const def = { + keyword: "discriminator", + type: "object", + schemaType: "object", + error, + code(cxt) { + const { gen, data, schema, parentSchema, it } = cxt; + const { oneOf } = parentSchema; + if (!it.opts.discriminator) { + throw new Error("discriminator: requires discriminator option"); + } + const tagName = schema.propertyName; + if (typeof tagName != "string") + throw new Error("discriminator: requires propertyName"); + if (schema.mapping) + throw new Error("discriminator: mapping is not supported"); + if (!oneOf) + throw new Error("discriminator: requires oneOf keyword"); + const valid = gen.let("valid", false); + const tag = gen.const("tag", (0, codegen_1._) `${data}${(0, codegen_1.getProperty)(tagName)}`); + gen.if((0, codegen_1._) `typeof ${tag} == "string"`, () => validateMapping(), () => cxt.error(false, { discrError: types_1.DiscrError.Tag, tag, tagName })); + cxt.ok(valid); + function validateMapping() { + const mapping = getMapping(); + gen.if(false); + for (const tagValue in mapping) { + gen.elseIf((0, codegen_1._) `${tag} === ${tagValue}`); + gen.assign(valid, applyTagSchema(mapping[tagValue])); + } + gen.else(); + cxt.error(false, { discrError: types_1.DiscrError.Mapping, tag, tagName }); + gen.endIf(); + } + function applyTagSchema(schemaProp) { + const _valid = gen.name("valid"); + const schCxt = cxt.subschema({ keyword: "oneOf", schemaProp }, _valid); + cxt.mergeEvaluated(schCxt, codegen_1.Name); + return _valid; + } + function getMapping() { + var _a; + const oneOfMapping = {}; + const topRequired = hasRequired(parentSchema); + let tagRequired = true; + for (let i = 0; i < oneOf.length; i++) { + let sch = oneOf[i]; + if ((sch === null || sch === void 0 ? void 0 : sch.$ref) && !(0, util_1.schemaHasRulesButRef)(sch, it.self.RULES)) { + sch = compile_1.resolveRef.call(it.self, it.schemaEnv.root, it.baseId, sch === null || sch === void 0 ? void 0 : sch.$ref); + if (sch instanceof compile_1.SchemaEnv) + sch = sch.schema; + } + const propSch = (_a = sch === null || sch === void 0 ? void 0 : sch.properties) === null || _a === void 0 ? void 0 : _a[tagName]; + if (typeof propSch != "object") { + throw new Error(`discriminator: oneOf subschemas (or referenced schemas) must have "properties/${tagName}"`); + } + tagRequired = tagRequired && (topRequired || hasRequired(sch)); + addMappings(propSch, i); + } + if (!tagRequired) + throw new Error(`discriminator: "${tagName}" must be required`); + return oneOfMapping; + function hasRequired({ required }) { + return Array.isArray(required) && required.includes(tagName); + } + function addMappings(sch, i) { + if (sch.const) { + addMapping(sch.const, i); + } + else if (sch.enum) { + for (const tagValue of sch.enum) { + addMapping(tagValue, i); + } + } + else { + throw new Error(`discriminator: "properties/${tagName}" must have "const" or "enum"`); + } + } + function addMapping(tagValue, i) { + if (typeof tagValue != "string" || tagValue in oneOfMapping) { + throw new Error(`discriminator: "${tagName}" values must be unique strings`); + } + oneOfMapping[tagValue] = i; + } + } + }, +}; +exports.default = def; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/discriminator/index.js.map b/website/node_modules/ajv/dist/vocabularies/discriminator/index.js.map new file mode 100644 index 00000000..8057a4ed --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/discriminator/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/vocabularies/discriminator/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/discriminator/types.d.ts b/website/node_modules/ajv/dist/vocabularies/discriminator/types.d.ts new file mode 100644 index 00000000..8550f6d9 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/discriminator/types.d.ts @@ -0,0 +1,10 @@ +import type { ErrorObject } from "../../types"; +export declare enum DiscrError { + Tag = "tag", + Mapping = "mapping" +} +export type DiscrErrorObj = ErrorObject<"discriminator", { + error: E; + tag: string; + tagValue: unknown; +}, string>; diff --git a/website/node_modules/ajv/dist/vocabularies/discriminator/types.js b/website/node_modules/ajv/dist/vocabularies/discriminator/types.js new file mode 100644 index 00000000..d538f0ce --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/discriminator/types.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DiscrError = void 0; +var DiscrError; +(function (DiscrError) { + DiscrError["Tag"] = "tag"; + DiscrError["Mapping"] = "mapping"; +})(DiscrError = exports.DiscrError || (exports.DiscrError = {})); +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/discriminator/types.js.map b/website/node_modules/ajv/dist/vocabularies/discriminator/types.js.map new file mode 100644 index 00000000..4908a4e9 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/discriminator/types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../lib/vocabularies/discriminator/types.ts"],"names":[],"mappings":";;;AAEA,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,yBAAW,CAAA;IACX,iCAAmB,CAAA;AACrB,CAAC,EAHW,UAAU,GAAV,kBAAU,KAAV,kBAAU,QAGrB"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/draft2020.d.ts b/website/node_modules/ajv/dist/vocabularies/draft2020.d.ts new file mode 100644 index 00000000..d65752c6 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/draft2020.d.ts @@ -0,0 +1,3 @@ +import type { Vocabulary } from "../types"; +declare const draft2020Vocabularies: Vocabulary[]; +export default draft2020Vocabularies; diff --git a/website/node_modules/ajv/dist/vocabularies/draft2020.js b/website/node_modules/ajv/dist/vocabularies/draft2020.js new file mode 100644 index 00000000..23d244ae --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/draft2020.js @@ -0,0 +1,23 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const core_1 = require("./core"); +const validation_1 = require("./validation"); +const applicator_1 = require("./applicator"); +const dynamic_1 = require("./dynamic"); +const next_1 = require("./next"); +const unevaluated_1 = require("./unevaluated"); +const format_1 = require("./format"); +const metadata_1 = require("./metadata"); +const draft2020Vocabularies = [ + dynamic_1.default, + core_1.default, + validation_1.default, + (0, applicator_1.default)(true), + format_1.default, + metadata_1.metadataVocabulary, + metadata_1.contentVocabulary, + next_1.default, + unevaluated_1.default, +]; +exports.default = draft2020Vocabularies; +//# sourceMappingURL=draft2020.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/draft2020.js.map b/website/node_modules/ajv/dist/vocabularies/draft2020.js.map new file mode 100644 index 00000000..ae1a4d83 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/draft2020.js.map @@ -0,0 +1 @@ +{"version":3,"file":"draft2020.js","sourceRoot":"","sources":["../../lib/vocabularies/draft2020.ts"],"names":[],"mappings":";;AACA,iCAAmC;AACnC,6CAA+C;AAC/C,6CAAkD;AAClD,uCAAyC;AACzC,iCAAmC;AACnC,+CAAiD;AACjD,qCAAuC;AACvC,yCAAgE;AAEhE,MAAM,qBAAqB,GAAiB;IAC1C,iBAAiB;IACjB,cAAc;IACd,oBAAoB;IACpB,IAAA,oBAAuB,EAAC,IAAI,CAAC;IAC7B,gBAAgB;IAChB,6BAAkB;IAClB,4BAAiB;IACjB,cAAc;IACd,qBAAqB;CACtB,CAAA;AAED,kBAAe,qBAAqB,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/draft7.d.ts b/website/node_modules/ajv/dist/vocabularies/draft7.d.ts new file mode 100644 index 00000000..469fb844 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/draft7.d.ts @@ -0,0 +1,3 @@ +import type { Vocabulary } from "../types"; +declare const draft7Vocabularies: Vocabulary[]; +export default draft7Vocabularies; diff --git a/website/node_modules/ajv/dist/vocabularies/draft7.js b/website/node_modules/ajv/dist/vocabularies/draft7.js new file mode 100644 index 00000000..1e993de0 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/draft7.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const core_1 = require("./core"); +const validation_1 = require("./validation"); +const applicator_1 = require("./applicator"); +const format_1 = require("./format"); +const metadata_1 = require("./metadata"); +const draft7Vocabularies = [ + core_1.default, + validation_1.default, + (0, applicator_1.default)(), + format_1.default, + metadata_1.metadataVocabulary, + metadata_1.contentVocabulary, +]; +exports.default = draft7Vocabularies; +//# sourceMappingURL=draft7.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/draft7.js.map b/website/node_modules/ajv/dist/vocabularies/draft7.js.map new file mode 100644 index 00000000..bc7389c6 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/draft7.js.map @@ -0,0 +1 @@ +{"version":3,"file":"draft7.js","sourceRoot":"","sources":["../../lib/vocabularies/draft7.ts"],"names":[],"mappings":";;AACA,iCAAmC;AACnC,6CAA+C;AAC/C,6CAAkD;AAClD,qCAAuC;AACvC,yCAAgE;AAEhE,MAAM,kBAAkB,GAAiB;IACvC,cAAc;IACd,oBAAoB;IACpB,IAAA,oBAAuB,GAAE;IACzB,gBAAgB;IAChB,6BAAkB;IAClB,4BAAiB;CAClB,CAAA;AAED,kBAAe,kBAAkB,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.d.ts b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.d.ts new file mode 100644 index 00000000..56212139 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.d.ts @@ -0,0 +1,5 @@ +import type { CodeKeywordDefinition } from "../../types"; +import type { KeywordCxt } from "../../compile/validate"; +declare const def: CodeKeywordDefinition; +export declare function dynamicAnchor(cxt: KeywordCxt, anchor: string): void; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.js b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.js new file mode 100644 index 00000000..972dc35c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.dynamicAnchor = void 0; +const codegen_1 = require("../../compile/codegen"); +const names_1 = require("../../compile/names"); +const compile_1 = require("../../compile"); +const ref_1 = require("../core/ref"); +const def = { + keyword: "$dynamicAnchor", + schemaType: "string", + code: (cxt) => dynamicAnchor(cxt, cxt.schema), +}; +function dynamicAnchor(cxt, anchor) { + const { gen, it } = cxt; + it.schemaEnv.root.dynamicAnchors[anchor] = true; + const v = (0, codegen_1._) `${names_1.default.dynamicAnchors}${(0, codegen_1.getProperty)(anchor)}`; + const validate = it.errSchemaPath === "#" ? it.validateName : _getValidate(cxt); + gen.if((0, codegen_1._) `!${v}`, () => gen.assign(v, validate)); +} +exports.dynamicAnchor = dynamicAnchor; +function _getValidate(cxt) { + const { schemaEnv, schema, self } = cxt.it; + const { root, baseId, localRefs, meta } = schemaEnv.root; + const { schemaId } = self.opts; + const sch = new compile_1.SchemaEnv({ schema, schemaId, root, baseId, localRefs, meta }); + compile_1.compileSchema.call(self, sch); + return (0, ref_1.getValidate)(cxt, sch); +} +exports.default = def; +//# sourceMappingURL=dynamicAnchor.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.js.map b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.js.map new file mode 100644 index 00000000..e70afe30 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dynamicAnchor.js","sourceRoot":"","sources":["../../../lib/vocabularies/dynamic/dynamicAnchor.ts"],"names":[],"mappings":";;;AAEA,mDAA0D;AAC1D,+CAAmC;AACnC,2CAAsD;AACtD,qCAAuC;AAEvC,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,gBAAgB;IACzB,UAAU,EAAE,QAAQ;IACpB,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC;CAC9C,CAAA;AAED,SAAgB,aAAa,CAAC,GAAe,EAAE,MAAc;IAC3D,MAAM,EAAC,GAAG,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;IACrB,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;IAC/C,MAAM,CAAC,GAAG,IAAA,WAAC,EAAA,GAAG,eAAC,CAAC,cAAc,GAAG,IAAA,qBAAW,EAAC,MAAM,CAAC,EAAE,CAAA;IACtD,MAAM,QAAQ,GAAG,EAAE,CAAC,aAAa,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;IAC/E,GAAG,CAAC,EAAE,CAAC,IAAA,WAAC,EAAA,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;AACjD,CAAC;AAND,sCAMC;AAED,SAAS,YAAY,CAAC,GAAe;IACnC,MAAM,EAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAC,GAAG,GAAG,CAAC,EAAE,CAAA;IACxC,MAAM,EAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAC,GAAG,SAAS,CAAC,IAAI,CAAA;IACtD,MAAM,EAAC,QAAQ,EAAC,GAAG,IAAI,CAAC,IAAI,CAAA;IAC5B,MAAM,GAAG,GAAG,IAAI,mBAAS,CAAC,EAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAC,CAAC,CAAA;IAC5E,uBAAa,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAC7B,OAAO,IAAA,iBAAW,EAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAC9B,CAAC;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.d.ts b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.d.ts new file mode 100644 index 00000000..fa2f2b81 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.d.ts @@ -0,0 +1,5 @@ +import type { CodeKeywordDefinition } from "../../types"; +import type { KeywordCxt } from "../../compile/validate"; +declare const def: CodeKeywordDefinition; +export declare function dynamicRef(cxt: KeywordCxt, ref: string): void; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.js b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.js new file mode 100644 index 00000000..9f010a0d --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.js @@ -0,0 +1,51 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.dynamicRef = void 0; +const codegen_1 = require("../../compile/codegen"); +const names_1 = require("../../compile/names"); +const ref_1 = require("../core/ref"); +const def = { + keyword: "$dynamicRef", + schemaType: "string", + code: (cxt) => dynamicRef(cxt, cxt.schema), +}; +function dynamicRef(cxt, ref) { + const { gen, keyword, it } = cxt; + if (ref[0] !== "#") + throw new Error(`"${keyword}" only supports hash fragment reference`); + const anchor = ref.slice(1); + if (it.allErrors) { + _dynamicRef(); + } + else { + const valid = gen.let("valid", false); + _dynamicRef(valid); + cxt.ok(valid); + } + function _dynamicRef(valid) { + // TODO the assumption here is that `recursiveRef: #` always points to the root + // of the schema object, which is not correct, because there may be $id that + // makes # point to it, and the target schema may not contain dynamic/recursiveAnchor. + // Because of that 2 tests in recursiveRef.json fail. + // This is a similar problem to #815 (`$id` doesn't alter resolution scope for `{ "$ref": "#" }`). + // (This problem is not tested in JSON-Schema-Test-Suite) + if (it.schemaEnv.root.dynamicAnchors[anchor]) { + const v = gen.let("_v", (0, codegen_1._) `${names_1.default.dynamicAnchors}${(0, codegen_1.getProperty)(anchor)}`); + gen.if(v, _callRef(v, valid), _callRef(it.validateName, valid)); + } + else { + _callRef(it.validateName, valid)(); + } + } + function _callRef(validate, valid) { + return valid + ? () => gen.block(() => { + (0, ref_1.callRef)(cxt, validate); + gen.let(valid, true); + }) + : () => (0, ref_1.callRef)(cxt, validate); + } +} +exports.dynamicRef = dynamicRef; +exports.default = def; +//# sourceMappingURL=dynamicRef.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.js.map b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.js.map new file mode 100644 index 00000000..921c893f --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dynamicRef.js","sourceRoot":"","sources":["../../../lib/vocabularies/dynamic/dynamicRef.ts"],"names":[],"mappings":";;;AAEA,mDAAgE;AAChE,+CAAmC;AACnC,qCAAmC;AAEnC,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,aAAa;IACtB,UAAU,EAAE,QAAQ;IACpB,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC;CAC3C,CAAA;AAED,SAAgB,UAAU,CAAC,GAAe,EAAE,GAAW;IACrD,MAAM,EAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;IAC9B,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,IAAI,OAAO,yCAAyC,CAAC,CAAA;IACzF,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAC3B,IAAI,EAAE,CAAC,SAAS,EAAE;QAChB,WAAW,EAAE,CAAA;KACd;SAAM;QACL,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACrC,WAAW,CAAC,KAAK,CAAC,CAAA;QAClB,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;KACd;IAED,SAAS,WAAW,CAAC,KAAY;QAC/B,+EAA+E;QAC/E,4EAA4E;QAC5E,sFAAsF;QACtF,qDAAqD;QACrD,kGAAkG;QAClG,yDAAyD;QACzD,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE;YAC5C,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,IAAA,WAAC,EAAA,GAAG,eAAC,CAAC,cAAc,GAAG,IAAA,qBAAW,EAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YACrE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAA;SAChE;aAAM;YACL,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE,CAAA;SACnC;IACH,CAAC;IAED,SAAS,QAAQ,CAAC,QAAc,EAAE,KAAY;QAC5C,OAAO,KAAK;YACV,CAAC,CAAC,GAAG,EAAE,CACH,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE;gBACb,IAAA,aAAO,EAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;gBACtB,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;YACtB,CAAC,CAAC;YACN,CAAC,CAAC,GAAG,EAAE,CAAC,IAAA,aAAO,EAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAClC,CAAC;AACH,CAAC;AApCD,gCAoCC;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/index.d.ts b/website/node_modules/ajv/dist/vocabularies/dynamic/index.d.ts new file mode 100644 index 00000000..0c751d93 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/index.d.ts @@ -0,0 +1,3 @@ +import type { Vocabulary } from "../../types"; +declare const dynamic: Vocabulary; +export default dynamic; diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/index.js b/website/node_modules/ajv/dist/vocabularies/dynamic/index.js new file mode 100644 index 00000000..f2388a75 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/index.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const dynamicAnchor_1 = require("./dynamicAnchor"); +const dynamicRef_1 = require("./dynamicRef"); +const recursiveAnchor_1 = require("./recursiveAnchor"); +const recursiveRef_1 = require("./recursiveRef"); +const dynamic = [dynamicAnchor_1.default, dynamicRef_1.default, recursiveAnchor_1.default, recursiveRef_1.default]; +exports.default = dynamic; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/index.js.map b/website/node_modules/ajv/dist/vocabularies/dynamic/index.js.map new file mode 100644 index 00000000..f96ba76d --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/vocabularies/dynamic/index.ts"],"names":[],"mappings":";;AACA,mDAA2C;AAC3C,6CAAqC;AACrC,uDAA+C;AAC/C,iDAAyC;AAEzC,MAAM,OAAO,GAAe,CAAC,uBAAa,EAAE,oBAAU,EAAE,yBAAe,EAAE,sBAAY,CAAC,CAAA;AAEtF,kBAAe,OAAO,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.d.ts b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.js b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.js new file mode 100644 index 00000000..9fd83235 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const dynamicAnchor_1 = require("./dynamicAnchor"); +const util_1 = require("../../compile/util"); +const def = { + keyword: "$recursiveAnchor", + schemaType: "boolean", + code(cxt) { + if (cxt.schema) + (0, dynamicAnchor_1.dynamicAnchor)(cxt, ""); + else + (0, util_1.checkStrictMode)(cxt.it, "$recursiveAnchor: false is ignored"); + }, +}; +exports.default = def; +//# sourceMappingURL=recursiveAnchor.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.js.map b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.js.map new file mode 100644 index 00000000..5d5e381b --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.js.map @@ -0,0 +1 @@ +{"version":3,"file":"recursiveAnchor.js","sourceRoot":"","sources":["../../../lib/vocabularies/dynamic/recursiveAnchor.ts"],"names":[],"mappings":";;AACA,mDAA6C;AAC7C,6CAAkD;AAElD,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,kBAAkB;IAC3B,UAAU,EAAE,SAAS;IACrB,IAAI,CAAC,GAAG;QACN,IAAI,GAAG,CAAC,MAAM;YAAE,IAAA,6BAAa,EAAC,GAAG,EAAE,EAAE,CAAC,CAAA;;YACjC,IAAA,sBAAe,EAAC,GAAG,CAAC,EAAE,EAAE,oCAAoC,CAAC,CAAA;IACpE,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.d.ts b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.js b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.js new file mode 100644 index 00000000..8cd5c696 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.js @@ -0,0 +1,10 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const dynamicRef_1 = require("./dynamicRef"); +const def = { + keyword: "$recursiveRef", + schemaType: "string", + code: (cxt) => (0, dynamicRef_1.dynamicRef)(cxt, cxt.schema), +}; +exports.default = def; +//# sourceMappingURL=recursiveRef.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.js.map b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.js.map new file mode 100644 index 00000000..f8138044 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.js.map @@ -0,0 +1 @@ +{"version":3,"file":"recursiveRef.js","sourceRoot":"","sources":["../../../lib/vocabularies/dynamic/recursiveRef.ts"],"names":[],"mappings":";;AACA,6CAAuC;AAEvC,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,eAAe;IACxB,UAAU,EAAE,QAAQ;IACpB,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAA,uBAAU,EAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC;CAC3C,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/errors.d.ts b/website/node_modules/ajv/dist/vocabularies/errors.d.ts new file mode 100644 index 00000000..be67f2e8 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/errors.d.ts @@ -0,0 +1,9 @@ +import type { TypeError } from "../compile/validate/dataType"; +import type { ApplicatorKeywordError } from "./applicator"; +import type { ValidationKeywordError } from "./validation"; +import type { FormatError } from "./format/format"; +import type { UnevaluatedPropertiesError } from "./unevaluated/unevaluatedProperties"; +import type { UnevaluatedItemsError } from "./unevaluated/unevaluatedItems"; +import type { DependentRequiredError } from "./validation/dependentRequired"; +import type { DiscriminatorError } from "./discriminator"; +export type DefinedError = TypeError | ApplicatorKeywordError | ValidationKeywordError | FormatError | UnevaluatedPropertiesError | UnevaluatedItemsError | DependentRequiredError | DiscriminatorError; diff --git a/website/node_modules/ajv/dist/vocabularies/errors.js b/website/node_modules/ajv/dist/vocabularies/errors.js new file mode 100644 index 00000000..d4d3fba0 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/errors.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=errors.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/errors.js.map b/website/node_modules/ajv/dist/vocabularies/errors.js.map new file mode 100644 index 00000000..56bad736 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/errors.js.map @@ -0,0 +1 @@ +{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../lib/vocabularies/errors.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/format/format.d.ts b/website/node_modules/ajv/dist/vocabularies/format/format.d.ts new file mode 100644 index 00000000..04dc98f6 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/format/format.d.ts @@ -0,0 +1,8 @@ +import type { CodeKeywordDefinition, ErrorObject } from "../../types"; +export type FormatError = ErrorObject<"format", { + format: string; +}, string | { + $data: string; +}>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/format/format.js b/website/node_modules/ajv/dist/vocabularies/format/format.js new file mode 100644 index 00000000..aa667c1e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/format/format.js @@ -0,0 +1,92 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const error = { + message: ({ schemaCode }) => (0, codegen_1.str) `must match format "${schemaCode}"`, + params: ({ schemaCode }) => (0, codegen_1._) `{format: ${schemaCode}}`, +}; +const def = { + keyword: "format", + type: ["number", "string"], + schemaType: "string", + $data: true, + error, + code(cxt, ruleType) { + const { gen, data, $data, schema, schemaCode, it } = cxt; + const { opts, errSchemaPath, schemaEnv, self } = it; + if (!opts.validateFormats) + return; + if ($data) + validate$DataFormat(); + else + validateFormat(); + function validate$DataFormat() { + const fmts = gen.scopeValue("formats", { + ref: self.formats, + code: opts.code.formats, + }); + const fDef = gen.const("fDef", (0, codegen_1._) `${fmts}[${schemaCode}]`); + const fType = gen.let("fType"); + const format = gen.let("format"); + // TODO simplify + gen.if((0, codegen_1._) `typeof ${fDef} == "object" && !(${fDef} instanceof RegExp)`, () => gen.assign(fType, (0, codegen_1._) `${fDef}.type || "string"`).assign(format, (0, codegen_1._) `${fDef}.validate`), () => gen.assign(fType, (0, codegen_1._) `"string"`).assign(format, fDef)); + cxt.fail$data((0, codegen_1.or)(unknownFmt(), invalidFmt())); + function unknownFmt() { + if (opts.strictSchema === false) + return codegen_1.nil; + return (0, codegen_1._) `${schemaCode} && !${format}`; + } + function invalidFmt() { + const callFormat = schemaEnv.$async + ? (0, codegen_1._) `(${fDef}.async ? await ${format}(${data}) : ${format}(${data}))` + : (0, codegen_1._) `${format}(${data})`; + const validData = (0, codegen_1._) `(typeof ${format} == "function" ? ${callFormat} : ${format}.test(${data}))`; + return (0, codegen_1._) `${format} && ${format} !== true && ${fType} === ${ruleType} && !${validData}`; + } + } + function validateFormat() { + const formatDef = self.formats[schema]; + if (!formatDef) { + unknownFormat(); + return; + } + if (formatDef === true) + return; + const [fmtType, format, fmtRef] = getFormat(formatDef); + if (fmtType === ruleType) + cxt.pass(validCondition()); + function unknownFormat() { + if (opts.strictSchema === false) { + self.logger.warn(unknownMsg()); + return; + } + throw new Error(unknownMsg()); + function unknownMsg() { + return `unknown format "${schema}" ignored in schema at path "${errSchemaPath}"`; + } + } + function getFormat(fmtDef) { + const code = fmtDef instanceof RegExp + ? (0, codegen_1.regexpCode)(fmtDef) + : opts.code.formats + ? (0, codegen_1._) `${opts.code.formats}${(0, codegen_1.getProperty)(schema)}` + : undefined; + const fmt = gen.scopeValue("formats", { key: schema, ref: fmtDef, code }); + if (typeof fmtDef == "object" && !(fmtDef instanceof RegExp)) { + return [fmtDef.type || "string", fmtDef.validate, (0, codegen_1._) `${fmt}.validate`]; + } + return ["string", fmtDef, fmt]; + } + function validCondition() { + if (typeof formatDef == "object" && !(formatDef instanceof RegExp) && formatDef.async) { + if (!schemaEnv.$async) + throw new Error("async format in sync schema"); + return (0, codegen_1._) `await ${fmtRef}(${data})`; + } + return typeof format == "function" ? (0, codegen_1._) `${fmtRef}(${data})` : (0, codegen_1._) `${fmtRef}.test(${data})`; + } + } + }, +}; +exports.default = def; +//# sourceMappingURL=format.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/format/format.js.map b/website/node_modules/ajv/dist/vocabularies/format/format.js.map new file mode 100644 index 00000000..897d9ded --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/format/format.js.map @@ -0,0 +1 @@ +{"version":3,"file":"format.js","sourceRoot":"","sources":["../../../lib/vocabularies/format/format.ts"],"names":[],"mappings":";;AASA,mDAAoF;AAapF,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,aAAG,EAAA,sBAAsB,UAAU,GAAG;IACjE,MAAM,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,YAAY,UAAU,GAAG;CACrD,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,QAAQ;IACjB,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC1B,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAE,IAAI;IACX,KAAK;IACL,IAAI,CAAC,GAAe,EAAE,QAAiB;QACrC,MAAM,EAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACtD,MAAM,EAAC,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,EAAC,GAAG,EAAE,CAAA;QACjD,IAAI,CAAC,IAAI,CAAC,eAAe;YAAE,OAAM;QAEjC,IAAI,KAAK;YAAE,mBAAmB,EAAE,CAAA;;YAC3B,cAAc,EAAE,CAAA;QAErB,SAAS,mBAAmB;YAC1B,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,SAAS,EAAE;gBACrC,GAAG,EAAE,IAAI,CAAC,OAAO;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO;aACxB,CAAC,CAAA;YACF,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,IAAA,WAAC,EAAA,GAAG,IAAI,IAAI,UAAU,GAAG,CAAC,CAAA;YACzD,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;YAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YAChC,gBAAgB;YAChB,GAAG,CAAC,EAAE,CACJ,IAAA,WAAC,EAAA,UAAU,IAAI,qBAAqB,IAAI,qBAAqB,EAC7D,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAA,WAAC,EAAA,GAAG,IAAI,mBAAmB,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,IAAA,WAAC,EAAA,GAAG,IAAI,WAAW,CAAC,EACxF,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAA,WAAC,EAAA,UAAU,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAC1D,CAAA;YACD,GAAG,CAAC,SAAS,CAAC,IAAA,YAAE,EAAC,UAAU,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,CAAA;YAE7C,SAAS,UAAU;gBACjB,IAAI,IAAI,CAAC,YAAY,KAAK,KAAK;oBAAE,OAAO,aAAG,CAAA;gBAC3C,OAAO,IAAA,WAAC,EAAA,GAAG,UAAU,QAAQ,MAAM,EAAE,CAAA;YACvC,CAAC;YAED,SAAS,UAAU;gBACjB,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM;oBACjC,CAAC,CAAC,IAAA,WAAC,EAAA,IAAI,IAAI,kBAAkB,MAAM,IAAI,IAAI,OAAO,MAAM,IAAI,IAAI,IAAI;oBACpE,CAAC,CAAC,IAAA,WAAC,EAAA,GAAG,MAAM,IAAI,IAAI,GAAG,CAAA;gBACzB,MAAM,SAAS,GAAG,IAAA,WAAC,EAAA,WAAW,MAAM,oBAAoB,UAAU,MAAM,MAAM,SAAS,IAAI,IAAI,CAAA;gBAC/F,OAAO,IAAA,WAAC,EAAA,GAAG,MAAM,OAAO,MAAM,gBAAgB,KAAK,QAAQ,QAAQ,QAAQ,SAAS,EAAE,CAAA;YACxF,CAAC;QACH,CAAC;QAED,SAAS,cAAc;YACrB,MAAM,SAAS,GAA4B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;YAC/D,IAAI,CAAC,SAAS,EAAE;gBACd,aAAa,EAAE,CAAA;gBACf,OAAM;aACP;YACD,IAAI,SAAS,KAAK,IAAI;gBAAE,OAAM;YAC9B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;YACtD,IAAI,OAAO,KAAK,QAAQ;gBAAE,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAA;YAEpD,SAAS,aAAa;gBACpB,IAAI,IAAI,CAAC,YAAY,KAAK,KAAK,EAAE;oBAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;oBAC9B,OAAM;iBACP;gBACD,MAAM,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC,CAAA;gBAE7B,SAAS,UAAU;oBACjB,OAAO,mBAAmB,MAAgB,gCAAgC,aAAa,GAAG,CAAA;gBAC5F,CAAC;YACH,CAAC;YAED,SAAS,SAAS,CAAC,MAAmB;gBACpC,MAAM,IAAI,GACR,MAAM,YAAY,MAAM;oBACtB,CAAC,CAAC,IAAA,oBAAU,EAAC,MAAM,CAAC;oBACpB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO;wBACnB,CAAC,CAAC,IAAA,WAAC,EAAA,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,IAAA,qBAAW,EAAC,MAAM,CAAC,EAAE;wBAC/C,CAAC,CAAC,SAAS,CAAA;gBACf,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,SAAS,EAAE,EAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAC,CAAC,CAAA;gBACvE,IAAI,OAAO,MAAM,IAAI,QAAQ,IAAI,CAAC,CAAC,MAAM,YAAY,MAAM,CAAC,EAAE;oBAC5D,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAA,WAAC,EAAA,GAAG,GAAG,WAAW,CAAC,CAAA;iBACtE;gBAED,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;YAChC,CAAC;YAED,SAAS,cAAc;gBACrB,IAAI,OAAO,SAAS,IAAI,QAAQ,IAAI,CAAC,CAAC,SAAS,YAAY,MAAM,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE;oBACrF,IAAI,CAAC,SAAS,CAAC,MAAM;wBAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;oBACrE,OAAO,IAAA,WAAC,EAAA,SAAS,MAAM,IAAI,IAAI,GAAG,CAAA;iBACnC;gBACD,OAAO,OAAO,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC,IAAA,WAAC,EAAA,GAAG,MAAM,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAA,WAAC,EAAA,GAAG,MAAM,SAAS,IAAI,GAAG,CAAA;YACzF,CAAC;QACH,CAAC;IACH,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/format/index.d.ts b/website/node_modules/ajv/dist/vocabularies/format/index.d.ts new file mode 100644 index 00000000..c8019c9d --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/format/index.d.ts @@ -0,0 +1,3 @@ +import type { Vocabulary } from "../../types"; +declare const format: Vocabulary; +export default format; diff --git a/website/node_modules/ajv/dist/vocabularies/format/index.js b/website/node_modules/ajv/dist/vocabularies/format/index.js new file mode 100644 index 00000000..d19023d2 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/format/index.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const format_1 = require("./format"); +const format = [format_1.default]; +exports.default = format; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/format/index.js.map b/website/node_modules/ajv/dist/vocabularies/format/index.js.map new file mode 100644 index 00000000..6315bfe1 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/format/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/vocabularies/format/index.ts"],"names":[],"mappings":";;AACA,qCAAoC;AAEpC,MAAM,MAAM,GAAe,CAAC,gBAAa,CAAC,CAAA;AAE1C,kBAAe,MAAM,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/discriminator.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/discriminator.d.ts new file mode 100644 index 00000000..85e16df6 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/discriminator.d.ts @@ -0,0 +1,6 @@ +import type { CodeKeywordDefinition } from "../../types"; +import { _JTDTypeError } from "./error"; +import { DiscrError, DiscrErrorObj } from "../discriminator/types"; +export type JTDDiscriminatorError = _JTDTypeError<"discriminator", "object", string> | DiscrErrorObj | DiscrErrorObj; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/discriminator.js b/website/node_modules/ajv/dist/vocabularies/jtd/discriminator.js new file mode 100644 index 00000000..e7074d27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/discriminator.js @@ -0,0 +1,71 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const metadata_1 = require("./metadata"); +const nullable_1 = require("./nullable"); +const error_1 = require("./error"); +const types_1 = require("../discriminator/types"); +const error = { + message: (cxt) => { + const { schema, params } = cxt; + return params.discrError + ? params.discrError === types_1.DiscrError.Tag + ? `tag "${schema}" must be string` + : `value of tag "${schema}" must be in mapping` + : (0, error_1.typeErrorMessage)(cxt, "object"); + }, + params: (cxt) => { + const { schema, params } = cxt; + return params.discrError + ? (0, codegen_1._) `{error: ${params.discrError}, tag: ${schema}, tagValue: ${params.tag}}` + : (0, error_1.typeErrorParams)(cxt, "object"); + }, +}; +const def = { + keyword: "discriminator", + schemaType: "string", + implements: ["mapping"], + error, + code(cxt) { + (0, metadata_1.checkMetadata)(cxt); + const { gen, data, schema, parentSchema } = cxt; + const [valid, cond] = (0, nullable_1.checkNullableObject)(cxt, data); + gen.if(cond); + validateDiscriminator(); + gen.elseIf((0, codegen_1.not)(valid)); + cxt.error(); + gen.endIf(); + cxt.ok(valid); + function validateDiscriminator() { + const tag = gen.const("tag", (0, codegen_1._) `${data}${(0, codegen_1.getProperty)(schema)}`); + gen.if((0, codegen_1._) `${tag} === undefined`); + cxt.error(false, { discrError: types_1.DiscrError.Tag, tag }); + gen.elseIf((0, codegen_1._) `typeof ${tag} == "string"`); + validateMapping(tag); + gen.else(); + cxt.error(false, { discrError: types_1.DiscrError.Tag, tag }, { instancePath: schema }); + gen.endIf(); + } + function validateMapping(tag) { + gen.if(false); + for (const tagValue in parentSchema.mapping) { + gen.elseIf((0, codegen_1._) `${tag} === ${tagValue}`); + gen.assign(valid, applyTagSchema(tagValue)); + } + gen.else(); + cxt.error(false, { discrError: types_1.DiscrError.Mapping, tag }, { instancePath: schema, schemaPath: "mapping", parentSchema: true }); + gen.endIf(); + } + function applyTagSchema(schemaProp) { + const _valid = gen.name("valid"); + cxt.subschema({ + keyword: "mapping", + schemaProp, + jtdDiscriminator: schema, + }, _valid); + return _valid; + } + }, +}; +exports.default = def; +//# sourceMappingURL=discriminator.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/discriminator.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/discriminator.js.map new file mode 100644 index 00000000..53cb717b --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/discriminator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"discriminator.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/discriminator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/elements.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/elements.d.ts new file mode 100644 index 00000000..82c942c3 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/elements.d.ts @@ -0,0 +1,5 @@ +import type { CodeKeywordDefinition, SchemaObject } from "../../types"; +import { _JTDTypeError } from "./error"; +export type JTDElementsError = _JTDTypeError<"elements", "array", SchemaObject>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/elements.js b/website/node_modules/ajv/dist/vocabularies/jtd/elements.js new file mode 100644 index 00000000..9b8fb548 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/elements.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const util_1 = require("../../compile/util"); +const code_1 = require("../code"); +const codegen_1 = require("../../compile/codegen"); +const metadata_1 = require("./metadata"); +const nullable_1 = require("./nullable"); +const error_1 = require("./error"); +const def = { + keyword: "elements", + schemaType: "object", + error: (0, error_1.typeError)("array"), + code(cxt) { + (0, metadata_1.checkMetadata)(cxt); + const { gen, data, schema, it } = cxt; + if ((0, util_1.alwaysValidSchema)(it, schema)) + return; + const [valid] = (0, nullable_1.checkNullable)(cxt); + gen.if((0, codegen_1.not)(valid), () => gen.if((0, codegen_1._) `Array.isArray(${data})`, () => gen.assign(valid, (0, code_1.validateArray)(cxt)), () => cxt.error())); + cxt.ok(valid); + }, +}; +exports.default = def; +//# sourceMappingURL=elements.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/elements.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/elements.js.map new file mode 100644 index 00000000..38fe3a12 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/elements.js.map @@ -0,0 +1 @@ +{"version":3,"file":"elements.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/elements.ts"],"names":[],"mappings":";;AAEA,6CAAoD;AACpD,kCAAqC;AACrC,mDAA4C;AAC5C,yCAAwC;AACxC,yCAAwC;AACxC,mCAAgD;AAIhD,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,UAAU;IACnB,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAE,IAAA,iBAAS,EAAC,OAAO,CAAC;IACzB,IAAI,CAAC,GAAe;QAClB,IAAA,wBAAa,EAAC,GAAG,CAAC,CAAA;QAClB,MAAM,EAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACnC,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC;YAAE,OAAM;QACzC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAA,wBAAa,EAAC,GAAG,CAAC,CAAA;QAClC,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CACtB,GAAG,CAAC,EAAE,CACJ,IAAA,WAAC,EAAA,iBAAiB,IAAI,GAAG,EACzB,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAA,oBAAa,EAAC,GAAG,CAAC,CAAC,EAC3C,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAClB,CACF,CAAA;QACD,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;IACf,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/enum.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/enum.d.ts new file mode 100644 index 00000000..8ba1790d --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/enum.d.ts @@ -0,0 +1,6 @@ +import type { CodeKeywordDefinition, ErrorObject } from "../../types"; +export type JTDEnumError = ErrorObject<"enum", { + allowedValues: string[]; +}, string[]>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/enum.js b/website/node_modules/ajv/dist/vocabularies/jtd/enum.js new file mode 100644 index 00000000..78b01ee3 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/enum.js @@ -0,0 +1,43 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const metadata_1 = require("./metadata"); +const nullable_1 = require("./nullable"); +const error = { + message: "must be equal to one of the allowed values", + params: ({ schemaCode }) => (0, codegen_1._) `{allowedValues: ${schemaCode}}`, +}; +const def = { + keyword: "enum", + schemaType: "array", + error, + code(cxt) { + (0, metadata_1.checkMetadata)(cxt); + const { gen, data, schema, schemaValue, parentSchema, it } = cxt; + if (schema.length === 0) + throw new Error("enum must have non-empty array"); + if (schema.length !== new Set(schema).size) + throw new Error("enum items must be unique"); + let valid; + const isString = (0, codegen_1._) `typeof ${data} == "string"`; + if (schema.length >= it.opts.loopEnum) { + let cond; + [valid, cond] = (0, nullable_1.checkNullable)(cxt, isString); + gen.if(cond, loopEnum); + } + else { + /* istanbul ignore if */ + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + valid = (0, codegen_1.and)(isString, (0, codegen_1.or)(...schema.map((value) => (0, codegen_1._) `${data} === ${value}`))); + if (parentSchema.nullable) + valid = (0, codegen_1.or)((0, codegen_1._) `${data} === null`, valid); + } + cxt.pass(valid); + function loopEnum() { + gen.forOf("v", schemaValue, (v) => gen.if((0, codegen_1._) `${valid} = ${data} === ${v}`, () => gen.break())); + } + }, +}; +exports.default = def; +//# sourceMappingURL=enum.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/enum.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/enum.js.map new file mode 100644 index 00000000..663cc7c5 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/enum.js.map @@ -0,0 +1 @@ +{"version":3,"file":"enum.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/enum.ts"],"names":[],"mappings":";;AAEA,mDAAsD;AACtD,yCAAwC;AACxC,yCAAwC;AAIxC,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,4CAA4C;IACrD,MAAM,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,mBAAmB,UAAU,GAAG;CAC5D,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,MAAM;IACf,UAAU,EAAE,OAAO;IACnB,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,IAAA,wBAAa,EAAC,GAAG,CAAC,CAAA;QAClB,MAAM,EAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QAC9D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;QAC1E,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;QACxF,IAAI,KAAW,CAAA;QACf,MAAM,QAAQ,GAAG,IAAA,WAAC,EAAA,UAAU,IAAI,cAAc,CAAA;QAC9C,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE;YACrC,IAAI,IAAU,CACb;YAAA,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,IAAA,wBAAa,EAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;YAC7C,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;SACvB;aAAM;YACL,wBAAwB;YACxB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;YACvE,KAAK,GAAG,IAAA,aAAG,EAAC,QAAQ,EAAE,IAAA,YAAE,EAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,GAAG,IAAI,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,CAAA;YACpF,IAAI,YAAY,CAAC,QAAQ;gBAAE,KAAK,GAAG,IAAA,YAAE,EAAC,IAAA,WAAC,EAAA,GAAG,IAAI,WAAW,EAAE,KAAK,CAAC,CAAA;SAClE;QACD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAEf,SAAS,QAAQ;YACf,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,WAAmB,EAAE,CAAC,CAAC,EAAE,EAAE,CACxC,GAAG,CAAC,EAAE,CAAC,IAAA,WAAC,EAAA,GAAG,KAAK,MAAM,IAAI,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAC1D,CAAA;QACH,CAAC;IACH,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/error.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/error.d.ts new file mode 100644 index 00000000..d334ff54 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/error.d.ts @@ -0,0 +1,9 @@ +import type { KeywordErrorDefinition, KeywordErrorCxt, ErrorObject } from "../../types"; +import { Code } from "../../compile/codegen"; +export type _JTDTypeError = ErrorObject; +export declare function typeError(t: string): KeywordErrorDefinition; +export declare function typeErrorMessage({ parentSchema }: KeywordErrorCxt, t: string): string; +export declare function typeErrorParams({ parentSchema }: KeywordErrorCxt, t: string): Code; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/error.js b/website/node_modules/ajv/dist/vocabularies/jtd/error.js new file mode 100644 index 00000000..1a3920a7 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/error.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.typeErrorParams = exports.typeErrorMessage = exports.typeError = void 0; +const codegen_1 = require("../../compile/codegen"); +function typeError(t) { + return { + message: (cxt) => typeErrorMessage(cxt, t), + params: (cxt) => typeErrorParams(cxt, t), + }; +} +exports.typeError = typeError; +function typeErrorMessage({ parentSchema }, t) { + return (parentSchema === null || parentSchema === void 0 ? void 0 : parentSchema.nullable) ? `must be ${t} or null` : `must be ${t}`; +} +exports.typeErrorMessage = typeErrorMessage; +function typeErrorParams({ parentSchema }, t) { + return (0, codegen_1._) `{type: ${t}, nullable: ${!!(parentSchema === null || parentSchema === void 0 ? void 0 : parentSchema.nullable)}}`; +} +exports.typeErrorParams = typeErrorParams; +//# sourceMappingURL=error.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/error.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/error.js.map new file mode 100644 index 00000000..db559678 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/error.js.map @@ -0,0 +1 @@ +{"version":3,"file":"error.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/error.ts"],"names":[],"mappings":";;;AACA,mDAA6C;AAQ7C,SAAgB,SAAS,CAAC,CAAS;IACjC,OAAO;QACL,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,EAAE,CAAC,CAAC;QAC1C,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,CAAC;KACzC,CAAA;AACH,CAAC;AALD,8BAKC;AAED,SAAgB,gBAAgB,CAAC,EAAC,YAAY,EAAkB,EAAE,CAAS;IACzE,OAAO,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,QAAQ,EAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAA;AACzE,CAAC;AAFD,4CAEC;AAED,SAAgB,eAAe,CAAC,EAAC,YAAY,EAAkB,EAAE,CAAS;IACxE,OAAO,IAAA,WAAC,EAAA,UAAU,CAAC,eAAe,CAAC,CAAC,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,QAAQ,CAAA,GAAG,CAAA;AAC/D,CAAC;AAFD,0CAEC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/index.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/index.d.ts new file mode 100644 index 00000000..c56246b7 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/index.d.ts @@ -0,0 +1,10 @@ +import type { Vocabulary } from "../../types"; +import { JTDTypeError } from "./type"; +import { JTDEnumError } from "./enum"; +import { JTDElementsError } from "./elements"; +import { JTDPropertiesError } from "./properties"; +import { JTDDiscriminatorError } from "./discriminator"; +import { JTDValuesError } from "./values"; +declare const jtdVocabulary: Vocabulary; +export default jtdVocabulary; +export type JTDErrorObject = JTDTypeError | JTDEnumError | JTDElementsError | JTDPropertiesError | JTDDiscriminatorError | JTDValuesError; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/index.js b/website/node_modules/ajv/dist/vocabularies/jtd/index.js new file mode 100644 index 00000000..18f40ab7 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/index.js @@ -0,0 +1,29 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const ref_1 = require("./ref"); +const type_1 = require("./type"); +const enum_1 = require("./enum"); +const elements_1 = require("./elements"); +const properties_1 = require("./properties"); +const optionalProperties_1 = require("./optionalProperties"); +const discriminator_1 = require("./discriminator"); +const values_1 = require("./values"); +const union_1 = require("./union"); +const metadata_1 = require("./metadata"); +const jtdVocabulary = [ + "definitions", + ref_1.default, + type_1.default, + enum_1.default, + elements_1.default, + properties_1.default, + optionalProperties_1.default, + discriminator_1.default, + values_1.default, + union_1.default, + metadata_1.default, + { keyword: "additionalProperties", schemaType: "boolean" }, + { keyword: "nullable", schemaType: "boolean" }, +]; +exports.default = jtdVocabulary; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/index.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/index.js.map new file mode 100644 index 00000000..713a1875 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/index.ts"],"names":[],"mappings":";;AACA,+BAA8B;AAC9B,iCAAgD;AAChD,iCAAgD;AAChD,yCAAqD;AACrD,6CAA2D;AAC3D,6DAAqD;AACrD,mDAAoE;AACpE,qCAA+C;AAC/C,mCAA2B;AAC3B,yCAAiC;AAEjC,MAAM,aAAa,GAAe;IAChC,aAAa;IACb,aAAU;IACV,cAAW;IACX,cAAW;IACX,kBAAQ;IACR,oBAAU;IACV,4BAAkB;IAClB,uBAAa;IACb,gBAAM;IACN,eAAK;IACL,kBAAQ;IACR,EAAC,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,SAAS,EAAC;IACxD,EAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAC;CAC7C,CAAA;AAED,kBAAe,aAAa,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/metadata.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/metadata.d.ts new file mode 100644 index 00000000..86e15a8e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/metadata.d.ts @@ -0,0 +1,5 @@ +import { KeywordCxt } from "../../ajv"; +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export declare function checkMetadata({ it, keyword }: KeywordCxt, metadata?: boolean): void; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/metadata.js b/website/node_modules/ajv/dist/vocabularies/jtd/metadata.js new file mode 100644 index 00000000..eeb3c91c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/metadata.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.checkMetadata = void 0; +const util_1 = require("../../compile/util"); +const def = { + keyword: "metadata", + schemaType: "object", + code(cxt) { + checkMetadata(cxt); + const { gen, schema, it } = cxt; + if ((0, util_1.alwaysValidSchema)(it, schema)) + return; + const valid = gen.name("valid"); + cxt.subschema({ keyword: "metadata", jtdMetadata: true }, valid); + cxt.ok(valid); + }, +}; +function checkMetadata({ it, keyword }, metadata) { + if (it.jtdMetadata !== metadata) { + throw new Error(`JTD: "${keyword}" cannot be used in this schema location`); + } +} +exports.checkMetadata = checkMetadata; +exports.default = def; +//# sourceMappingURL=metadata.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/metadata.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/metadata.js.map new file mode 100644 index 00000000..c22e73ad --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/metadata.js.map @@ -0,0 +1 @@ +{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/metadata.ts"],"names":[],"mappings":";;;AAEA,6CAAoD;AAEpD,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,UAAU;IACnB,UAAU,EAAE,QAAQ;IACpB,IAAI,CAAC,GAAe;QAClB,aAAa,CAAC,GAAG,CAAC,CAAA;QAClB,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QAC7B,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC;YAAE,OAAM;QACzC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC/B,GAAG,CAAC,SAAS,CAAC,EAAC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,IAAI,EAAC,EAAE,KAAK,CAAC,CAAA;QAC9D,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;IACf,CAAC;CACF,CAAA;AAED,SAAgB,aAAa,CAAC,EAAC,EAAE,EAAE,OAAO,EAAa,EAAE,QAAkB;IACzE,IAAI,EAAE,CAAC,WAAW,KAAK,QAAQ,EAAE;QAC/B,MAAM,IAAI,KAAK,CAAC,SAAS,OAAO,0CAA0C,CAAC,CAAA;KAC5E;AACH,CAAC;AAJD,sCAIC;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/nullable.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/nullable.d.ts new file mode 100644 index 00000000..254f7602 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/nullable.d.ts @@ -0,0 +1,4 @@ +import type { KeywordCxt } from "../../compile/validate"; +import { Code, Name } from "../../compile/codegen"; +export declare function checkNullable({ gen, data, parentSchema }: KeywordCxt, cond?: Code): [Name, Code]; +export declare function checkNullableObject(cxt: KeywordCxt, cond: Code): [Name, Code]; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/nullable.js b/website/node_modules/ajv/dist/vocabularies/jtd/nullable.js new file mode 100644 index 00000000..8c92d2cd --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/nullable.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.checkNullableObject = exports.checkNullable = void 0; +const codegen_1 = require("../../compile/codegen"); +function checkNullable({ gen, data, parentSchema }, cond = codegen_1.nil) { + const valid = gen.name("valid"); + if (parentSchema.nullable) { + gen.let(valid, (0, codegen_1._) `${data} === null`); + cond = (0, codegen_1.not)(valid); + } + else { + gen.let(valid, false); + } + return [valid, cond]; +} +exports.checkNullable = checkNullable; +function checkNullableObject(cxt, cond) { + const [valid, cond_] = checkNullable(cxt, cond); + return [valid, (0, codegen_1._) `${cond_} && typeof ${cxt.data} == "object" && !Array.isArray(${cxt.data})`]; +} +exports.checkNullableObject = checkNullableObject; +//# sourceMappingURL=nullable.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/nullable.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/nullable.js.map new file mode 100644 index 00000000..5bfaf327 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/nullable.js.map @@ -0,0 +1 @@ +{"version":3,"file":"nullable.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/nullable.ts"],"names":[],"mappings":";;;AACA,mDAA6D;AAE7D,SAAgB,aAAa,CAC3B,EAAC,GAAG,EAAE,IAAI,EAAE,YAAY,EAAa,EACrC,OAAa,aAAG;IAEhB,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC/B,IAAI,YAAY,CAAC,QAAQ,EAAE;QACzB,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,IAAA,WAAC,EAAA,GAAG,IAAI,WAAW,CAAC,CAAA;QACnC,IAAI,GAAG,IAAA,aAAG,EAAC,KAAK,CAAC,CAAA;KAClB;SAAM;QACL,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;KACtB;IACD,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;AACtB,CAAC;AAZD,sCAYC;AAED,SAAgB,mBAAmB,CAAC,GAAe,EAAE,IAAU;IAC7D,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAC/C,OAAO,CAAC,KAAK,EAAE,IAAA,WAAC,EAAA,GAAG,KAAK,cAAc,GAAG,CAAC,IAAI,kCAAkC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAA;AAC9F,CAAC;AAHD,kDAGC"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.js b/website/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.js new file mode 100644 index 00000000..fe272758 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const properties_1 = require("./properties"); +const def = { + keyword: "optionalProperties", + schemaType: "object", + error: properties_1.error, + code(cxt) { + if (cxt.parentSchema.properties) + return; + (0, properties_1.validateProperties)(cxt); + }, +}; +exports.default = def; +//# sourceMappingURL=optionalProperties.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.js.map new file mode 100644 index 00000000..21e5f0d4 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.js.map @@ -0,0 +1 @@ +{"version":3,"file":"optionalProperties.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/optionalProperties.ts"],"names":[],"mappings":";;AAEA,6CAAsD;AAEtD,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,oBAAoB;IAC7B,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAL,kBAAK;IACL,IAAI,CAAC,GAAe;QAClB,IAAI,GAAG,CAAC,YAAY,CAAC,UAAU;YAAE,OAAM;QACvC,IAAA,+BAAkB,EAAC,GAAG,CAAC,CAAA;IACzB,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/properties.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/properties.d.ts new file mode 100644 index 00000000..54e3b347 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/properties.d.ts @@ -0,0 +1,22 @@ +import type { CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition, SchemaObject } from "../../types"; +import type { KeywordCxt } from "../../compile/validate"; +import { _JTDTypeError } from "./error"; +declare enum PropError { + Additional = "additional", + Missing = "missing" +} +type PropKeyword = "properties" | "optionalProperties"; +type PropSchema = { + [P in string]?: SchemaObject; +}; +export type JTDPropertiesError = _JTDTypeError | ErrorObject | ErrorObject; +export declare const error: KeywordErrorDefinition; +declare const def: CodeKeywordDefinition; +export declare function validateProperties(cxt: KeywordCxt): void; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/properties.js b/website/node_modules/ajv/dist/vocabularies/jtd/properties.js new file mode 100644 index 00000000..f4e9de45 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/properties.js @@ -0,0 +1,149 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.validateProperties = exports.error = void 0; +const code_1 = require("../code"); +const util_1 = require("../../compile/util"); +const codegen_1 = require("../../compile/codegen"); +const metadata_1 = require("./metadata"); +const nullable_1 = require("./nullable"); +const error_1 = require("./error"); +var PropError; +(function (PropError) { + PropError["Additional"] = "additional"; + PropError["Missing"] = "missing"; +})(PropError || (PropError = {})); +exports.error = { + message: (cxt) => { + const { params } = cxt; + return params.propError + ? params.propError === PropError.Additional + ? "must NOT have additional properties" + : `must have property '${params.missingProperty}'` + : (0, error_1.typeErrorMessage)(cxt, "object"); + }, + params: (cxt) => { + const { params } = cxt; + return params.propError + ? params.propError === PropError.Additional + ? (0, codegen_1._) `{error: ${params.propError}, additionalProperty: ${params.additionalProperty}}` + : (0, codegen_1._) `{error: ${params.propError}, missingProperty: ${params.missingProperty}}` + : (0, error_1.typeErrorParams)(cxt, "object"); + }, +}; +const def = { + keyword: "properties", + schemaType: "object", + error: exports.error, + code: validateProperties, +}; +// const error: KeywordErrorDefinition = { +// message: "should NOT have additional properties", +// params: ({params}) => _`{additionalProperty: ${params.additionalProperty}}`, +// } +function validateProperties(cxt) { + (0, metadata_1.checkMetadata)(cxt); + const { gen, data, parentSchema, it } = cxt; + const { additionalProperties, nullable } = parentSchema; + if (it.jtdDiscriminator && nullable) + throw new Error("JTD: nullable inside discriminator mapping"); + if (commonProperties()) { + throw new Error("JTD: properties and optionalProperties have common members"); + } + const [allProps, properties] = schemaProperties("properties"); + const [allOptProps, optProperties] = schemaProperties("optionalProperties"); + if (properties.length === 0 && optProperties.length === 0 && additionalProperties) { + return; + } + const [valid, cond] = it.jtdDiscriminator === undefined + ? (0, nullable_1.checkNullableObject)(cxt, data) + : [gen.let("valid", false), true]; + gen.if(cond, () => gen.assign(valid, true).block(() => { + validateProps(properties, "properties", true); + validateProps(optProperties, "optionalProperties"); + if (!additionalProperties) + validateAdditional(); + })); + cxt.pass(valid); + function commonProperties() { + const props = parentSchema.properties; + const optProps = parentSchema.optionalProperties; + if (!(props && optProps)) + return false; + for (const p in props) { + if (Object.prototype.hasOwnProperty.call(optProps, p)) + return true; + } + return false; + } + function schemaProperties(keyword) { + const schema = parentSchema[keyword]; + const allPs = schema ? (0, code_1.allSchemaProperties)(schema) : []; + if (it.jtdDiscriminator && allPs.some((p) => p === it.jtdDiscriminator)) { + throw new Error(`JTD: discriminator tag used in ${keyword}`); + } + const ps = allPs.filter((p) => !(0, util_1.alwaysValidSchema)(it, schema[p])); + return [allPs, ps]; + } + function validateProps(props, keyword, required) { + const _valid = gen.var("valid"); + for (const prop of props) { + gen.if((0, code_1.propertyInData)(gen, data, prop, it.opts.ownProperties), () => applyPropertySchema(prop, keyword, _valid), () => missingProperty(prop)); + cxt.ok(_valid); + } + function missingProperty(prop) { + if (required) { + gen.assign(_valid, false); + cxt.error(false, { propError: PropError.Missing, missingProperty: prop }, { schemaPath: prop }); + } + else { + gen.assign(_valid, true); + } + } + } + function applyPropertySchema(prop, keyword, _valid) { + cxt.subschema({ + keyword, + schemaProp: prop, + dataProp: prop, + }, _valid); + } + function validateAdditional() { + gen.forIn("key", data, (key) => { + const addProp = isAdditional(key, allProps, "properties", it.jtdDiscriminator); + const addOptProp = isAdditional(key, allOptProps, "optionalProperties"); + const extra = addProp === true ? addOptProp : addOptProp === true ? addProp : (0, codegen_1.and)(addProp, addOptProp); + gen.if(extra, () => { + if (it.opts.removeAdditional) { + gen.code((0, codegen_1._) `delete ${data}[${key}]`); + } + else { + cxt.error(false, { propError: PropError.Additional, additionalProperty: key }, { instancePath: key, parentSchema: true }); + if (!it.opts.allErrors) + gen.break(); + } + }); + }); + } + function isAdditional(key, props, keyword, jtdDiscriminator) { + let additional; + if (props.length > 8) { + // TODO maybe an option instead of hard-coded 8? + const propsSchema = (0, util_1.schemaRefOrVal)(it, parentSchema[keyword], keyword); + additional = (0, codegen_1.not)((0, code_1.isOwnProperty)(gen, propsSchema, key)); + if (jtdDiscriminator !== undefined) { + additional = (0, codegen_1.and)(additional, (0, codegen_1._) `${key} !== ${jtdDiscriminator}`); + } + } + else if (props.length || jtdDiscriminator !== undefined) { + const ps = jtdDiscriminator === undefined ? props : [jtdDiscriminator].concat(props); + additional = (0, codegen_1.and)(...ps.map((p) => (0, codegen_1._) `${key} !== ${p}`)); + } + else { + additional = true; + } + return additional; + } +} +exports.validateProperties = validateProperties; +exports.default = def; +//# sourceMappingURL=properties.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/properties.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/properties.js.map new file mode 100644 index 00000000..03997a3d --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/properties.js.map @@ -0,0 +1 @@ +{"version":3,"file":"properties.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/properties.ts"],"names":[],"mappings":";;;AAOA,kCAA0E;AAC1E,6CAAoE;AACpE,mDAA6D;AAC7D,yCAAwC;AACxC,yCAA8C;AAC9C,mCAAwE;AAExE,IAAK,SAGJ;AAHD,WAAK,SAAS;IACZ,sCAAyB,CAAA;IACzB,gCAAmB,CAAA;AACrB,CAAC,EAHI,SAAS,KAAT,SAAS,QAGb;AAWY,QAAA,KAAK,GAA2B;IAC3C,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACf,MAAM,EAAC,MAAM,EAAC,GAAG,GAAG,CAAA;QACpB,OAAO,MAAM,CAAC,SAAS;YACrB,CAAC,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,UAAU;gBACzC,CAAC,CAAC,qCAAqC;gBACvC,CAAC,CAAC,uBAAuB,MAAM,CAAC,eAAe,GAAG;YACpD,CAAC,CAAC,IAAA,wBAAgB,EAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IACrC,CAAC;IACD,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE;QACd,MAAM,EAAC,MAAM,EAAC,GAAG,GAAG,CAAA;QACpB,OAAO,MAAM,CAAC,SAAS;YACrB,CAAC,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,UAAU;gBACzC,CAAC,CAAC,IAAA,WAAC,EAAA,WAAW,MAAM,CAAC,SAAS,yBAAyB,MAAM,CAAC,kBAAkB,GAAG;gBACnF,CAAC,CAAC,IAAA,WAAC,EAAA,WAAW,MAAM,CAAC,SAAS,sBAAsB,MAAM,CAAC,eAAe,GAAG;YAC/E,CAAC,CAAC,IAAA,uBAAe,EAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IACpC,CAAC;CACF,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,YAAY;IACrB,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAL,aAAK;IACL,IAAI,EAAE,kBAAkB;CACzB,CAAA;AAED,0CAA0C;AAC1C,sDAAsD;AACtD,iFAAiF;AACjF,IAAI;AAEJ,SAAgB,kBAAkB,CAAC,GAAe;IAChD,IAAA,wBAAa,EAAC,GAAG,CAAC,CAAA;IAClB,MAAM,EAAC,GAAG,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;IACzC,MAAM,EAAC,oBAAoB,EAAE,QAAQ,EAAC,GAAG,YAAY,CAAA;IACrD,IAAI,EAAE,CAAC,gBAAgB,IAAI,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IAClG,IAAI,gBAAgB,EAAE,EAAE;QACtB,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAA;KAC9E;IACD,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAC7D,MAAM,CAAC,WAAW,EAAE,aAAa,CAAC,GAAG,gBAAgB,CAAC,oBAAoB,CAAC,CAAA;IAC3E,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,IAAI,oBAAoB,EAAE;QACjF,OAAM;KACP;IAED,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GACjB,EAAE,CAAC,gBAAgB,KAAK,SAAS;QAC/B,CAAC,CAAC,IAAA,8BAAmB,EAAC,GAAG,EAAE,IAAI,CAAC;QAChC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,CAAA;IACrC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,CAChB,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;QACjC,aAAa,CAAC,UAAU,EAAE,YAAY,EAAE,IAAI,CAAC,CAAA;QAC7C,aAAa,CAAC,aAAa,EAAE,oBAAoB,CAAC,CAAA;QAClD,IAAI,CAAC,oBAAoB;YAAE,kBAAkB,EAAE,CAAA;IACjD,CAAC,CAAC,CACH,CAAA;IACD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAEf,SAAS,gBAAgB;QACvB,MAAM,KAAK,GAAG,YAAY,CAAC,UAA6C,CAAA;QACxE,MAAM,QAAQ,GAAG,YAAY,CAAC,kBAAqD,CAAA;QACnF,IAAI,CAAC,CAAC,KAAK,IAAI,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAA;QACtC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE;YACrB,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAA;SACnE;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,SAAS,gBAAgB,CAAC,OAAe;QACvC,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;QACpC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAA,0BAAmB,EAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACvD,IAAI,EAAE,CAAC,gBAAgB,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,gBAAgB,CAAC,EAAE;YACvE,MAAM,IAAI,KAAK,CAAC,kCAAkC,OAAO,EAAE,CAAC,CAAA;SAC7D;QACD,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACjE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACpB,CAAC;IAED,SAAS,aAAa,CAAC,KAAe,EAAE,OAAe,EAAE,QAAkB;QACzE,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;YACxB,GAAG,CAAC,EAAE,CACJ,IAAA,qBAAc,EAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,EACtD,GAAG,EAAE,CAAC,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAChD,GAAG,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAC5B,CAAA;YACD,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,CAAA;SACf;QAED,SAAS,eAAe,CAAC,IAAY;YACnC,IAAI,QAAQ,EAAE;gBACZ,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;gBACzB,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,EAAC,SAAS,EAAE,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAC,EAAE,EAAC,UAAU,EAAE,IAAI,EAAC,CAAC,CAAA;aAC5F;iBAAM;gBACL,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;aACzB;QACH,CAAC;IACH,CAAC;IAED,SAAS,mBAAmB,CAAC,IAAY,EAAE,OAAe,EAAE,MAAY;QACtE,GAAG,CAAC,SAAS,CACX;YACE,OAAO;YACP,UAAU,EAAE,IAAI;YAChB,QAAQ,EAAE,IAAI;SACf,EACD,MAAM,CACP,CAAA;IACH,CAAC;IAED,SAAS,kBAAkB;QACzB,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,GAAS,EAAE,EAAE;YACnC,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAA;YAC9E,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,EAAE,WAAW,EAAE,oBAAoB,CAAC,CAAA;YACvE,MAAM,KAAK,GACT,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAA,aAAG,EAAC,OAAO,EAAE,UAAU,CAAC,CAAA;YAC1F,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,IAAI,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE;oBAC5B,GAAG,CAAC,IAAI,CAAC,IAAA,WAAC,EAAA,UAAU,IAAI,IAAI,GAAG,GAAG,CAAC,CAAA;iBACpC;qBAAM;oBACL,GAAG,CAAC,KAAK,CACP,KAAK,EACL,EAAC,SAAS,EAAE,SAAS,CAAC,UAAU,EAAE,kBAAkB,EAAE,GAAG,EAAC,EAC1D,EAAC,YAAY,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAC,CACxC,CAAA;oBACD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS;wBAAE,GAAG,CAAC,KAAK,EAAE,CAAA;iBACpC;YACH,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,SAAS,YAAY,CACnB,GAAS,EACT,KAAe,EACf,OAAe,EACf,gBAAyB;QAEzB,IAAI,UAA0B,CAAA;QAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;YACpB,gDAAgD;YAChD,MAAM,WAAW,GAAG,IAAA,qBAAc,EAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAA;YACtE,UAAU,GAAG,IAAA,aAAG,EAAC,IAAA,oBAAa,EAAC,GAAG,EAAE,WAAmB,EAAE,GAAG,CAAC,CAAC,CAAA;YAC9D,IAAI,gBAAgB,KAAK,SAAS,EAAE;gBAClC,UAAU,GAAG,IAAA,aAAG,EAAC,UAAU,EAAE,IAAA,WAAC,EAAA,GAAG,GAAG,QAAQ,gBAAgB,EAAE,CAAC,CAAA;aAChE;SACF;aAAM,IAAI,KAAK,CAAC,MAAM,IAAI,gBAAgB,KAAK,SAAS,EAAE;YACzD,MAAM,EAAE,GAAG,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACpF,UAAU,GAAG,IAAA,aAAG,EAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;SACvD;aAAM;YACL,UAAU,GAAG,IAAI,CAAA;SAClB;QACD,OAAO,UAAU,CAAA;IACnB,CAAC;AACH,CAAC;AA1HD,gDA0HC;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/ref.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/ref.d.ts new file mode 100644 index 00000000..ccdf84c0 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/ref.d.ts @@ -0,0 +1,4 @@ +import type { CodeKeywordDefinition, AnySchemaObject } from "../../types"; +declare const def: CodeKeywordDefinition; +export declare function hasRef(schema: AnySchemaObject): boolean; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/ref.js b/website/node_modules/ajv/dist/vocabularies/jtd/ref.js new file mode 100644 index 00000000..b7c6b02d --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/ref.js @@ -0,0 +1,67 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.hasRef = void 0; +const compile_1 = require("../../compile"); +const codegen_1 = require("../../compile/codegen"); +const ref_error_1 = require("../../compile/ref_error"); +const names_1 = require("../../compile/names"); +const ref_1 = require("../core/ref"); +const metadata_1 = require("./metadata"); +const def = { + keyword: "ref", + schemaType: "string", + code(cxt) { + (0, metadata_1.checkMetadata)(cxt); + const { gen, data, schema: ref, parentSchema, it } = cxt; + const { schemaEnv: { root }, } = it; + const valid = gen.name("valid"); + if (parentSchema.nullable) { + gen.var(valid, (0, codegen_1._) `${data} === null`); + gen.if((0, codegen_1.not)(valid), validateJtdRef); + } + else { + gen.var(valid, false); + validateJtdRef(); + } + cxt.ok(valid); + function validateJtdRef() { + var _a; + const refSchema = (_a = root.schema.definitions) === null || _a === void 0 ? void 0 : _a[ref]; + if (!refSchema) { + throw new ref_error_1.default(it.opts.uriResolver, "", ref, `No definition ${ref}`); + } + if (hasRef(refSchema) || !it.opts.inlineRefs) + callValidate(refSchema); + else + inlineRefSchema(refSchema); + } + function callValidate(schema) { + const sch = compile_1.compileSchema.call(it.self, new compile_1.SchemaEnv({ schema, root, schemaPath: `/definitions/${ref}` })); + const v = (0, ref_1.getValidate)(cxt, sch); + const errsCount = gen.const("_errs", names_1.default.errors); + (0, ref_1.callRef)(cxt, v, sch, sch.$async); + gen.assign(valid, (0, codegen_1._) `${errsCount} === ${names_1.default.errors}`); + } + function inlineRefSchema(schema) { + const schName = gen.scopeValue("schema", it.opts.code.source === true ? { ref: schema, code: (0, codegen_1.stringify)(schema) } : { ref: schema }); + cxt.subschema({ + schema, + dataTypes: [], + schemaPath: codegen_1.nil, + topSchemaRef: schName, + errSchemaPath: `/definitions/${ref}`, + }, valid); + } + }, +}; +function hasRef(schema) { + for (const key in schema) { + let sch; + if (key === "ref" || (typeof (sch = schema[key]) == "object" && hasRef(sch))) + return true; + } + return false; +} +exports.hasRef = hasRef; +exports.default = def; +//# sourceMappingURL=ref.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/ref.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/ref.js.map new file mode 100644 index 00000000..c11c45c1 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/ref.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ref.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/ref.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/type.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/type.d.ts new file mode 100644 index 00000000..a115c7da --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/type.d.ts @@ -0,0 +1,10 @@ +import type { CodeKeywordDefinition } from "../../types"; +import { _JTDTypeError } from "./error"; +export type JTDTypeError = _JTDTypeError<"type", JTDType, JTDType>; +export type IntType = "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32"; +export declare const intRange: { + [T in IntType]: [number, number, number]; +}; +export type JTDType = "boolean" | "string" | "timestamp" | "float32" | "float64" | IntType; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/type.js b/website/node_modules/ajv/dist/vocabularies/jtd/type.js new file mode 100644 index 00000000..17a0b510 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/type.js @@ -0,0 +1,69 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.intRange = void 0; +const codegen_1 = require("../../compile/codegen"); +const timestamp_1 = require("../../runtime/timestamp"); +const util_1 = require("../../compile/util"); +const metadata_1 = require("./metadata"); +const error_1 = require("./error"); +exports.intRange = { + int8: [-128, 127, 3], + uint8: [0, 255, 3], + int16: [-32768, 32767, 5], + uint16: [0, 65535, 5], + int32: [-2147483648, 2147483647, 10], + uint32: [0, 4294967295, 10], +}; +const error = { + message: (cxt) => (0, error_1.typeErrorMessage)(cxt, cxt.schema), + params: (cxt) => (0, error_1.typeErrorParams)(cxt, cxt.schema), +}; +function timestampCode(cxt) { + const { gen, data, it } = cxt; + const { timestamp, allowDate } = it.opts; + if (timestamp === "date") + return (0, codegen_1._) `${data} instanceof Date `; + const vts = (0, util_1.useFunc)(gen, timestamp_1.default); + const allowDateArg = allowDate ? (0, codegen_1._) `, true` : codegen_1.nil; + const validString = (0, codegen_1._) `typeof ${data} == "string" && ${vts}(${data}${allowDateArg})`; + return timestamp === "string" ? validString : (0, codegen_1.or)((0, codegen_1._) `${data} instanceof Date`, validString); +} +const def = { + keyword: "type", + schemaType: "string", + error, + code(cxt) { + (0, metadata_1.checkMetadata)(cxt); + const { data, schema, parentSchema, it } = cxt; + let cond; + switch (schema) { + case "boolean": + case "string": + cond = (0, codegen_1._) `typeof ${data} == ${schema}`; + break; + case "timestamp": { + cond = timestampCode(cxt); + break; + } + case "float32": + case "float64": + cond = (0, codegen_1._) `typeof ${data} == "number"`; + break; + default: { + const sch = schema; + cond = (0, codegen_1._) `typeof ${data} == "number" && isFinite(${data}) && !(${data} % 1)`; + if (!it.opts.int32range && (sch === "int32" || sch === "uint32")) { + if (sch === "uint32") + cond = (0, codegen_1._) `${cond} && ${data} >= 0`; + } + else { + const [min, max] = exports.intRange[sch]; + cond = (0, codegen_1._) `${cond} && ${data} >= ${min} && ${data} <= ${max}`; + } + } + } + cxt.pass(parentSchema.nullable ? (0, codegen_1.or)((0, codegen_1._) `${data} === null`, cond) : cond); + }, +}; +exports.default = def; +//# sourceMappingURL=type.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/type.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/type.js.map new file mode 100644 index 00000000..9d339390 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/type.js.map @@ -0,0 +1 @@ +{"version":3,"file":"type.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/type.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/union.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/union.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/union.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/union.js b/website/node_modules/ajv/dist/vocabularies/jtd/union.js new file mode 100644 index 00000000..01a94399 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/union.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const code_1 = require("../code"); +const def = { + keyword: "union", + schemaType: "array", + trackErrors: true, + code: code_1.validateUnion, + error: { message: "must match a schema in union" }, +}; +exports.default = def; +//# sourceMappingURL=union.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/union.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/union.js.map new file mode 100644 index 00000000..4d6eb411 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/union.js.map @@ -0,0 +1 @@ +{"version":3,"file":"union.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/union.ts"],"names":[],"mappings":";;AACA,kCAAqC;AAErC,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,OAAO;IAChB,UAAU,EAAE,OAAO;IACnB,WAAW,EAAE,IAAI;IACjB,IAAI,EAAE,oBAAa;IACnB,KAAK,EAAE,EAAC,OAAO,EAAE,8BAA8B,EAAC;CACjD,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/values.d.ts b/website/node_modules/ajv/dist/vocabularies/jtd/values.d.ts new file mode 100644 index 00000000..1eaa884a --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/values.d.ts @@ -0,0 +1,5 @@ +import type { CodeKeywordDefinition, SchemaObject } from "../../types"; +import { _JTDTypeError } from "./error"; +export type JTDValuesError = _JTDTypeError<"values", "object", SchemaObject>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/values.js b/website/node_modules/ajv/dist/vocabularies/jtd/values.js new file mode 100644 index 00000000..3c2c95f5 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/values.js @@ -0,0 +1,51 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const util_1 = require("../../compile/util"); +const codegen_1 = require("../../compile/codegen"); +const metadata_1 = require("./metadata"); +const nullable_1 = require("./nullable"); +const error_1 = require("./error"); +const def = { + keyword: "values", + schemaType: "object", + error: (0, error_1.typeError)("object"), + code(cxt) { + (0, metadata_1.checkMetadata)(cxt); + const { gen, data, schema, it } = cxt; + const [valid, cond] = (0, nullable_1.checkNullableObject)(cxt, data); + if ((0, util_1.alwaysValidSchema)(it, schema)) { + gen.if((0, codegen_1.not)((0, codegen_1.or)(cond, valid)), () => cxt.error()); + } + else { + gen.if(cond); + gen.assign(valid, validateMap()); + gen.elseIf((0, codegen_1.not)(valid)); + cxt.error(); + gen.endIf(); + } + cxt.ok(valid); + function validateMap() { + const _valid = gen.name("valid"); + if (it.allErrors) { + const validMap = gen.let("valid", true); + validateValues(() => gen.assign(validMap, false)); + return validMap; + } + gen.var(_valid, true); + validateValues(() => gen.break()); + return _valid; + function validateValues(notValid) { + gen.forIn("key", data, (key) => { + cxt.subschema({ + keyword: "values", + dataProp: key, + dataPropType: util_1.Type.Str, + }, _valid); + gen.if((0, codegen_1.not)(_valid), notValid); + }); + } + } + }, +}; +exports.default = def; +//# sourceMappingURL=values.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/jtd/values.js.map b/website/node_modules/ajv/dist/vocabularies/jtd/values.js.map new file mode 100644 index 00000000..790a736d --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/jtd/values.js.map @@ -0,0 +1 @@ +{"version":3,"file":"values.js","sourceRoot":"","sources":["../../../lib/vocabularies/jtd/values.ts"],"names":[],"mappings":";;AAEA,6CAA0D;AAC1D,mDAAmD;AACnD,yCAAwC;AACxC,yCAA8C;AAC9C,mCAAgD;AAIhD,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,QAAQ;IACjB,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAE,IAAA,iBAAS,EAAC,QAAQ,CAAC;IAC1B,IAAI,CAAC,GAAe;QAClB,IAAA,wBAAa,EAAC,GAAG,CAAC,CAAA;QAClB,MAAM,EAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACnC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,IAAA,8BAAmB,EAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QACpD,IAAI,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC,EAAE;YACjC,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,IAAA,YAAE,EAAC,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAA;SAChD;aAAM;YACL,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;YACZ,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,CAAA;YAChC,GAAG,CAAC,MAAM,CAAC,IAAA,aAAG,EAAC,KAAK,CAAC,CAAC,CAAA;YACtB,GAAG,CAAC,KAAK,EAAE,CAAA;YACX,GAAG,CAAC,KAAK,EAAE,CAAA;SACZ;QACD,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;QAEb,SAAS,WAAW;YAClB,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YAChC,IAAI,EAAE,CAAC,SAAS,EAAE;gBAChB,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;gBACvC,cAAc,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAA;gBACjD,OAAO,QAAQ,CAAA;aAChB;YACD,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;YACrB,cAAc,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAA;YACjC,OAAO,MAAM,CAAA;YAEb,SAAS,cAAc,CAAC,QAAoB;gBAC1C,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE;oBAC7B,GAAG,CAAC,SAAS,CACX;wBACE,OAAO,EAAE,QAAQ;wBACjB,QAAQ,EAAE,GAAG;wBACb,YAAY,EAAE,WAAI,CAAC,GAAG;qBACvB,EACD,MAAM,CACP,CAAA;oBACD,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAA;gBAC/B,CAAC,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;IACH,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/metadata.d.ts b/website/node_modules/ajv/dist/vocabularies/metadata.d.ts new file mode 100644 index 00000000..df9cc07c --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/metadata.d.ts @@ -0,0 +1,3 @@ +import type { Vocabulary } from "../types"; +export declare const metadataVocabulary: Vocabulary; +export declare const contentVocabulary: Vocabulary; diff --git a/website/node_modules/ajv/dist/vocabularies/metadata.js b/website/node_modules/ajv/dist/vocabularies/metadata.js new file mode 100644 index 00000000..f07bf28b --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/metadata.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.contentVocabulary = exports.metadataVocabulary = void 0; +exports.metadataVocabulary = [ + "title", + "description", + "default", + "deprecated", + "readOnly", + "writeOnly", + "examples", +]; +exports.contentVocabulary = [ + "contentMediaType", + "contentEncoding", + "contentSchema", +]; +//# sourceMappingURL=metadata.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/metadata.js.map b/website/node_modules/ajv/dist/vocabularies/metadata.js.map new file mode 100644 index 00000000..0d61f083 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/metadata.js.map @@ -0,0 +1 @@ +{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../lib/vocabularies/metadata.ts"],"names":[],"mappings":";;;AAEa,QAAA,kBAAkB,GAAe;IAC5C,OAAO;IACP,aAAa;IACb,SAAS;IACT,YAAY;IACZ,UAAU;IACV,WAAW;IACX,UAAU;CACX,CAAA;AAEY,QAAA,iBAAiB,GAAe;IAC3C,kBAAkB;IAClB,iBAAiB;IACjB,eAAe;CAChB,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/next.d.ts b/website/node_modules/ajv/dist/vocabularies/next.d.ts new file mode 100644 index 00000000..7fd5c644 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/next.d.ts @@ -0,0 +1,3 @@ +import type { Vocabulary } from "../types"; +declare const next: Vocabulary; +export default next; diff --git a/website/node_modules/ajv/dist/vocabularies/next.js b/website/node_modules/ajv/dist/vocabularies/next.js new file mode 100644 index 00000000..c861b324 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/next.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const dependentRequired_1 = require("./validation/dependentRequired"); +const dependentSchemas_1 = require("./applicator/dependentSchemas"); +const limitContains_1 = require("./validation/limitContains"); +const next = [dependentRequired_1.default, dependentSchemas_1.default, limitContains_1.default]; +exports.default = next; +//# sourceMappingURL=next.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/next.js.map b/website/node_modules/ajv/dist/vocabularies/next.js.map new file mode 100644 index 00000000..474a6d4e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/next.js.map @@ -0,0 +1 @@ +{"version":3,"file":"next.js","sourceRoot":"","sources":["../../lib/vocabularies/next.ts"],"names":[],"mappings":";;AACA,sEAA8D;AAC9D,oEAA4D;AAC5D,8DAAsD;AAEtD,MAAM,IAAI,GAAe,CAAC,2BAAiB,EAAE,0BAAgB,EAAE,uBAAa,CAAC,CAAA;AAE7E,kBAAe,IAAI,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/unevaluated/index.d.ts b/website/node_modules/ajv/dist/vocabularies/unevaluated/index.d.ts new file mode 100644 index 00000000..ec67e63e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/unevaluated/index.d.ts @@ -0,0 +1,3 @@ +import type { Vocabulary } from "../../types"; +declare const unevaluated: Vocabulary; +export default unevaluated; diff --git a/website/node_modules/ajv/dist/vocabularies/unevaluated/index.js b/website/node_modules/ajv/dist/vocabularies/unevaluated/index.js new file mode 100644 index 00000000..30e31674 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/unevaluated/index.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const unevaluatedProperties_1 = require("./unevaluatedProperties"); +const unevaluatedItems_1 = require("./unevaluatedItems"); +const unevaluated = [unevaluatedProperties_1.default, unevaluatedItems_1.default]; +exports.default = unevaluated; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/unevaluated/index.js.map b/website/node_modules/ajv/dist/vocabularies/unevaluated/index.js.map new file mode 100644 index 00000000..a4872ea4 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/unevaluated/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/vocabularies/unevaluated/index.ts"],"names":[],"mappings":";;AACA,mEAA2D;AAC3D,yDAAiD;AAEjD,MAAM,WAAW,GAAe,CAAC,+BAAqB,EAAE,0BAAgB,CAAC,CAAA;AAEzE,kBAAe,WAAW,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.d.ts b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.d.ts new file mode 100644 index 00000000..06f02392 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.d.ts @@ -0,0 +1,6 @@ +import type { CodeKeywordDefinition, ErrorObject, AnySchema } from "../../types"; +export type UnevaluatedItemsError = ErrorObject<"unevaluatedItems", { + limit: number; +}, AnySchema>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.js b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.js new file mode 100644 index 00000000..0a0cd3aa --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.js @@ -0,0 +1,40 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const error = { + message: ({ params: { len } }) => (0, codegen_1.str) `must NOT have more than ${len} items`, + params: ({ params: { len } }) => (0, codegen_1._) `{limit: ${len}}`, +}; +const def = { + keyword: "unevaluatedItems", + type: "array", + schemaType: ["boolean", "object"], + error, + code(cxt) { + const { gen, schema, data, it } = cxt; + const items = it.items || 0; + if (items === true) + return; + const len = gen.const("len", (0, codegen_1._) `${data}.length`); + if (schema === false) { + cxt.setParams({ len: items }); + cxt.fail((0, codegen_1._) `${len} > ${items}`); + } + else if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it, schema)) { + const valid = gen.var("valid", (0, codegen_1._) `${len} <= ${items}`); + gen.if((0, codegen_1.not)(valid), () => validateItems(valid, items)); + cxt.ok(valid); + } + it.items = true; + function validateItems(valid, from) { + gen.forRange("i", from, len, (i) => { + cxt.subschema({ keyword: "unevaluatedItems", dataProp: i, dataPropType: util_1.Type.Num }, valid); + if (!it.allErrors) + gen.if((0, codegen_1.not)(valid), () => gen.break()); + }); + } + }, +}; +exports.default = def; +//# sourceMappingURL=unevaluatedItems.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.js.map b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.js.map new file mode 100644 index 00000000..a8268f03 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.js.map @@ -0,0 +1 @@ +{"version":3,"file":"unevaluatedItems.js","sourceRoot":"","sources":["../../../lib/vocabularies/unevaluated/unevaluatedItems.ts"],"names":[],"mappings":";;AAOA,mDAAuD;AACvD,6CAA0D;AAI1D,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,EAAC,GAAG,EAAC,EAAC,EAAE,EAAE,CAAC,IAAA,aAAG,EAAA,2BAA2B,GAAG,QAAQ;IACvE,MAAM,EAAE,CAAC,EAAC,MAAM,EAAE,EAAC,GAAG,EAAC,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,WAAW,GAAG,GAAG;CAChD,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,kBAAkB;IAC3B,IAAI,EAAE,OAAO;IACb,UAAU,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC;IACjC,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACnC,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,CAAA;QAC3B,IAAI,KAAK,KAAK,IAAI;YAAE,OAAM;QAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,IAAA,WAAC,EAAA,GAAG,IAAI,SAAS,CAAC,CAAA;QAC/C,IAAI,MAAM,KAAK,KAAK,EAAE;YACpB,GAAG,CAAC,SAAS,CAAC,EAAC,GAAG,EAAE,KAAK,EAAC,CAAC,CAAA;YAC3B,GAAG,CAAC,IAAI,CAAC,IAAA,WAAC,EAAA,GAAG,GAAG,MAAM,KAAK,EAAE,CAAC,CAAA;SAC/B;aAAM,IAAI,OAAO,MAAM,IAAI,QAAQ,IAAI,CAAC,IAAA,wBAAiB,EAAC,EAAE,EAAE,MAAM,CAAC,EAAE;YACtE,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,IAAA,WAAC,EAAA,GAAG,GAAG,OAAO,KAAK,EAAE,CAAC,CAAA;YACrD,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;YACrD,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;SACd;QACD,EAAE,CAAC,KAAK,GAAG,IAAI,CAAA;QAEf,SAAS,aAAa,CAAC,KAAW,EAAE,IAAmB;YACrD,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE;gBACjC,GAAG,CAAC,SAAS,CAAC,EAAC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,CAAC,EAAE,YAAY,EAAE,WAAI,CAAC,GAAG,EAAC,EAAE,KAAK,CAAC,CAAA;gBACxF,IAAI,CAAC,EAAE,CAAC,SAAS;oBAAE,GAAG,CAAC,EAAE,CAAC,IAAA,aAAG,EAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAA;YAC1D,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.d.ts b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.d.ts new file mode 100644 index 00000000..ba63f62e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.d.ts @@ -0,0 +1,6 @@ +import type { CodeKeywordDefinition, ErrorObject, AnySchema } from "../../types"; +export type UnevaluatedPropertiesError = ErrorObject<"unevaluatedProperties", { + unevaluatedProperty: string; +}, AnySchema>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.js b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.js new file mode 100644 index 00000000..ad298499 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.js @@ -0,0 +1,65 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const names_1 = require("../../compile/names"); +const error = { + message: "must NOT have unevaluated properties", + params: ({ params }) => (0, codegen_1._) `{unevaluatedProperty: ${params.unevaluatedProperty}}`, +}; +const def = { + keyword: "unevaluatedProperties", + type: "object", + schemaType: ["boolean", "object"], + trackErrors: true, + error, + code(cxt) { + const { gen, schema, data, errsCount, it } = cxt; + /* istanbul ignore if */ + if (!errsCount) + throw new Error("ajv implementation error"); + const { allErrors, props } = it; + if (props instanceof codegen_1.Name) { + gen.if((0, codegen_1._) `${props} !== true`, () => gen.forIn("key", data, (key) => gen.if(unevaluatedDynamic(props, key), () => unevaluatedPropCode(key)))); + } + else if (props !== true) { + gen.forIn("key", data, (key) => props === undefined + ? unevaluatedPropCode(key) + : gen.if(unevaluatedStatic(props, key), () => unevaluatedPropCode(key))); + } + it.props = true; + cxt.ok((0, codegen_1._) `${errsCount} === ${names_1.default.errors}`); + function unevaluatedPropCode(key) { + if (schema === false) { + cxt.setParams({ unevaluatedProperty: key }); + cxt.error(); + if (!allErrors) + gen.break(); + return; + } + if (!(0, util_1.alwaysValidSchema)(it, schema)) { + const valid = gen.name("valid"); + cxt.subschema({ + keyword: "unevaluatedProperties", + dataProp: key, + dataPropType: util_1.Type.Str, + }, valid); + if (!allErrors) + gen.if((0, codegen_1.not)(valid), () => gen.break()); + } + } + function unevaluatedDynamic(evaluatedProps, key) { + return (0, codegen_1._) `!${evaluatedProps} || !${evaluatedProps}[${key}]`; + } + function unevaluatedStatic(evaluatedProps, key) { + const ps = []; + for (const p in evaluatedProps) { + if (evaluatedProps[p] === true) + ps.push((0, codegen_1._) `${key} !== ${p}`); + } + return (0, codegen_1.and)(...ps); + } + }, +}; +exports.default = def; +//# sourceMappingURL=unevaluatedProperties.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.js.map b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.js.map new file mode 100644 index 00000000..f415251e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.js.map @@ -0,0 +1 @@ +{"version":3,"file":"unevaluatedProperties.js","sourceRoot":"","sources":["../../../lib/vocabularies/unevaluated/unevaluatedProperties.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/const.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/const.d.ts new file mode 100644 index 00000000..af91a900 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/const.d.ts @@ -0,0 +1,6 @@ +import type { CodeKeywordDefinition, ErrorObject } from "../../types"; +export type ConstError = ErrorObject<"const", { + allowedValue: any; +}>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/const.js b/website/node_modules/ajv/dist/vocabularies/validation/const.js new file mode 100644 index 00000000..9564496a --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/const.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const equal_1 = require("../../runtime/equal"); +const error = { + message: "must be equal to constant", + params: ({ schemaCode }) => (0, codegen_1._) `{allowedValue: ${schemaCode}}`, +}; +const def = { + keyword: "const", + $data: true, + error, + code(cxt) { + const { gen, data, $data, schemaCode, schema } = cxt; + if ($data || (schema && typeof schema == "object")) { + cxt.fail$data((0, codegen_1._) `!${(0, util_1.useFunc)(gen, equal_1.default)}(${data}, ${schemaCode})`); + } + else { + cxt.fail((0, codegen_1._) `${schema} !== ${data}`); + } + }, +}; +exports.default = def; +//# sourceMappingURL=const.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/const.js.map b/website/node_modules/ajv/dist/vocabularies/validation/const.js.map new file mode 100644 index 00000000..0cf6cd14 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/const.js.map @@ -0,0 +1 @@ +{"version":3,"file":"const.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/const.ts"],"names":[],"mappings":";;AAEA,mDAAuC;AACvC,6CAA0C;AAC1C,+CAAuC;AAIvC,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,2BAA2B;IACpC,MAAM,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,kBAAkB,UAAU,GAAG;CAC3D,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,IAAI;IACX,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAC,GAAG,GAAG,CAAA;QAClD,IAAI,KAAK,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,IAAI,QAAQ,CAAC,EAAE;YAClD,GAAG,CAAC,SAAS,CAAC,IAAA,WAAC,EAAA,IAAI,IAAA,cAAO,EAAC,GAAG,EAAE,eAAK,CAAC,IAAI,IAAI,KAAK,UAAU,GAAG,CAAC,CAAA;SAClE;aAAM;YACL,GAAG,CAAC,IAAI,CAAC,IAAA,WAAC,EAAA,GAAG,MAAM,QAAQ,IAAI,EAAE,CAAC,CAAA;SACnC;IACH,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/dependentRequired.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/dependentRequired.d.ts new file mode 100644 index 00000000..00f0d370 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/dependentRequired.d.ts @@ -0,0 +1,5 @@ +import type { CodeKeywordDefinition, ErrorObject } from "../../types"; +import { DependenciesErrorParams, PropertyDependencies } from "../applicator/dependencies"; +export type DependentRequiredError = ErrorObject<"dependentRequired", DependenciesErrorParams, PropertyDependencies>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/dependentRequired.js b/website/node_modules/ajv/dist/vocabularies/validation/dependentRequired.js new file mode 100644 index 00000000..09e59639 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/dependentRequired.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const dependencies_1 = require("../applicator/dependencies"); +const def = { + keyword: "dependentRequired", + type: "object", + schemaType: "object", + error: dependencies_1.error, + code: (cxt) => (0, dependencies_1.validatePropertyDeps)(cxt), +}; +exports.default = def; +//# sourceMappingURL=dependentRequired.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/dependentRequired.js.map b/website/node_modules/ajv/dist/vocabularies/validation/dependentRequired.js.map new file mode 100644 index 00000000..c7e88f88 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/dependentRequired.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dependentRequired.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/dependentRequired.ts"],"names":[],"mappings":";;AACA,6DAKmC;AAQnC,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,mBAAmB;IAC5B,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAL,oBAAK;IACL,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAA,mCAAoB,EAAC,GAAG,CAAC;CACzC,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/enum.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/enum.d.ts new file mode 100644 index 00000000..6231082f --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/enum.d.ts @@ -0,0 +1,8 @@ +import type { CodeKeywordDefinition, ErrorObject } from "../../types"; +export type EnumError = ErrorObject<"enum", { + allowedValues: any[]; +}, any[] | { + $data: string; +}>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/enum.js b/website/node_modules/ajv/dist/vocabularies/validation/enum.js new file mode 100644 index 00000000..eab6487e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/enum.js @@ -0,0 +1,48 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const equal_1 = require("../../runtime/equal"); +const error = { + message: "must be equal to one of the allowed values", + params: ({ schemaCode }) => (0, codegen_1._) `{allowedValues: ${schemaCode}}`, +}; +const def = { + keyword: "enum", + schemaType: "array", + $data: true, + error, + code(cxt) { + const { gen, data, $data, schema, schemaCode, it } = cxt; + if (!$data && schema.length === 0) + throw new Error("enum must have non-empty array"); + const useLoop = schema.length >= it.opts.loopEnum; + let eql; + const getEql = () => (eql !== null && eql !== void 0 ? eql : (eql = (0, util_1.useFunc)(gen, equal_1.default))); + let valid; + if (useLoop || $data) { + valid = gen.let("valid"); + cxt.block$data(valid, loopEnum); + } + else { + /* istanbul ignore if */ + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + const vSchema = gen.const("vSchema", schemaCode); + valid = (0, codegen_1.or)(...schema.map((_x, i) => equalCode(vSchema, i))); + } + cxt.pass(valid); + function loopEnum() { + gen.assign(valid, false); + gen.forOf("v", schemaCode, (v) => gen.if((0, codegen_1._) `${getEql()}(${data}, ${v})`, () => gen.assign(valid, true).break())); + } + function equalCode(vSchema, i) { + const sch = schema[i]; + return typeof sch === "object" && sch !== null + ? (0, codegen_1._) `${getEql()}(${data}, ${vSchema}[${i}])` + : (0, codegen_1._) `${data} === ${sch}`; + } + }, +}; +exports.default = def; +//# sourceMappingURL=enum.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/enum.js.map b/website/node_modules/ajv/dist/vocabularies/validation/enum.js.map new file mode 100644 index 00000000..7b4e1eda --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/enum.js.map @@ -0,0 +1 @@ +{"version":3,"file":"enum.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/enum.ts"],"names":[],"mappings":";;AAEA,mDAAuD;AACvD,6CAA0C;AAC1C,+CAAuC;AAIvC,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,4CAA4C;IACrD,MAAM,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,mBAAmB,UAAU,GAAG;CAC5D,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,MAAM;IACf,UAAU,EAAE,OAAO;IACnB,KAAK,EAAE,IAAI;IACX,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACtD,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;QACpF,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAA;QACjD,IAAI,GAAqB,CAAA;QACzB,MAAM,MAAM,GAAG,GAAS,EAAE,CAAC,CAAC,GAAG,aAAH,GAAG,cAAH,GAAG,IAAH,GAAG,GAAK,IAAA,cAAO,EAAC,GAAG,EAAE,eAAK,CAAC,EAAC,CAAA;QAExD,IAAI,KAAW,CAAA;QACf,IAAI,OAAO,IAAI,KAAK,EAAE;YACpB,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;YACxB,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;SAChC;aAAM;YACL,wBAAwB;YACxB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;YACvE,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;YAChD,KAAK,GAAG,IAAA,YAAE,EAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,EAAW,EAAE,CAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;SAC7E;QACD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAEf,SAAS,QAAQ;YACf,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;YACxB,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,UAAkB,EAAE,CAAC,CAAC,EAAE,EAAE,CACvC,GAAG,CAAC,EAAE,CAAC,IAAA,WAAC,EAAA,GAAG,MAAM,EAAE,IAAI,IAAI,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAC7E,CAAA;QACH,CAAC;QAED,SAAS,SAAS,CAAC,OAAa,EAAE,CAAS;YACzC,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;YACrB,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;gBAC5C,CAAC,CAAC,IAAA,WAAC,EAAA,GAAG,MAAM,EAAE,IAAI,IAAI,KAAK,OAAO,IAAI,CAAC,IAAI;gBAC3C,CAAC,CAAC,IAAA,WAAC,EAAA,GAAG,IAAI,QAAQ,GAAG,EAAE,CAAA;QAC3B,CAAC;IACH,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/index.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/index.d.ts new file mode 100644 index 00000000..a078be55 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/index.d.ts @@ -0,0 +1,16 @@ +import type { ErrorObject, Vocabulary } from "../../types"; +import { LimitNumberError } from "./limitNumber"; +import { MultipleOfError } from "./multipleOf"; +import { PatternError } from "./pattern"; +import { RequiredError } from "./required"; +import { UniqueItemsError } from "./uniqueItems"; +import { ConstError } from "./const"; +import { EnumError } from "./enum"; +declare const validation: Vocabulary; +export default validation; +type LimitError = ErrorObject<"maxItems" | "minItems" | "minProperties" | "maxProperties" | "minLength" | "maxLength", { + limit: number; +}, number | { + $data: string; +}>; +export type ValidationKeywordError = LimitError | LimitNumberError | MultipleOfError | PatternError | RequiredError | UniqueItemsError | ConstError | EnumError; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/index.js b/website/node_modules/ajv/dist/vocabularies/validation/index.js new file mode 100644 index 00000000..7b56b4e4 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/index.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const limitNumber_1 = require("./limitNumber"); +const multipleOf_1 = require("./multipleOf"); +const limitLength_1 = require("./limitLength"); +const pattern_1 = require("./pattern"); +const limitProperties_1 = require("./limitProperties"); +const required_1 = require("./required"); +const limitItems_1 = require("./limitItems"); +const uniqueItems_1 = require("./uniqueItems"); +const const_1 = require("./const"); +const enum_1 = require("./enum"); +const validation = [ + // number + limitNumber_1.default, + multipleOf_1.default, + // string + limitLength_1.default, + pattern_1.default, + // object + limitProperties_1.default, + required_1.default, + // array + limitItems_1.default, + uniqueItems_1.default, + // any + { keyword: "type", schemaType: ["string", "array"] }, + { keyword: "nullable", schemaType: "boolean" }, + const_1.default, + enum_1.default, +]; +exports.default = validation; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/index.js.map b/website/node_modules/ajv/dist/vocabularies/validation/index.js.map new file mode 100644 index 00000000..69436fb3 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/index.ts"],"names":[],"mappings":";;AACA,+CAA2D;AAC3D,6CAAwD;AACxD,+CAAuC;AACvC,uCAA+C;AAC/C,uDAA+C;AAC/C,yCAAkD;AAClD,6CAAqC;AACrC,+CAA2D;AAC3D,mCAAgD;AAChD,iCAA6C;AAE7C,MAAM,UAAU,GAAe;IAC7B,SAAS;IACT,qBAAW;IACX,oBAAU;IACV,SAAS;IACT,qBAAW;IACX,iBAAO;IACP,SAAS;IACT,yBAAe;IACf,kBAAQ;IACR,QAAQ;IACR,oBAAU;IACV,qBAAW;IACX,MAAM;IACN,EAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAC;IAClD,EAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAC;IAC5C,eAAY;IACZ,cAAW;CACZ,CAAA;AAED,kBAAe,UAAU,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitContains.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/limitContains.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitContains.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitContains.js b/website/node_modules/ajv/dist/vocabularies/validation/limitContains.js new file mode 100644 index 00000000..c884dae4 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitContains.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const util_1 = require("../../compile/util"); +const def = { + keyword: ["maxContains", "minContains"], + type: "array", + schemaType: "number", + code({ keyword, parentSchema, it }) { + if (parentSchema.contains === undefined) { + (0, util_1.checkStrictMode)(it, `"${keyword}" without "contains" is ignored`); + } + }, +}; +exports.default = def; +//# sourceMappingURL=limitContains.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitContains.js.map b/website/node_modules/ajv/dist/vocabularies/validation/limitContains.js.map new file mode 100644 index 00000000..788010aa --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitContains.js.map @@ -0,0 +1 @@ +{"version":3,"file":"limitContains.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/limitContains.ts"],"names":[],"mappings":";;AAEA,6CAAkD;AAElD,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,IAAI,EAAE,OAAO;IACb,UAAU,EAAE,QAAQ;IACpB,IAAI,CAAC,EAAC,OAAO,EAAE,YAAY,EAAE,EAAE,EAAa;QAC1C,IAAI,YAAY,CAAC,QAAQ,KAAK,SAAS,EAAE;YACvC,IAAA,sBAAe,EAAC,EAAE,EAAE,IAAI,OAAO,iCAAiC,CAAC,CAAA;SAClE;IACH,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitItems.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/limitItems.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitItems.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitItems.js b/website/node_modules/ajv/dist/vocabularies/validation/limitItems.js new file mode 100644 index 00000000..e1386f88 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitItems.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const error = { + message({ keyword, schemaCode }) { + const comp = keyword === "maxItems" ? "more" : "fewer"; + return (0, codegen_1.str) `must NOT have ${comp} than ${schemaCode} items`; + }, + params: ({ schemaCode }) => (0, codegen_1._) `{limit: ${schemaCode}}`, +}; +const def = { + keyword: ["maxItems", "minItems"], + type: "array", + schemaType: "number", + $data: true, + error, + code(cxt) { + const { keyword, data, schemaCode } = cxt; + const op = keyword === "maxItems" ? codegen_1.operators.GT : codegen_1.operators.LT; + cxt.fail$data((0, codegen_1._) `${data}.length ${op} ${schemaCode}`); + }, +}; +exports.default = def; +//# sourceMappingURL=limitItems.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitItems.js.map b/website/node_modules/ajv/dist/vocabularies/validation/limitItems.js.map new file mode 100644 index 00000000..690c7345 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitItems.js.map @@ -0,0 +1 @@ +{"version":3,"file":"limitItems.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/limitItems.ts"],"names":[],"mappings":";;AAEA,mDAAuD;AAEvD,MAAM,KAAK,GAA2B;IACpC,OAAO,CAAC,EAAC,OAAO,EAAE,UAAU,EAAC;QAC3B,MAAM,IAAI,GAAG,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;QACtD,OAAO,IAAA,aAAG,EAAA,iBAAiB,IAAI,SAAS,UAAU,QAAQ,CAAA;IAC5D,CAAC;IACD,MAAM,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,WAAW,UAAU,GAAG;CACpD,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;IACjC,IAAI,EAAE,OAAO;IACb,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAE,IAAI;IACX,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAC,GAAG,GAAG,CAAA;QACvC,MAAM,EAAE,GAAG,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,mBAAS,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAS,CAAC,EAAE,CAAA;QAC/D,GAAG,CAAC,SAAS,CAAC,IAAA,WAAC,EAAA,GAAG,IAAI,WAAW,EAAE,IAAI,UAAU,EAAE,CAAC,CAAA;IACtD,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitLength.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/limitLength.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitLength.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitLength.js b/website/node_modules/ajv/dist/vocabularies/validation/limitLength.js new file mode 100644 index 00000000..6ae5f92e --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitLength.js @@ -0,0 +1,27 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const ucs2length_1 = require("../../runtime/ucs2length"); +const error = { + message({ keyword, schemaCode }) { + const comp = keyword === "maxLength" ? "more" : "fewer"; + return (0, codegen_1.str) `must NOT have ${comp} than ${schemaCode} characters`; + }, + params: ({ schemaCode }) => (0, codegen_1._) `{limit: ${schemaCode}}`, +}; +const def = { + keyword: ["maxLength", "minLength"], + type: "string", + schemaType: "number", + $data: true, + error, + code(cxt) { + const { keyword, data, schemaCode, it } = cxt; + const op = keyword === "maxLength" ? codegen_1.operators.GT : codegen_1.operators.LT; + const len = it.opts.unicode === false ? (0, codegen_1._) `${data}.length` : (0, codegen_1._) `${(0, util_1.useFunc)(cxt.gen, ucs2length_1.default)}(${data})`; + cxt.fail$data((0, codegen_1._) `${len} ${op} ${schemaCode}`); + }, +}; +exports.default = def; +//# sourceMappingURL=limitLength.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitLength.js.map b/website/node_modules/ajv/dist/vocabularies/validation/limitLength.js.map new file mode 100644 index 00000000..f09c35e5 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitLength.js.map @@ -0,0 +1 @@ +{"version":3,"file":"limitLength.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/limitLength.ts"],"names":[],"mappings":";;AAEA,mDAAuD;AACvD,6CAA0C;AAC1C,yDAAiD;AAEjD,MAAM,KAAK,GAA2B;IACpC,OAAO,CAAC,EAAC,OAAO,EAAE,UAAU,EAAC;QAC3B,MAAM,IAAI,GAAG,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;QACvD,OAAO,IAAA,aAAG,EAAA,iBAAiB,IAAI,SAAS,UAAU,aAAa,CAAA;IACjE,CAAC;IACD,MAAM,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,WAAW,UAAU,GAAG;CACpD,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC;IACnC,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAE,IAAI;IACX,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QAC3C,MAAM,EAAE,GAAG,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,mBAAS,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAS,CAAC,EAAE,CAAA;QAChE,MAAM,GAAG,GACP,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,IAAA,WAAC,EAAA,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC,IAAA,WAAC,EAAA,GAAG,IAAA,cAAO,EAAC,GAAG,CAAC,GAAG,EAAE,oBAAU,CAAC,IAAI,IAAI,GAAG,CAAA;QAC7F,GAAG,CAAC,SAAS,CAAC,IAAA,WAAC,EAAA,GAAG,GAAG,IAAI,EAAE,IAAI,UAAU,EAAE,CAAC,CAAA;IAC9C,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitNumber.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/limitNumber.d.ts new file mode 100644 index 00000000..7b35096d --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitNumber.d.ts @@ -0,0 +1,11 @@ +import type { CodeKeywordDefinition, ErrorObject } from "../../types"; +type Kwd = "maximum" | "minimum" | "exclusiveMaximum" | "exclusiveMinimum"; +type Comparison = "<=" | ">=" | "<" | ">"; +export type LimitNumberError = ErrorObject; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitNumber.js b/website/node_modules/ajv/dist/vocabularies/validation/limitNumber.js new file mode 100644 index 00000000..a97c0eb9 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitNumber.js @@ -0,0 +1,27 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const ops = codegen_1.operators; +const KWDs = { + maximum: { okStr: "<=", ok: ops.LTE, fail: ops.GT }, + minimum: { okStr: ">=", ok: ops.GTE, fail: ops.LT }, + exclusiveMaximum: { okStr: "<", ok: ops.LT, fail: ops.GTE }, + exclusiveMinimum: { okStr: ">", ok: ops.GT, fail: ops.LTE }, +}; +const error = { + message: ({ keyword, schemaCode }) => (0, codegen_1.str) `must be ${KWDs[keyword].okStr} ${schemaCode}`, + params: ({ keyword, schemaCode }) => (0, codegen_1._) `{comparison: ${KWDs[keyword].okStr}, limit: ${schemaCode}}`, +}; +const def = { + keyword: Object.keys(KWDs), + type: "number", + schemaType: "number", + $data: true, + error, + code(cxt) { + const { keyword, data, schemaCode } = cxt; + cxt.fail$data((0, codegen_1._) `${data} ${KWDs[keyword].fail} ${schemaCode} || isNaN(${data})`); + }, +}; +exports.default = def; +//# sourceMappingURL=limitNumber.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitNumber.js.map b/website/node_modules/ajv/dist/vocabularies/validation/limitNumber.js.map new file mode 100644 index 00000000..18a36533 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitNumber.js.map @@ -0,0 +1 @@ +{"version":3,"file":"limitNumber.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/limitNumber.ts"],"names":[],"mappings":";;AAEA,mDAA6D;AAE7D,MAAM,GAAG,GAAG,mBAAS,CAAA;AAMrB,MAAM,IAAI,GAA4D;IACpE,OAAO,EAAE,EAAC,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,EAAC;IACjD,OAAO,EAAE,EAAC,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,EAAC;IACjD,gBAAgB,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,EAAC;IACzD,gBAAgB,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,EAAC;CAC1D,CAAA;AAQD,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,CAAC,EAAC,OAAO,EAAE,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,aAAG,EAAA,WAAW,IAAI,CAAC,OAAc,CAAC,CAAC,KAAK,IAAI,UAAU,EAAE;IAC5F,MAAM,EAAE,CAAC,EAAC,OAAO,EAAE,UAAU,EAAC,EAAE,EAAE,CAChC,IAAA,WAAC,EAAA,gBAAgB,IAAI,CAAC,OAAc,CAAC,CAAC,KAAK,YAAY,UAAU,GAAG;CACvE,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;IAC1B,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAE,IAAI;IACX,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAC,GAAG,GAAG,CAAA;QACvC,GAAG,CAAC,SAAS,CAAC,IAAA,WAAC,EAAA,GAAG,IAAI,IAAI,IAAI,CAAC,OAAc,CAAC,CAAC,IAAI,IAAI,UAAU,aAAa,IAAI,GAAG,CAAC,CAAA;IACxF,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitProperties.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/limitProperties.d.ts new file mode 100644 index 00000000..cde2aa27 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitProperties.d.ts @@ -0,0 +1,3 @@ +import type { CodeKeywordDefinition } from "../../types"; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitProperties.js b/website/node_modules/ajv/dist/vocabularies/validation/limitProperties.js new file mode 100644 index 00000000..e6fc40cc --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitProperties.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const error = { + message({ keyword, schemaCode }) { + const comp = keyword === "maxProperties" ? "more" : "fewer"; + return (0, codegen_1.str) `must NOT have ${comp} than ${schemaCode} properties`; + }, + params: ({ schemaCode }) => (0, codegen_1._) `{limit: ${schemaCode}}`, +}; +const def = { + keyword: ["maxProperties", "minProperties"], + type: "object", + schemaType: "number", + $data: true, + error, + code(cxt) { + const { keyword, data, schemaCode } = cxt; + const op = keyword === "maxProperties" ? codegen_1.operators.GT : codegen_1.operators.LT; + cxt.fail$data((0, codegen_1._) `Object.keys(${data}).length ${op} ${schemaCode}`); + }, +}; +exports.default = def; +//# sourceMappingURL=limitProperties.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/limitProperties.js.map b/website/node_modules/ajv/dist/vocabularies/validation/limitProperties.js.map new file mode 100644 index 00000000..a695943a --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/limitProperties.js.map @@ -0,0 +1 @@ +{"version":3,"file":"limitProperties.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/limitProperties.ts"],"names":[],"mappings":";;AAEA,mDAAuD;AAEvD,MAAM,KAAK,GAA2B;IACpC,OAAO,CAAC,EAAC,OAAO,EAAE,UAAU,EAAC;QAC3B,MAAM,IAAI,GAAG,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;QAC3D,OAAO,IAAA,aAAG,EAAA,iBAAiB,IAAI,SAAS,UAAU,aAAa,CAAA;IACjE,CAAC;IACD,MAAM,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,WAAW,UAAU,GAAG;CACpD,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,CAAC,eAAe,EAAE,eAAe,CAAC;IAC3C,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAE,IAAI;IACX,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAC,GAAG,GAAG,CAAA;QACvC,MAAM,EAAE,GAAG,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,mBAAS,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAS,CAAC,EAAE,CAAA;QACpE,GAAG,CAAC,SAAS,CAAC,IAAA,WAAC,EAAA,eAAe,IAAI,YAAY,EAAE,IAAI,UAAU,EAAE,CAAC,CAAA;IACnE,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/multipleOf.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/multipleOf.d.ts new file mode 100644 index 00000000..67685dcd --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/multipleOf.d.ts @@ -0,0 +1,8 @@ +import type { CodeKeywordDefinition, ErrorObject } from "../../types"; +export type MultipleOfError = ErrorObject<"multipleOf", { + multipleOf: number; +}, number | { + $data: string; +}>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/multipleOf.js b/website/node_modules/ajv/dist/vocabularies/validation/multipleOf.js new file mode 100644 index 00000000..43cf67b7 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/multipleOf.js @@ -0,0 +1,26 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("../../compile/codegen"); +const error = { + message: ({ schemaCode }) => (0, codegen_1.str) `must be multiple of ${schemaCode}`, + params: ({ schemaCode }) => (0, codegen_1._) `{multipleOf: ${schemaCode}}`, +}; +const def = { + keyword: "multipleOf", + type: "number", + schemaType: "number", + $data: true, + error, + code(cxt) { + const { gen, data, schemaCode, it } = cxt; + // const bdt = bad$DataType(schemaCode, def.schemaType, $data) + const prec = it.opts.multipleOfPrecision; + const res = gen.let("res"); + const invalid = prec + ? (0, codegen_1._) `Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` + : (0, codegen_1._) `${res} !== parseInt(${res})`; + cxt.fail$data((0, codegen_1._) `(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`); + }, +}; +exports.default = def; +//# sourceMappingURL=multipleOf.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/multipleOf.js.map b/website/node_modules/ajv/dist/vocabularies/validation/multipleOf.js.map new file mode 100644 index 00000000..9ef825b7 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/multipleOf.js.map @@ -0,0 +1 @@ +{"version":3,"file":"multipleOf.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/multipleOf.ts"],"names":[],"mappings":";;AAEA,mDAA4C;AAQ5C,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,aAAG,EAAA,uBAAuB,UAAU,EAAE;IACjE,MAAM,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,gBAAgB,UAAU,GAAG;CACzD,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,YAAY;IACrB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAE,IAAI;IACX,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACvC,sEAAsE;QACtE,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAA;QACxC,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAC1B,MAAM,OAAO,GAAG,IAAI;YAClB,CAAC,CAAC,IAAA,WAAC,EAAA,uBAAuB,GAAG,OAAO,GAAG,UAAU,IAAI,EAAE;YACvD,CAAC,CAAC,IAAA,WAAC,EAAA,GAAG,GAAG,iBAAiB,GAAG,GAAG,CAAA;QAClC,GAAG,CAAC,SAAS,CAAC,IAAA,WAAC,EAAA,IAAI,UAAU,cAAc,GAAG,MAAM,IAAI,IAAI,UAAU,KAAK,OAAO,IAAI,CAAC,CAAA;IACzF,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/pattern.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/pattern.d.ts new file mode 100644 index 00000000..7edbfda9 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/pattern.d.ts @@ -0,0 +1,8 @@ +import type { CodeKeywordDefinition, ErrorObject } from "../../types"; +export type PatternError = ErrorObject<"pattern", { + pattern: string; +}, string | { + $data: string; +}>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/pattern.js b/website/node_modules/ajv/dist/vocabularies/validation/pattern.js new file mode 100644 index 00000000..f8ccdf29 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/pattern.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const code_1 = require("../code"); +const codegen_1 = require("../../compile/codegen"); +const error = { + message: ({ schemaCode }) => (0, codegen_1.str) `must match pattern "${schemaCode}"`, + params: ({ schemaCode }) => (0, codegen_1._) `{pattern: ${schemaCode}}`, +}; +const def = { + keyword: "pattern", + type: "string", + schemaType: "string", + $data: true, + error, + code(cxt) { + const { data, $data, schema, schemaCode, it } = cxt; + // TODO regexp should be wrapped in try/catchs + const u = it.opts.unicodeRegExp ? "u" : ""; + const regExp = $data ? (0, codegen_1._) `(new RegExp(${schemaCode}, ${u}))` : (0, code_1.usePattern)(cxt, schema); + cxt.fail$data((0, codegen_1._) `!${regExp}.test(${data})`); + }, +}; +exports.default = def; +//# sourceMappingURL=pattern.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/pattern.js.map b/website/node_modules/ajv/dist/vocabularies/validation/pattern.js.map new file mode 100644 index 00000000..82fa5d74 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/pattern.js.map @@ -0,0 +1 @@ +{"version":3,"file":"pattern.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/pattern.ts"],"names":[],"mappings":";;AAEA,kCAAkC;AAClC,mDAA4C;AAI5C,MAAM,KAAK,GAA2B;IACpC,OAAO,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,aAAG,EAAA,uBAAuB,UAAU,GAAG;IAClE,MAAM,EAAE,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE,CAAC,IAAA,WAAC,EAAA,aAAa,UAAU,GAAG;CACtD,CAAA;AAED,MAAM,GAAG,GAA0B;IACjC,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,QAAQ;IACpB,KAAK,EAAE,IAAI;IACX,KAAK;IACL,IAAI,CAAC,GAAe;QAClB,MAAM,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAC,GAAG,GAAG,CAAA;QACjD,8CAA8C;QAC9C,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;QAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAA,WAAC,EAAA,eAAe,UAAU,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAA,iBAAU,EAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QACrF,GAAG,CAAC,SAAS,CAAC,IAAA,WAAC,EAAA,IAAI,MAAM,SAAS,IAAI,GAAG,CAAC,CAAA;IAC5C,CAAC;CACF,CAAA;AAED,kBAAe,GAAG,CAAA"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/required.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/required.d.ts new file mode 100644 index 00000000..c9cffda1 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/required.d.ts @@ -0,0 +1,8 @@ +import type { CodeKeywordDefinition, ErrorObject } from "../../types"; +export type RequiredError = ErrorObject<"required", { + missingProperty: string; +}, string[] | { + $data: string; +}>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/required.js b/website/node_modules/ajv/dist/vocabularies/validation/required.js new file mode 100644 index 00000000..1d8e2926 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/required.js @@ -0,0 +1,79 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const code_1 = require("../code"); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const error = { + message: ({ params: { missingProperty } }) => (0, codegen_1.str) `must have required property '${missingProperty}'`, + params: ({ params: { missingProperty } }) => (0, codegen_1._) `{missingProperty: ${missingProperty}}`, +}; +const def = { + keyword: "required", + type: "object", + schemaType: "array", + $data: true, + error, + code(cxt) { + const { gen, schema, schemaCode, data, $data, it } = cxt; + const { opts } = it; + if (!$data && schema.length === 0) + return; + const useLoop = schema.length >= opts.loopRequired; + if (it.allErrors) + allErrorsMode(); + else + exitOnErrorMode(); + if (opts.strictRequired) { + const props = cxt.parentSchema.properties; + const { definedProperties } = cxt.it; + for (const requiredKey of schema) { + if ((props === null || props === void 0 ? void 0 : props[requiredKey]) === undefined && !definedProperties.has(requiredKey)) { + const schemaPath = it.schemaEnv.baseId + it.errSchemaPath; + const msg = `required property "${requiredKey}" is not defined at "${schemaPath}" (strictRequired)`; + (0, util_1.checkStrictMode)(it, msg, it.opts.strictRequired); + } + } + } + function allErrorsMode() { + if (useLoop || $data) { + cxt.block$data(codegen_1.nil, loopAllRequired); + } + else { + for (const prop of schema) { + (0, code_1.checkReportMissingProp)(cxt, prop); + } + } + } + function exitOnErrorMode() { + const missing = gen.let("missing"); + if (useLoop || $data) { + const valid = gen.let("valid", true); + cxt.block$data(valid, () => loopUntilMissing(missing, valid)); + cxt.ok(valid); + } + else { + gen.if((0, code_1.checkMissingProp)(cxt, schema, missing)); + (0, code_1.reportMissingProp)(cxt, missing); + gen.else(); + } + } + function loopAllRequired() { + gen.forOf("prop", schemaCode, (prop) => { + cxt.setParams({ missingProperty: prop }); + gen.if((0, code_1.noPropertyInData)(gen, data, prop, opts.ownProperties), () => cxt.error()); + }); + } + function loopUntilMissing(missing, valid) { + cxt.setParams({ missingProperty: missing }); + gen.forOf(missing, schemaCode, () => { + gen.assign(valid, (0, code_1.propertyInData)(gen, data, missing, opts.ownProperties)); + gen.if((0, codegen_1.not)(valid), () => { + cxt.error(); + gen.break(); + }); + }, codegen_1.nil); + } + }, +}; +exports.default = def; +//# sourceMappingURL=required.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/required.js.map b/website/node_modules/ajv/dist/vocabularies/validation/required.js.map new file mode 100644 index 00000000..0fcbee58 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/required.js.map @@ -0,0 +1 @@ +{"version":3,"file":"required.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/required.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/uniqueItems.d.ts b/website/node_modules/ajv/dist/vocabularies/validation/uniqueItems.d.ts new file mode 100644 index 00000000..e7c241b9 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/uniqueItems.d.ts @@ -0,0 +1,9 @@ +import type { CodeKeywordDefinition, ErrorObject } from "../../types"; +export type UniqueItemsError = ErrorObject<"uniqueItems", { + i: number; + j: number; +}, boolean | { + $data: string; +}>; +declare const def: CodeKeywordDefinition; +export default def; diff --git a/website/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js b/website/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js new file mode 100644 index 00000000..cdbecea1 --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js @@ -0,0 +1,64 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const dataType_1 = require("../../compile/validate/dataType"); +const codegen_1 = require("../../compile/codegen"); +const util_1 = require("../../compile/util"); +const equal_1 = require("../../runtime/equal"); +const error = { + message: ({ params: { i, j } }) => (0, codegen_1.str) `must NOT have duplicate items (items ## ${j} and ${i} are identical)`, + params: ({ params: { i, j } }) => (0, codegen_1._) `{i: ${i}, j: ${j}}`, +}; +const def = { + keyword: "uniqueItems", + type: "array", + schemaType: "boolean", + $data: true, + error, + code(cxt) { + const { gen, data, $data, schema, parentSchema, schemaCode, it } = cxt; + if (!$data && !schema) + return; + const valid = gen.let("valid"); + const itemTypes = parentSchema.items ? (0, dataType_1.getSchemaTypes)(parentSchema.items) : []; + cxt.block$data(valid, validateUniqueItems, (0, codegen_1._) `${schemaCode} === false`); + cxt.ok(valid); + function validateUniqueItems() { + const i = gen.let("i", (0, codegen_1._) `${data}.length`); + const j = gen.let("j"); + cxt.setParams({ i, j }); + gen.assign(valid, true); + gen.if((0, codegen_1._) `${i} > 1`, () => (canOptimize() ? loopN : loopN2)(i, j)); + } + function canOptimize() { + return itemTypes.length > 0 && !itemTypes.some((t) => t === "object" || t === "array"); + } + function loopN(i, j) { + const item = gen.name("item"); + const wrongType = (0, dataType_1.checkDataTypes)(itemTypes, item, it.opts.strictNumbers, dataType_1.DataType.Wrong); + const indices = gen.const("indices", (0, codegen_1._) `{}`); + gen.for((0, codegen_1._) `;${i}--;`, () => { + gen.let(item, (0, codegen_1._) `${data}[${i}]`); + gen.if(wrongType, (0, codegen_1._) `continue`); + if (itemTypes.length > 1) + gen.if((0, codegen_1._) `typeof ${item} == "string"`, (0, codegen_1._) `${item} += "_"`); + gen + .if((0, codegen_1._) `typeof ${indices}[${item}] == "number"`, () => { + gen.assign(j, (0, codegen_1._) `${indices}[${item}]`); + cxt.error(); + gen.assign(valid, false).break(); + }) + .code((0, codegen_1._) `${indices}[${item}] = ${i}`); + }); + } + function loopN2(i, j) { + const eql = (0, util_1.useFunc)(gen, equal_1.default); + const outer = gen.name("outer"); + gen.label(outer).for((0, codegen_1._) `;${i}--;`, () => gen.for((0, codegen_1._) `${j} = ${i}; ${j}--;`, () => gen.if((0, codegen_1._) `${eql}(${data}[${i}], ${data}[${j}])`, () => { + cxt.error(); + gen.assign(valid, false).break(outer); + }))); + } + }, +}; +exports.default = def; +//# sourceMappingURL=uniqueItems.js.map \ No newline at end of file diff --git a/website/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js.map b/website/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js.map new file mode 100644 index 00000000..46eb7a2a --- /dev/null +++ b/website/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js.map @@ -0,0 +1 @@ +{"version":3,"file":"uniqueItems.js","sourceRoot":"","sources":["../../../lib/vocabularies/validation/uniqueItems.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/website/node_modules/ajv/lib/2019.ts b/website/node_modules/ajv/lib/2019.ts new file mode 100644 index 00000000..45a3fa53 --- /dev/null +++ b/website/node_modules/ajv/lib/2019.ts @@ -0,0 +1,80 @@ +import type {AnySchemaObject} from "./types" +import AjvCore, {Options} from "./core" + +import draft7Vocabularies from "./vocabularies/draft7" +import dynamicVocabulary from "./vocabularies/dynamic" +import nextVocabulary from "./vocabularies/next" +import unevaluatedVocabulary from "./vocabularies/unevaluated" +import discriminator from "./vocabularies/discriminator" +import addMetaSchema2019 from "./refs/json-schema-2019-09" + +const META_SCHEMA_ID = "https://json-schema.org/draft/2019-09/schema" + +class Ajv2019 extends AjvCore { + constructor(opts: Options = {}) { + super({ + ...opts, + dynamicRef: true, + next: true, + unevaluated: true, + }) + } + + _addVocabularies(): void { + super._addVocabularies() + this.addVocabulary(dynamicVocabulary) + draft7Vocabularies.forEach((v) => this.addVocabulary(v)) + this.addVocabulary(nextVocabulary) + this.addVocabulary(unevaluatedVocabulary) + if (this.opts.discriminator) this.addKeyword(discriminator) + } + + _addDefaultMetaSchema(): void { + super._addDefaultMetaSchema() + const {$data, meta} = this.opts + if (!meta) return + addMetaSchema2019.call(this, $data) + this.refs["http://json-schema.org/schema"] = META_SCHEMA_ID + } + + defaultMeta(): string | AnySchemaObject | undefined { + return (this.opts.defaultMeta = + super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined)) + } +} + +module.exports = exports = Ajv2019 +Object.defineProperty(exports, "__esModule", {value: true}) + +export default Ajv2019 + +export { + Format, + FormatDefinition, + AsyncFormatDefinition, + KeywordDefinition, + KeywordErrorDefinition, + CodeKeywordDefinition, + MacroKeywordDefinition, + FuncKeywordDefinition, + Vocabulary, + Schema, + SchemaObject, + AnySchemaObject, + AsyncSchema, + AnySchema, + ValidateFunction, + AsyncValidateFunction, + ErrorObject, + ErrorNoParams, +} from "./types" + +export {Plugin, Options, CodeOptions, InstanceOptions, Logger, ErrorsTextOptions} from "./core" +export {SchemaCxt, SchemaObjCxt} from "./compile" +export {KeywordCxt} from "./compile/validate" +export {DefinedError} from "./vocabularies/errors" +export {JSONType} from "./compile/rules" +export {JSONSchemaType} from "./types/json-schema" +export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./compile/codegen" +export {default as ValidationError} from "./runtime/validation_error" +export {default as MissingRefError} from "./compile/ref_error" diff --git a/website/node_modules/ajv/lib/2020.ts b/website/node_modules/ajv/lib/2020.ts new file mode 100644 index 00000000..afbdda20 --- /dev/null +++ b/website/node_modules/ajv/lib/2020.ts @@ -0,0 +1,74 @@ +import type {AnySchemaObject} from "./types" +import AjvCore, {Options} from "./core" + +import draft2020Vocabularies from "./vocabularies/draft2020" +import discriminator from "./vocabularies/discriminator" +import addMetaSchema2020 from "./refs/json-schema-2020-12" + +const META_SCHEMA_ID = "https://json-schema.org/draft/2020-12/schema" + +class Ajv2020 extends AjvCore { + constructor(opts: Options = {}) { + super({ + ...opts, + dynamicRef: true, + next: true, + unevaluated: true, + }) + } + + _addVocabularies(): void { + super._addVocabularies() + draft2020Vocabularies.forEach((v) => this.addVocabulary(v)) + if (this.opts.discriminator) this.addKeyword(discriminator) + } + + _addDefaultMetaSchema(): void { + super._addDefaultMetaSchema() + const {$data, meta} = this.opts + if (!meta) return + addMetaSchema2020.call(this, $data) + this.refs["http://json-schema.org/schema"] = META_SCHEMA_ID + } + + defaultMeta(): string | AnySchemaObject | undefined { + return (this.opts.defaultMeta = + super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined)) + } +} + +module.exports = exports = Ajv2020 +Object.defineProperty(exports, "__esModule", {value: true}) + +export default Ajv2020 + +export { + Format, + FormatDefinition, + AsyncFormatDefinition, + KeywordDefinition, + KeywordErrorDefinition, + CodeKeywordDefinition, + MacroKeywordDefinition, + FuncKeywordDefinition, + Vocabulary, + Schema, + SchemaObject, + AnySchemaObject, + AsyncSchema, + AnySchema, + ValidateFunction, + AsyncValidateFunction, + ErrorObject, + ErrorNoParams, +} from "./types" + +export {Plugin, Options, CodeOptions, InstanceOptions, Logger, ErrorsTextOptions} from "./core" +export {SchemaCxt, SchemaObjCxt} from "./compile" +export {KeywordCxt} from "./compile/validate" +export {DefinedError} from "./vocabularies/errors" +export {JSONType} from "./compile/rules" +export {JSONSchemaType} from "./types/json-schema" +export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./compile/codegen" +export {default as ValidationError} from "./runtime/validation_error" +export {default as MissingRefError} from "./compile/ref_error" diff --git a/website/node_modules/ajv/lib/ajv.ts b/website/node_modules/ajv/lib/ajv.ts new file mode 100644 index 00000000..7f87c8ae --- /dev/null +++ b/website/node_modules/ajv/lib/ajv.ts @@ -0,0 +1,69 @@ +import type {AnySchemaObject} from "./types" +import AjvCore from "./core" +import draft7Vocabularies from "./vocabularies/draft7" +import discriminator from "./vocabularies/discriminator" +import * as draft7MetaSchema from "./refs/json-schema-draft-07.json" + +const META_SUPPORT_DATA = ["/properties"] + +const META_SCHEMA_ID = "http://json-schema.org/draft-07/schema" + +class Ajv extends AjvCore { + _addVocabularies(): void { + super._addVocabularies() + draft7Vocabularies.forEach((v) => this.addVocabulary(v)) + if (this.opts.discriminator) this.addKeyword(discriminator) + } + + _addDefaultMetaSchema(): void { + super._addDefaultMetaSchema() + if (!this.opts.meta) return + const metaSchema = this.opts.$data + ? this.$dataMetaSchema(draft7MetaSchema, META_SUPPORT_DATA) + : draft7MetaSchema + this.addMetaSchema(metaSchema, META_SCHEMA_ID, false) + this.refs["http://json-schema.org/schema"] = META_SCHEMA_ID + } + + defaultMeta(): string | AnySchemaObject | undefined { + return (this.opts.defaultMeta = + super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined)) + } +} + +module.exports = exports = Ajv +Object.defineProperty(exports, "__esModule", {value: true}) + +export default Ajv + +export { + Format, + FormatDefinition, + AsyncFormatDefinition, + KeywordDefinition, + KeywordErrorDefinition, + CodeKeywordDefinition, + MacroKeywordDefinition, + FuncKeywordDefinition, + Vocabulary, + Schema, + SchemaObject, + AnySchemaObject, + AsyncSchema, + AnySchema, + ValidateFunction, + AsyncValidateFunction, + SchemaValidateFunction, + ErrorObject, + ErrorNoParams, +} from "./types" + +export {Plugin, Options, CodeOptions, InstanceOptions, Logger, ErrorsTextOptions} from "./core" +export {SchemaCxt, SchemaObjCxt} from "./compile" +export {KeywordCxt} from "./compile/validate" +export {DefinedError} from "./vocabularies/errors" +export {JSONType} from "./compile/rules" +export {JSONSchemaType} from "./types/json-schema" +export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./compile/codegen" +export {default as ValidationError} from "./runtime/validation_error" +export {default as MissingRefError} from "./compile/ref_error" diff --git a/website/node_modules/ajv/lib/compile/codegen/code.ts b/website/node_modules/ajv/lib/compile/codegen/code.ts new file mode 100644 index 00000000..b1770197 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/codegen/code.ts @@ -0,0 +1,168 @@ +export abstract class _CodeOrName { + abstract readonly str: string + abstract readonly names: UsedNames + abstract toString(): string + abstract emptyStr(): boolean +} + +export const IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i + +export class Name extends _CodeOrName { + readonly str: string + constructor(s: string) { + super() + if (!IDENTIFIER.test(s)) throw new Error("CodeGen: name must be a valid identifier") + this.str = s + } + + toString(): string { + return this.str + } + + emptyStr(): boolean { + return false + } + + get names(): UsedNames { + return {[this.str]: 1} + } +} + +export class _Code extends _CodeOrName { + readonly _items: readonly CodeItem[] + private _str?: string + private _names?: UsedNames + + constructor(code: string | readonly CodeItem[]) { + super() + this._items = typeof code === "string" ? [code] : code + } + + toString(): string { + return this.str + } + + emptyStr(): boolean { + if (this._items.length > 1) return false + const item = this._items[0] + return item === "" || item === '""' + } + + get str(): string { + return (this._str ??= this._items.reduce((s: string, c: CodeItem) => `${s}${c}`, "")) + } + + get names(): UsedNames { + return (this._names ??= this._items.reduce((names: UsedNames, c) => { + if (c instanceof Name) names[c.str] = (names[c.str] || 0) + 1 + return names + }, {})) + } +} + +export type CodeItem = Name | string | number | boolean | null + +export type UsedNames = Record + +export type Code = _Code | Name + +export type SafeExpr = Code | number | boolean | null + +export const nil = new _Code("") + +type CodeArg = SafeExpr | string | undefined + +export function _(strs: TemplateStringsArray, ...args: CodeArg[]): _Code { + const code: CodeItem[] = [strs[0]] + let i = 0 + while (i < args.length) { + addCodeArg(code, args[i]) + code.push(strs[++i]) + } + return new _Code(code) +} + +const plus = new _Code("+") + +export function str(strs: TemplateStringsArray, ...args: (CodeArg | string[])[]): _Code { + const expr: CodeItem[] = [safeStringify(strs[0])] + let i = 0 + while (i < args.length) { + expr.push(plus) + addCodeArg(expr, args[i]) + expr.push(plus, safeStringify(strs[++i])) + } + optimize(expr) + return new _Code(expr) +} + +export function addCodeArg(code: CodeItem[], arg: CodeArg | string[]): void { + if (arg instanceof _Code) code.push(...arg._items) + else if (arg instanceof Name) code.push(arg) + else code.push(interpolate(arg)) +} + +function optimize(expr: CodeItem[]): void { + let i = 1 + while (i < expr.length - 1) { + if (expr[i] === plus) { + const res = mergeExprItems(expr[i - 1], expr[i + 1]) + if (res !== undefined) { + expr.splice(i - 1, 3, res) + continue + } + expr[i++] = "+" + } + i++ + } +} + +function mergeExprItems(a: CodeItem, b: CodeItem): CodeItem | undefined { + if (b === '""') return a + if (a === '""') return b + if (typeof a == "string") { + if (b instanceof Name || a[a.length - 1] !== '"') return + if (typeof b != "string") return `${a.slice(0, -1)}${b}"` + if (b[0] === '"') return a.slice(0, -1) + b.slice(1) + return + } + if (typeof b == "string" && b[0] === '"' && !(a instanceof Name)) return `"${a}${b.slice(1)}` + return +} + +export function strConcat(c1: Code, c2: Code): Code { + return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str`${c1}${c2}` +} + +// TODO do not allow arrays here +function interpolate(x?: string | string[] | number | boolean | null): SafeExpr | string { + return typeof x == "number" || typeof x == "boolean" || x === null + ? x + : safeStringify(Array.isArray(x) ? x.join(",") : x) +} + +export function stringify(x: unknown): Code { + return new _Code(safeStringify(x)) +} + +export function safeStringify(x: unknown): string { + return JSON.stringify(x) + .replace(/\u2028/g, "\\u2028") + .replace(/\u2029/g, "\\u2029") +} + +export function getProperty(key: Code | string | number): Code { + return typeof key == "string" && IDENTIFIER.test(key) ? new _Code(`.${key}`) : _`[${key}]` +} + +//Does best effort to format the name properly +export function getEsmExportName(key: Code | string | number): Code { + if (typeof key == "string" && IDENTIFIER.test(key)) { + return new _Code(`${key}`) + } + throw new Error(`CodeGen: invalid export name: ${key}, use explicit $id name mapping`) +} + +export function regexpCode(rx: RegExp): Code { + return new _Code(rx.toString()) +} diff --git a/website/node_modules/ajv/lib/compile/codegen/index.ts b/website/node_modules/ajv/lib/compile/codegen/index.ts new file mode 100644 index 00000000..9d29055d --- /dev/null +++ b/website/node_modules/ajv/lib/compile/codegen/index.ts @@ -0,0 +1,832 @@ +import type {ScopeValueSets, NameValue, ValueScope, ValueScopeName} from "./scope" +import {_, nil, _Code, Code, Name, UsedNames, CodeItem, addCodeArg, _CodeOrName} from "./code" +import {Scope, varKinds} from "./scope" + +export {_, str, strConcat, nil, getProperty, stringify, regexpCode, Name, Code} from "./code" +export {Scope, ScopeStore, ValueScope, ValueScopeName, ScopeValueSets, varKinds} from "./scope" + +// type for expressions that can be safely inserted in code without quotes +export type SafeExpr = Code | number | boolean | null + +// type that is either Code of function that adds code to CodeGen instance using its methods +export type Block = Code | (() => void) + +export const operators = { + GT: new _Code(">"), + GTE: new _Code(">="), + LT: new _Code("<"), + LTE: new _Code("<="), + EQ: new _Code("==="), + NEQ: new _Code("!=="), + NOT: new _Code("!"), + OR: new _Code("||"), + AND: new _Code("&&"), + ADD: new _Code("+"), +} + +abstract class Node { + abstract readonly names: UsedNames + + optimizeNodes(): this | ChildNode | ChildNode[] | undefined { + return this + } + + optimizeNames(_names: UsedNames, _constants: Constants): this | undefined { + return this + } + + // get count(): number { + // return 1 + // } +} + +class Def extends Node { + constructor(private readonly varKind: Name, private readonly name: Name, private rhs?: SafeExpr) { + super() + } + + render({es5, _n}: CGOptions): string { + const varKind = es5 ? varKinds.var : this.varKind + const rhs = this.rhs === undefined ? "" : ` = ${this.rhs}` + return `${varKind} ${this.name}${rhs};` + _n + } + + optimizeNames(names: UsedNames, constants: Constants): this | undefined { + if (!names[this.name.str]) return + if (this.rhs) this.rhs = optimizeExpr(this.rhs, names, constants) + return this + } + + get names(): UsedNames { + return this.rhs instanceof _CodeOrName ? this.rhs.names : {} + } +} + +class Assign extends Node { + constructor(readonly lhs: Code, public rhs: SafeExpr, private readonly sideEffects?: boolean) { + super() + } + + render({_n}: CGOptions): string { + return `${this.lhs} = ${this.rhs};` + _n + } + + optimizeNames(names: UsedNames, constants: Constants): this | undefined { + if (this.lhs instanceof Name && !names[this.lhs.str] && !this.sideEffects) return + this.rhs = optimizeExpr(this.rhs, names, constants) + return this + } + + get names(): UsedNames { + const names = this.lhs instanceof Name ? {} : {...this.lhs.names} + return addExprNames(names, this.rhs) + } +} + +class AssignOp extends Assign { + constructor(lhs: Code, private readonly op: Code, rhs: SafeExpr, sideEffects?: boolean) { + super(lhs, rhs, sideEffects) + } + + render({_n}: CGOptions): string { + return `${this.lhs} ${this.op}= ${this.rhs};` + _n + } +} + +class Label extends Node { + readonly names: UsedNames = {} + constructor(readonly label: Name) { + super() + } + + render({_n}: CGOptions): string { + return `${this.label}:` + _n + } +} + +class Break extends Node { + readonly names: UsedNames = {} + constructor(readonly label?: Code) { + super() + } + + render({_n}: CGOptions): string { + const label = this.label ? ` ${this.label}` : "" + return `break${label};` + _n + } +} + +class Throw extends Node { + constructor(readonly error: Code) { + super() + } + + render({_n}: CGOptions): string { + return `throw ${this.error};` + _n + } + + get names(): UsedNames { + return this.error.names + } +} + +class AnyCode extends Node { + constructor(private code: SafeExpr) { + super() + } + + render({_n}: CGOptions): string { + return `${this.code};` + _n + } + + optimizeNodes(): this | undefined { + return `${this.code}` ? this : undefined + } + + optimizeNames(names: UsedNames, constants: Constants): this { + this.code = optimizeExpr(this.code, names, constants) + return this + } + + get names(): UsedNames { + return this.code instanceof _CodeOrName ? this.code.names : {} + } +} + +abstract class ParentNode extends Node { + constructor(readonly nodes: ChildNode[] = []) { + super() + } + + render(opts: CGOptions): string { + return this.nodes.reduce((code, n) => code + n.render(opts), "") + } + + optimizeNodes(): this | ChildNode | ChildNode[] | undefined { + const {nodes} = this + let i = nodes.length + while (i--) { + const n = nodes[i].optimizeNodes() + if (Array.isArray(n)) nodes.splice(i, 1, ...n) + else if (n) nodes[i] = n + else nodes.splice(i, 1) + } + return nodes.length > 0 ? this : undefined + } + + optimizeNames(names: UsedNames, constants: Constants): this | undefined { + const {nodes} = this + let i = nodes.length + while (i--) { + // iterating backwards improves 1-pass optimization + const n = nodes[i] + if (n.optimizeNames(names, constants)) continue + subtractNames(names, n.names) + nodes.splice(i, 1) + } + return nodes.length > 0 ? this : undefined + } + + get names(): UsedNames { + return this.nodes.reduce((names: UsedNames, n) => addNames(names, n.names), {}) + } + + // get count(): number { + // return this.nodes.reduce((c, n) => c + n.count, 1) + // } +} + +abstract class BlockNode extends ParentNode { + render(opts: CGOptions): string { + return "{" + opts._n + super.render(opts) + "}" + opts._n + } +} + +class Root extends ParentNode {} + +class Else extends BlockNode { + static readonly kind = "else" +} + +class If extends BlockNode { + static readonly kind = "if" + else?: If | Else + constructor(private condition: Code | boolean, nodes?: ChildNode[]) { + super(nodes) + } + + render(opts: CGOptions): string { + let code = `if(${this.condition})` + super.render(opts) + if (this.else) code += "else " + this.else.render(opts) + return code + } + + optimizeNodes(): If | ChildNode[] | undefined { + super.optimizeNodes() + const cond = this.condition + if (cond === true) return this.nodes // else is ignored here + let e = this.else + if (e) { + const ns = e.optimizeNodes() + e = this.else = Array.isArray(ns) ? new Else(ns) : (ns as Else | undefined) + } + if (e) { + if (cond === false) return e instanceof If ? e : e.nodes + if (this.nodes.length) return this + return new If(not(cond), e instanceof If ? [e] : e.nodes) + } + if (cond === false || !this.nodes.length) return undefined + return this + } + + optimizeNames(names: UsedNames, constants: Constants): this | undefined { + this.else = this.else?.optimizeNames(names, constants) + if (!(super.optimizeNames(names, constants) || this.else)) return + this.condition = optimizeExpr(this.condition, names, constants) + return this + } + + get names(): UsedNames { + const names = super.names + addExprNames(names, this.condition) + if (this.else) addNames(names, this.else.names) + return names + } + + // get count(): number { + // return super.count + (this.else?.count || 0) + // } +} + +abstract class For extends BlockNode { + static readonly kind = "for" +} + +class ForLoop extends For { + constructor(private iteration: Code) { + super() + } + + render(opts: CGOptions): string { + return `for(${this.iteration})` + super.render(opts) + } + + optimizeNames(names: UsedNames, constants: Constants): this | undefined { + if (!super.optimizeNames(names, constants)) return + this.iteration = optimizeExpr(this.iteration, names, constants) + return this + } + + get names(): UsedNames { + return addNames(super.names, this.iteration.names) + } +} + +class ForRange extends For { + constructor( + private readonly varKind: Name, + private readonly name: Name, + private readonly from: SafeExpr, + private readonly to: SafeExpr + ) { + super() + } + + render(opts: CGOptions): string { + const varKind = opts.es5 ? varKinds.var : this.varKind + const {name, from, to} = this + return `for(${varKind} ${name}=${from}; ${name}<${to}; ${name}++)` + super.render(opts) + } + + get names(): UsedNames { + const names = addExprNames(super.names, this.from) + return addExprNames(names, this.to) + } +} + +class ForIter extends For { + constructor( + private readonly loop: "of" | "in", + private readonly varKind: Name, + private readonly name: Name, + private iterable: Code + ) { + super() + } + + render(opts: CGOptions): string { + return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts) + } + + optimizeNames(names: UsedNames, constants: Constants): this | undefined { + if (!super.optimizeNames(names, constants)) return + this.iterable = optimizeExpr(this.iterable, names, constants) + return this + } + + get names(): UsedNames { + return addNames(super.names, this.iterable.names) + } +} + +class Func extends BlockNode { + static readonly kind = "func" + constructor(public name: Name, public args: Code, public async?: boolean) { + super() + } + + render(opts: CGOptions): string { + const _async = this.async ? "async " : "" + return `${_async}function ${this.name}(${this.args})` + super.render(opts) + } +} + +class Return extends ParentNode { + static readonly kind = "return" + + render(opts: CGOptions): string { + return "return " + super.render(opts) + } +} + +class Try extends BlockNode { + catch?: Catch + finally?: Finally + + render(opts: CGOptions): string { + let code = "try" + super.render(opts) + if (this.catch) code += this.catch.render(opts) + if (this.finally) code += this.finally.render(opts) + return code + } + + optimizeNodes(): this { + super.optimizeNodes() + this.catch?.optimizeNodes() as Catch | undefined + this.finally?.optimizeNodes() as Finally | undefined + return this + } + + optimizeNames(names: UsedNames, constants: Constants): this { + super.optimizeNames(names, constants) + this.catch?.optimizeNames(names, constants) + this.finally?.optimizeNames(names, constants) + return this + } + + get names(): UsedNames { + const names = super.names + if (this.catch) addNames(names, this.catch.names) + if (this.finally) addNames(names, this.finally.names) + return names + } + + // get count(): number { + // return super.count + (this.catch?.count || 0) + (this.finally?.count || 0) + // } +} + +class Catch extends BlockNode { + static readonly kind = "catch" + constructor(readonly error: Name) { + super() + } + + render(opts: CGOptions): string { + return `catch(${this.error})` + super.render(opts) + } +} + +class Finally extends BlockNode { + static readonly kind = "finally" + render(opts: CGOptions): string { + return "finally" + super.render(opts) + } +} + +type StartBlockNode = If | For | Func | Return | Try + +type LeafNode = Def | Assign | Label | Break | Throw | AnyCode + +type ChildNode = StartBlockNode | LeafNode + +type EndBlockNodeType = + | typeof If + | typeof Else + | typeof For + | typeof Func + | typeof Return + | typeof Catch + | typeof Finally + +type Constants = Record + +export interface CodeGenOptions { + es5?: boolean + lines?: boolean + ownProperties?: boolean +} + +interface CGOptions extends CodeGenOptions { + _n: "\n" | "" +} + +export class CodeGen { + readonly _scope: Scope + readonly _extScope: ValueScope + readonly _values: ScopeValueSets = {} + private readonly _nodes: ParentNode[] + private readonly _blockStarts: number[] = [] + private readonly _constants: Constants = {} + private readonly opts: CGOptions + + constructor(extScope: ValueScope, opts: CodeGenOptions = {}) { + this.opts = {...opts, _n: opts.lines ? "\n" : ""} + this._extScope = extScope + this._scope = new Scope({parent: extScope}) + this._nodes = [new Root()] + } + + toString(): string { + return this._root.render(this.opts) + } + + // returns unique name in the internal scope + name(prefix: string): Name { + return this._scope.name(prefix) + } + + // reserves unique name in the external scope + scopeName(prefix: string): ValueScopeName { + return this._extScope.name(prefix) + } + + // reserves unique name in the external scope and assigns value to it + scopeValue(prefixOrName: ValueScopeName | string, value: NameValue): Name { + const name = this._extScope.value(prefixOrName, value) + const vs = this._values[name.prefix] || (this._values[name.prefix] = new Set()) + vs.add(name) + return name + } + + getScopeValue(prefix: string, keyOrRef: unknown): ValueScopeName | undefined { + return this._extScope.getValue(prefix, keyOrRef) + } + + // return code that assigns values in the external scope to the names that are used internally + // (same names that were returned by gen.scopeName or gen.scopeValue) + scopeRefs(scopeName: Name): Code { + return this._extScope.scopeRefs(scopeName, this._values) + } + + scopeCode(): Code { + return this._extScope.scopeCode(this._values) + } + + private _def( + varKind: Name, + nameOrPrefix: Name | string, + rhs?: SafeExpr, + constant?: boolean + ): Name { + const name = this._scope.toName(nameOrPrefix) + if (rhs !== undefined && constant) this._constants[name.str] = rhs + this._leafNode(new Def(varKind, name, rhs)) + return name + } + + // `const` declaration (`var` in es5 mode) + const(nameOrPrefix: Name | string, rhs: SafeExpr, _constant?: boolean): Name { + return this._def(varKinds.const, nameOrPrefix, rhs, _constant) + } + + // `let` declaration with optional assignment (`var` in es5 mode) + let(nameOrPrefix: Name | string, rhs?: SafeExpr, _constant?: boolean): Name { + return this._def(varKinds.let, nameOrPrefix, rhs, _constant) + } + + // `var` declaration with optional assignment + var(nameOrPrefix: Name | string, rhs?: SafeExpr, _constant?: boolean): Name { + return this._def(varKinds.var, nameOrPrefix, rhs, _constant) + } + + // assignment code + assign(lhs: Code, rhs: SafeExpr, sideEffects?: boolean): CodeGen { + return this._leafNode(new Assign(lhs, rhs, sideEffects)) + } + + // `+=` code + add(lhs: Code, rhs: SafeExpr): CodeGen { + return this._leafNode(new AssignOp(lhs, operators.ADD, rhs)) + } + + // appends passed SafeExpr to code or executes Block + code(c: Block | SafeExpr): CodeGen { + if (typeof c == "function") c() + else if (c !== nil) this._leafNode(new AnyCode(c)) + return this + } + + // returns code for object literal for the passed argument list of key-value pairs + object(...keyValues: [Name | string, SafeExpr | string][]): _Code { + const code: CodeItem[] = ["{"] + for (const [key, value] of keyValues) { + if (code.length > 1) code.push(",") + code.push(key) + if (key !== value || this.opts.es5) { + code.push(":") + addCodeArg(code, value) + } + } + code.push("}") + return new _Code(code) + } + + // `if` clause (or statement if `thenBody` and, optionally, `elseBody` are passed) + if(condition: Code | boolean, thenBody?: Block, elseBody?: Block): CodeGen { + this._blockNode(new If(condition)) + + if (thenBody && elseBody) { + this.code(thenBody).else().code(elseBody).endIf() + } else if (thenBody) { + this.code(thenBody).endIf() + } else if (elseBody) { + throw new Error('CodeGen: "else" body without "then" body') + } + return this + } + + // `else if` clause - invalid without `if` or after `else` clauses + elseIf(condition: Code | boolean): CodeGen { + return this._elseNode(new If(condition)) + } + + // `else` clause - only valid after `if` or `else if` clauses + else(): CodeGen { + return this._elseNode(new Else()) + } + + // end `if` statement (needed if gen.if was used only with condition) + endIf(): CodeGen { + return this._endBlockNode(If, Else) + } + + private _for(node: For, forBody?: Block): CodeGen { + this._blockNode(node) + if (forBody) this.code(forBody).endFor() + return this + } + + // a generic `for` clause (or statement if `forBody` is passed) + for(iteration: Code, forBody?: Block): CodeGen { + return this._for(new ForLoop(iteration), forBody) + } + + // `for` statement for a range of values + forRange( + nameOrPrefix: Name | string, + from: SafeExpr, + to: SafeExpr, + forBody: (index: Name) => void, + varKind: Code = this.opts.es5 ? varKinds.var : varKinds.let + ): CodeGen { + const name = this._scope.toName(nameOrPrefix) + return this._for(new ForRange(varKind, name, from, to), () => forBody(name)) + } + + // `for-of` statement (in es5 mode replace with a normal for loop) + forOf( + nameOrPrefix: Name | string, + iterable: Code, + forBody: (item: Name) => void, + varKind: Code = varKinds.const + ): CodeGen { + const name = this._scope.toName(nameOrPrefix) + if (this.opts.es5) { + const arr = iterable instanceof Name ? iterable : this.var("_arr", iterable) + return this.forRange("_i", 0, _`${arr}.length`, (i) => { + this.var(name, _`${arr}[${i}]`) + forBody(name) + }) + } + return this._for(new ForIter("of", varKind, name, iterable), () => forBody(name)) + } + + // `for-in` statement. + // With option `ownProperties` replaced with a `for-of` loop for object keys + forIn( + nameOrPrefix: Name | string, + obj: Code, + forBody: (item: Name) => void, + varKind: Code = this.opts.es5 ? varKinds.var : varKinds.const + ): CodeGen { + if (this.opts.ownProperties) { + return this.forOf(nameOrPrefix, _`Object.keys(${obj})`, forBody) + } + const name = this._scope.toName(nameOrPrefix) + return this._for(new ForIter("in", varKind, name, obj), () => forBody(name)) + } + + // end `for` loop + endFor(): CodeGen { + return this._endBlockNode(For) + } + + // `label` statement + label(label: Name): CodeGen { + return this._leafNode(new Label(label)) + } + + // `break` statement + break(label?: Code): CodeGen { + return this._leafNode(new Break(label)) + } + + // `return` statement + return(value: Block | SafeExpr): CodeGen { + const node = new Return() + this._blockNode(node) + this.code(value) + if (node.nodes.length !== 1) throw new Error('CodeGen: "return" should have one node') + return this._endBlockNode(Return) + } + + // `try` statement + try(tryBody: Block, catchCode?: (e: Name) => void, finallyCode?: Block): CodeGen { + if (!catchCode && !finallyCode) throw new Error('CodeGen: "try" without "catch" and "finally"') + const node = new Try() + this._blockNode(node) + this.code(tryBody) + if (catchCode) { + const error = this.name("e") + this._currNode = node.catch = new Catch(error) + catchCode(error) + } + if (finallyCode) { + this._currNode = node.finally = new Finally() + this.code(finallyCode) + } + return this._endBlockNode(Catch, Finally) + } + + // `throw` statement + throw(error: Code): CodeGen { + return this._leafNode(new Throw(error)) + } + + // start self-balancing block + block(body?: Block, nodeCount?: number): CodeGen { + this._blockStarts.push(this._nodes.length) + if (body) this.code(body).endBlock(nodeCount) + return this + } + + // end the current self-balancing block + endBlock(nodeCount?: number): CodeGen { + const len = this._blockStarts.pop() + if (len === undefined) throw new Error("CodeGen: not in self-balancing block") + const toClose = this._nodes.length - len + if (toClose < 0 || (nodeCount !== undefined && toClose !== nodeCount)) { + throw new Error(`CodeGen: wrong number of nodes: ${toClose} vs ${nodeCount} expected`) + } + this._nodes.length = len + return this + } + + // `function` heading (or definition if funcBody is passed) + func(name: Name, args: Code = nil, async?: boolean, funcBody?: Block): CodeGen { + this._blockNode(new Func(name, args, async)) + if (funcBody) this.code(funcBody).endFunc() + return this + } + + // end function definition + endFunc(): CodeGen { + return this._endBlockNode(Func) + } + + optimize(n = 1): void { + while (n-- > 0) { + this._root.optimizeNodes() + this._root.optimizeNames(this._root.names, this._constants) + } + } + + private _leafNode(node: LeafNode): CodeGen { + this._currNode.nodes.push(node) + return this + } + + private _blockNode(node: StartBlockNode): void { + this._currNode.nodes.push(node) + this._nodes.push(node) + } + + private _endBlockNode(N1: EndBlockNodeType, N2?: EndBlockNodeType): CodeGen { + const n = this._currNode + if (n instanceof N1 || (N2 && n instanceof N2)) { + this._nodes.pop() + return this + } + throw new Error(`CodeGen: not in block "${N2 ? `${N1.kind}/${N2.kind}` : N1.kind}"`) + } + + private _elseNode(node: If | Else): CodeGen { + const n = this._currNode + if (!(n instanceof If)) { + throw new Error('CodeGen: "else" without "if"') + } + this._currNode = n.else = node + return this + } + + private get _root(): Root { + return this._nodes[0] as Root + } + + private get _currNode(): ParentNode { + const ns = this._nodes + return ns[ns.length - 1] + } + + private set _currNode(node: ParentNode) { + const ns = this._nodes + ns[ns.length - 1] = node + } + + // get nodeCount(): number { + // return this._root.count + // } +} + +function addNames(names: UsedNames, from: UsedNames): UsedNames { + for (const n in from) names[n] = (names[n] || 0) + (from[n] || 0) + return names +} + +function addExprNames(names: UsedNames, from: SafeExpr): UsedNames { + return from instanceof _CodeOrName ? addNames(names, from.names) : names +} + +function optimizeExpr(expr: T, names: UsedNames, constants: Constants): T +function optimizeExpr(expr: SafeExpr, names: UsedNames, constants: Constants): SafeExpr { + if (expr instanceof Name) return replaceName(expr) + if (!canOptimize(expr)) return expr + return new _Code( + expr._items.reduce((items: CodeItem[], c: SafeExpr | string) => { + if (c instanceof Name) c = replaceName(c) + if (c instanceof _Code) items.push(...c._items) + else items.push(c) + return items + }, []) + ) + + function replaceName(n: Name): SafeExpr { + const c = constants[n.str] + if (c === undefined || names[n.str] !== 1) return n + delete names[n.str] + return c + } + + function canOptimize(e: SafeExpr): e is _Code { + return ( + e instanceof _Code && + e._items.some( + (c) => c instanceof Name && names[c.str] === 1 && constants[c.str] !== undefined + ) + ) + } +} + +function subtractNames(names: UsedNames, from: UsedNames): void { + for (const n in from) names[n] = (names[n] || 0) - (from[n] || 0) +} + +export function not(x: T): T +export function not(x: Code | SafeExpr): Code | SafeExpr { + return typeof x == "boolean" || typeof x == "number" || x === null ? !x : _`!${par(x)}` +} + +const andCode = mappend(operators.AND) + +// boolean AND (&&) expression with the passed arguments +export function and(...args: Code[]): Code { + return args.reduce(andCode) +} + +const orCode = mappend(operators.OR) + +// boolean OR (||) expression with the passed arguments +export function or(...args: Code[]): Code { + return args.reduce(orCode) +} + +type MAppend = (x: Code, y: Code) => Code + +function mappend(op: Code): MAppend { + return (x, y) => (x === nil ? y : y === nil ? x : _`${par(x)} ${op} ${par(y)}`) +} + +function par(x: Code): Code { + return x instanceof Name ? x : _`(${x})` +} diff --git a/website/node_modules/ajv/lib/compile/codegen/scope.ts b/website/node_modules/ajv/lib/compile/codegen/scope.ts new file mode 100644 index 00000000..51199229 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/codegen/scope.ts @@ -0,0 +1,215 @@ +import {_, nil, Code, Name} from "./code" + +interface NameGroup { + prefix: string + index: number +} + +export interface NameValue { + ref: ValueReference // this is the reference to any value that can be referred to from generated code via `globals` var in the closure + key?: unknown // any key to identify a global to avoid duplicates, if not passed ref is used + code?: Code // this is the code creating the value needed for standalone code wit_out closure - can be a primitive value, function or import (`require`) +} + +export type ValueReference = unknown // possibly make CodeGen parameterized type on this type + +class ValueError extends Error { + readonly value?: NameValue + constructor(name: ValueScopeName) { + super(`CodeGen: "code" for ${name} not defined`) + this.value = name.value + } +} + +interface ScopeOptions { + prefixes?: Set + parent?: Scope +} + +interface ValueScopeOptions extends ScopeOptions { + scope: ScopeStore + es5?: boolean + lines?: boolean +} + +export type ScopeStore = Record + +type ScopeValues = { + [Prefix in string]?: Map +} + +export type ScopeValueSets = { + [Prefix in string]?: Set +} + +export enum UsedValueState { + Started, + Completed, +} + +export type UsedScopeValues = { + [Prefix in string]?: Map +} + +export const varKinds = { + const: new Name("const"), + let: new Name("let"), + var: new Name("var"), +} + +export class Scope { + protected readonly _names: {[Prefix in string]?: NameGroup} = {} + protected readonly _prefixes?: Set + protected readonly _parent?: Scope + + constructor({prefixes, parent}: ScopeOptions = {}) { + this._prefixes = prefixes + this._parent = parent + } + + toName(nameOrPrefix: Name | string): Name { + return nameOrPrefix instanceof Name ? nameOrPrefix : this.name(nameOrPrefix) + } + + name(prefix: string): Name { + return new Name(this._newName(prefix)) + } + + protected _newName(prefix: string): string { + const ng = this._names[prefix] || this._nameGroup(prefix) + return `${prefix}${ng.index++}` + } + + private _nameGroup(prefix: string): NameGroup { + if (this._parent?._prefixes?.has(prefix) || (this._prefixes && !this._prefixes.has(prefix))) { + throw new Error(`CodeGen: prefix "${prefix}" is not allowed in this scope`) + } + return (this._names[prefix] = {prefix, index: 0}) + } +} + +interface ScopePath { + property: string + itemIndex: number +} + +export class ValueScopeName extends Name { + readonly prefix: string + value?: NameValue + scopePath?: Code + + constructor(prefix: string, nameStr: string) { + super(nameStr) + this.prefix = prefix + } + + setValue(value: NameValue, {property, itemIndex}: ScopePath): void { + this.value = value + this.scopePath = _`.${new Name(property)}[${itemIndex}]` + } +} + +interface VSOptions extends ValueScopeOptions { + _n: Code +} + +const line = _`\n` + +export class ValueScope extends Scope { + protected readonly _values: ScopeValues = {} + protected readonly _scope: ScopeStore + readonly opts: VSOptions + + constructor(opts: ValueScopeOptions) { + super(opts) + this._scope = opts.scope + this.opts = {...opts, _n: opts.lines ? line : nil} + } + + get(): ScopeStore { + return this._scope + } + + name(prefix: string): ValueScopeName { + return new ValueScopeName(prefix, this._newName(prefix)) + } + + value(nameOrPrefix: ValueScopeName | string, value: NameValue): ValueScopeName { + if (value.ref === undefined) throw new Error("CodeGen: ref must be passed in value") + const name = this.toName(nameOrPrefix) as ValueScopeName + const {prefix} = name + const valueKey = value.key ?? value.ref + let vs = this._values[prefix] + if (vs) { + const _name = vs.get(valueKey) + if (_name) return _name + } else { + vs = this._values[prefix] = new Map() + } + vs.set(valueKey, name) + + const s = this._scope[prefix] || (this._scope[prefix] = []) + const itemIndex = s.length + s[itemIndex] = value.ref + name.setValue(value, {property: prefix, itemIndex}) + return name + } + + getValue(prefix: string, keyOrRef: unknown): ValueScopeName | undefined { + const vs = this._values[prefix] + if (!vs) return + return vs.get(keyOrRef) + } + + scopeRefs(scopeName: Name, values: ScopeValues | ScopeValueSets = this._values): Code { + return this._reduceValues(values, (name: ValueScopeName) => { + if (name.scopePath === undefined) throw new Error(`CodeGen: name "${name}" has no value`) + return _`${scopeName}${name.scopePath}` + }) + } + + scopeCode( + values: ScopeValues | ScopeValueSets = this._values, + usedValues?: UsedScopeValues, + getCode?: (n: ValueScopeName) => Code | undefined + ): Code { + return this._reduceValues( + values, + (name: ValueScopeName) => { + if (name.value === undefined) throw new Error(`CodeGen: name "${name}" has no value`) + return name.value.code + }, + usedValues, + getCode + ) + } + + private _reduceValues( + values: ScopeValues | ScopeValueSets, + valueCode: (n: ValueScopeName) => Code | undefined, + usedValues: UsedScopeValues = {}, + getCode?: (n: ValueScopeName) => Code | undefined + ): Code { + let code: Code = nil + for (const prefix in values) { + const vs = values[prefix] + if (!vs) continue + const nameSet = (usedValues[prefix] = usedValues[prefix] || new Map()) + vs.forEach((name: ValueScopeName) => { + if (nameSet.has(name)) return + nameSet.set(name, UsedValueState.Started) + let c = valueCode(name) + if (c) { + const def = this.opts.es5 ? varKinds.var : varKinds.const + code = _`${code}${def} ${name} = ${c};${this.opts._n}` + } else if ((c = getCode?.(name))) { + code = _`${code}${c}${this.opts._n}` + } else { + throw new ValueError(name) + } + nameSet.set(name, UsedValueState.Completed) + }) + } + return code + } +} diff --git a/website/node_modules/ajv/lib/compile/errors.ts b/website/node_modules/ajv/lib/compile/errors.ts new file mode 100644 index 00000000..18424a0f --- /dev/null +++ b/website/node_modules/ajv/lib/compile/errors.ts @@ -0,0 +1,184 @@ +import type {KeywordErrorCxt, KeywordErrorDefinition} from "../types" +import type {SchemaCxt} from "./index" +import {CodeGen, _, str, strConcat, Code, Name} from "./codegen" +import {SafeExpr} from "./codegen/code" +import {getErrorPath, Type} from "./util" +import N from "./names" + +export const keywordError: KeywordErrorDefinition = { + message: ({keyword}) => str`must pass "${keyword}" keyword validation`, +} + +export const keyword$DataError: KeywordErrorDefinition = { + message: ({keyword, schemaType}) => + schemaType + ? str`"${keyword}" keyword must be ${schemaType} ($data)` + : str`"${keyword}" keyword is invalid ($data)`, +} + +export interface ErrorPaths { + instancePath?: Code + schemaPath?: string + parentSchema?: boolean +} + +export function reportError( + cxt: KeywordErrorCxt, + error: KeywordErrorDefinition = keywordError, + errorPaths?: ErrorPaths, + overrideAllErrors?: boolean +): void { + const {it} = cxt + const {gen, compositeRule, allErrors} = it + const errObj = errorObjectCode(cxt, error, errorPaths) + if (overrideAllErrors ?? (compositeRule || allErrors)) { + addError(gen, errObj) + } else { + returnErrors(it, _`[${errObj}]`) + } +} + +export function reportExtraError( + cxt: KeywordErrorCxt, + error: KeywordErrorDefinition = keywordError, + errorPaths?: ErrorPaths +): void { + const {it} = cxt + const {gen, compositeRule, allErrors} = it + const errObj = errorObjectCode(cxt, error, errorPaths) + addError(gen, errObj) + if (!(compositeRule || allErrors)) { + returnErrors(it, N.vErrors) + } +} + +export function resetErrorsCount(gen: CodeGen, errsCount: Name): void { + gen.assign(N.errors, errsCount) + gen.if(_`${N.vErrors} !== null`, () => + gen.if( + errsCount, + () => gen.assign(_`${N.vErrors}.length`, errsCount), + () => gen.assign(N.vErrors, null) + ) + ) +} + +export function extendErrors({ + gen, + keyword, + schemaValue, + data, + errsCount, + it, +}: KeywordErrorCxt): void { + /* istanbul ignore if */ + if (errsCount === undefined) throw new Error("ajv implementation error") + const err = gen.name("err") + gen.forRange("i", errsCount, N.errors, (i) => { + gen.const(err, _`${N.vErrors}[${i}]`) + gen.if(_`${err}.instancePath === undefined`, () => + gen.assign(_`${err}.instancePath`, strConcat(N.instancePath, it.errorPath)) + ) + gen.assign(_`${err}.schemaPath`, str`${it.errSchemaPath}/${keyword}`) + if (it.opts.verbose) { + gen.assign(_`${err}.schema`, schemaValue) + gen.assign(_`${err}.data`, data) + } + }) +} + +function addError(gen: CodeGen, errObj: Code): void { + const err = gen.const("err", errObj) + gen.if( + _`${N.vErrors} === null`, + () => gen.assign(N.vErrors, _`[${err}]`), + _`${N.vErrors}.push(${err})` + ) + gen.code(_`${N.errors}++`) +} + +function returnErrors(it: SchemaCxt, errs: Code): void { + const {gen, validateName, schemaEnv} = it + if (schemaEnv.$async) { + gen.throw(_`new ${it.ValidationError as Name}(${errs})`) + } else { + gen.assign(_`${validateName}.errors`, errs) + gen.return(false) + } +} + +const E = { + keyword: new Name("keyword"), + schemaPath: new Name("schemaPath"), // also used in JTD errors + params: new Name("params"), + propertyName: new Name("propertyName"), + message: new Name("message"), + schema: new Name("schema"), + parentSchema: new Name("parentSchema"), +} + +function errorObjectCode( + cxt: KeywordErrorCxt, + error: KeywordErrorDefinition, + errorPaths?: ErrorPaths +): Code { + const {createErrors} = cxt.it + if (createErrors === false) return _`{}` + return errorObject(cxt, error, errorPaths) +} + +function errorObject( + cxt: KeywordErrorCxt, + error: KeywordErrorDefinition, + errorPaths: ErrorPaths = {} +): Code { + const {gen, it} = cxt + const keyValues: [Name, SafeExpr | string][] = [ + errorInstancePath(it, errorPaths), + errorSchemaPath(cxt, errorPaths), + ] + extraErrorProps(cxt, error, keyValues) + return gen.object(...keyValues) +} + +function errorInstancePath({errorPath}: SchemaCxt, {instancePath}: ErrorPaths): [Name, Code] { + const instPath = instancePath + ? str`${errorPath}${getErrorPath(instancePath, Type.Str)}` + : errorPath + return [N.instancePath, strConcat(N.instancePath, instPath)] +} + +function errorSchemaPath( + {keyword, it: {errSchemaPath}}: KeywordErrorCxt, + {schemaPath, parentSchema}: ErrorPaths +): [Name, string | Code] { + let schPath = parentSchema ? errSchemaPath : str`${errSchemaPath}/${keyword}` + if (schemaPath) { + schPath = str`${schPath}${getErrorPath(schemaPath, Type.Str)}` + } + return [E.schemaPath, schPath] +} + +function extraErrorProps( + cxt: KeywordErrorCxt, + {params, message}: KeywordErrorDefinition, + keyValues: [Name, SafeExpr | string][] +): void { + const {keyword, data, schemaValue, it} = cxt + const {opts, propertyName, topSchemaRef, schemaPath} = it + keyValues.push( + [E.keyword, keyword], + [E.params, typeof params == "function" ? params(cxt) : params || _`{}`] + ) + if (opts.messages) { + keyValues.push([E.message, typeof message == "function" ? message(cxt) : message]) + } + if (opts.verbose) { + keyValues.push( + [E.schema, schemaValue], + [E.parentSchema, _`${topSchemaRef}${schemaPath}`], + [N.data, data] + ) + } + if (propertyName) keyValues.push([E.propertyName, propertyName]) +} diff --git a/website/node_modules/ajv/lib/compile/index.ts b/website/node_modules/ajv/lib/compile/index.ts new file mode 100644 index 00000000..3dac2699 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/index.ts @@ -0,0 +1,324 @@ +import type { + AnySchema, + AnySchemaObject, + AnyValidateFunction, + AsyncValidateFunction, + EvaluatedProperties, + EvaluatedItems, +} from "../types" +import type Ajv from "../core" +import type {InstanceOptions} from "../core" +import {CodeGen, _, nil, stringify, Name, Code, ValueScopeName} from "./codegen" +import ValidationError from "../runtime/validation_error" +import N from "./names" +import {LocalRefs, getFullPath, _getFullPath, inlineRef, normalizeId, resolveUrl} from "./resolve" +import {schemaHasRulesButRef, unescapeFragment} from "./util" +import {validateFunctionCode} from "./validate" +import * as URI from "uri-js" +import {JSONType} from "./rules" + +export type SchemaRefs = { + [Ref in string]?: SchemaEnv | AnySchema +} + +export interface SchemaCxt { + readonly gen: CodeGen + readonly allErrors?: boolean // validation mode - whether to collect all errors or break on error + readonly data: Name // Name with reference to the current part of data instance + readonly parentData: Name // should be used in keywords modifying data + readonly parentDataProperty: Code | number // should be used in keywords modifying data + readonly dataNames: Name[] + readonly dataPathArr: (Code | number)[] + readonly dataLevel: number // the level of the currently validated data, + // it can be used to access both the property names and the data on all levels from the top. + dataTypes: JSONType[] // data types applied to the current part of data instance + definedProperties: Set // set of properties to keep track of for required checks + readonly topSchemaRef: Code + readonly validateName: Name + evaluated?: Name + readonly ValidationError?: Name + readonly schema: AnySchema // current schema object - equal to parentSchema passed via KeywordCxt + readonly schemaEnv: SchemaEnv + readonly rootId: string + baseId: string // the current schema base URI that should be used as the base for resolving URIs in references (\$ref) + readonly schemaPath: Code // the run-time expression that evaluates to the property name of the current schema + readonly errSchemaPath: string // this is actual string, should not be changed to Code + readonly errorPath: Code + readonly propertyName?: Name + readonly compositeRule?: boolean // true indicates that the current schema is inside the compound keyword, + // where failing some rule doesn't mean validation failure (`anyOf`, `oneOf`, `not`, `if`). + // This flag is used to determine whether you can return validation result immediately after any error in case the option `allErrors` is not `true. + // You only need to use it if you have many steps in your keywords and potentially can define multiple errors. + props?: EvaluatedProperties | Name // properties evaluated by this schema - used by parent schema or assigned to validation function + items?: EvaluatedItems | Name // last item evaluated by this schema - used by parent schema or assigned to validation function + jtdDiscriminator?: string + jtdMetadata?: boolean + readonly createErrors?: boolean + readonly opts: InstanceOptions // Ajv instance option. + readonly self: Ajv // current Ajv instance +} + +export interface SchemaObjCxt extends SchemaCxt { + readonly schema: AnySchemaObject +} +interface SchemaEnvArgs { + readonly schema: AnySchema + readonly schemaId?: "$id" | "id" + readonly root?: SchemaEnv + readonly baseId?: string + readonly schemaPath?: string + readonly localRefs?: LocalRefs + readonly meta?: boolean +} + +export class SchemaEnv implements SchemaEnvArgs { + readonly schema: AnySchema + readonly schemaId?: "$id" | "id" + readonly root: SchemaEnv + baseId: string // TODO possibly, it should be readonly + schemaPath?: string + localRefs?: LocalRefs + readonly meta?: boolean + readonly $async?: boolean // true if the current schema is asynchronous. + readonly refs: SchemaRefs = {} + readonly dynamicAnchors: {[Ref in string]?: true} = {} + validate?: AnyValidateFunction + validateName?: ValueScopeName + serialize?: (data: unknown) => string + serializeName?: ValueScopeName + parse?: (data: string) => unknown + parseName?: ValueScopeName + + constructor(env: SchemaEnvArgs) { + let schema: AnySchemaObject | undefined + if (typeof env.schema == "object") schema = env.schema + this.schema = env.schema + this.schemaId = env.schemaId + this.root = env.root || this + this.baseId = env.baseId ?? normalizeId(schema?.[env.schemaId || "$id"]) + this.schemaPath = env.schemaPath + this.localRefs = env.localRefs + this.meta = env.meta + this.$async = schema?.$async + this.refs = {} + } +} + +// let codeSize = 0 +// let nodeCount = 0 + +// Compiles schema in SchemaEnv +export function compileSchema(this: Ajv, sch: SchemaEnv): SchemaEnv { + // TODO refactor - remove compilations + const _sch = getCompilingSchema.call(this, sch) + if (_sch) return _sch + const rootId = getFullPath(this.opts.uriResolver, sch.root.baseId) // TODO if getFullPath removed 1 tests fails + const {es5, lines} = this.opts.code + const {ownProperties} = this.opts + const gen = new CodeGen(this.scope, {es5, lines, ownProperties}) + let _ValidationError + if (sch.$async) { + _ValidationError = gen.scopeValue("Error", { + ref: ValidationError, + code: _`require("ajv/dist/runtime/validation_error").default`, + }) + } + + const validateName = gen.scopeName("validate") + sch.validateName = validateName + + const schemaCxt: SchemaCxt = { + gen, + allErrors: this.opts.allErrors, + data: N.data, + parentData: N.parentData, + parentDataProperty: N.parentDataProperty, + dataNames: [N.data], + dataPathArr: [nil], // TODO can its length be used as dataLevel if nil is removed? + dataLevel: 0, + dataTypes: [], + definedProperties: new Set(), + topSchemaRef: gen.scopeValue( + "schema", + this.opts.code.source === true + ? {ref: sch.schema, code: stringify(sch.schema)} + : {ref: sch.schema} + ), + validateName, + ValidationError: _ValidationError, + schema: sch.schema, + schemaEnv: sch, + rootId, + baseId: sch.baseId || rootId, + schemaPath: nil, + errSchemaPath: sch.schemaPath || (this.opts.jtd ? "" : "#"), + errorPath: _`""`, + opts: this.opts, + self: this, + } + + let sourceCode: string | undefined + try { + this._compilations.add(sch) + validateFunctionCode(schemaCxt) + gen.optimize(this.opts.code.optimize) + // gen.optimize(1) + const validateCode = gen.toString() + sourceCode = `${gen.scopeRefs(N.scope)}return ${validateCode}` + // console.log((codeSize += sourceCode.length), (nodeCount += gen.nodeCount)) + if (this.opts.code.process) sourceCode = this.opts.code.process(sourceCode, sch) + // console.log("\n\n\n *** \n", sourceCode) + const makeValidate = new Function(`${N.self}`, `${N.scope}`, sourceCode) + const validate: AnyValidateFunction = makeValidate(this, this.scope.get()) + this.scope.value(validateName, {ref: validate}) + + validate.errors = null + validate.schema = sch.schema + validate.schemaEnv = sch + if (sch.$async) (validate as AsyncValidateFunction).$async = true + if (this.opts.code.source === true) { + validate.source = {validateName, validateCode, scopeValues: gen._values} + } + if (this.opts.unevaluated) { + const {props, items} = schemaCxt + validate.evaluated = { + props: props instanceof Name ? undefined : props, + items: items instanceof Name ? undefined : items, + dynamicProps: props instanceof Name, + dynamicItems: items instanceof Name, + } + if (validate.source) validate.source.evaluated = stringify(validate.evaluated) + } + sch.validate = validate + return sch + } catch (e) { + delete sch.validate + delete sch.validateName + if (sourceCode) this.logger.error("Error compiling schema, function code:", sourceCode) + // console.log("\n\n\n *** \n", sourceCode, this.opts) + throw e + } finally { + this._compilations.delete(sch) + } +} + +export function resolveRef( + this: Ajv, + root: SchemaEnv, + baseId: string, + ref: string +): AnySchema | SchemaEnv | undefined { + ref = resolveUrl(this.opts.uriResolver, baseId, ref) + const schOrFunc = root.refs[ref] + if (schOrFunc) return schOrFunc + + let _sch = resolve.call(this, root, ref) + if (_sch === undefined) { + const schema = root.localRefs?.[ref] // TODO maybe localRefs should hold SchemaEnv + const {schemaId} = this.opts + if (schema) _sch = new SchemaEnv({schema, schemaId, root, baseId}) + } + + if (_sch === undefined) return + return (root.refs[ref] = inlineOrCompile.call(this, _sch)) +} + +function inlineOrCompile(this: Ajv, sch: SchemaEnv): AnySchema | SchemaEnv { + if (inlineRef(sch.schema, this.opts.inlineRefs)) return sch.schema + return sch.validate ? sch : compileSchema.call(this, sch) +} + +// Index of schema compilation in the currently compiled list +export function getCompilingSchema(this: Ajv, schEnv: SchemaEnv): SchemaEnv | void { + for (const sch of this._compilations) { + if (sameSchemaEnv(sch, schEnv)) return sch + } +} + +function sameSchemaEnv(s1: SchemaEnv, s2: SchemaEnv): boolean { + return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId +} + +// resolve and compile the references ($ref) +// TODO returns AnySchemaObject (if the schema can be inlined) or validation function +function resolve( + this: Ajv, + root: SchemaEnv, // information about the root schema for the current schema + ref: string // reference to resolve +): SchemaEnv | undefined { + let sch + while (typeof (sch = this.refs[ref]) == "string") ref = sch + return sch || this.schemas[ref] || resolveSchema.call(this, root, ref) +} + +// Resolve schema, its root and baseId +export function resolveSchema( + this: Ajv, + root: SchemaEnv, // root object with properties schema, refs TODO below SchemaEnv is assigned to it + ref: string // reference to resolve +): SchemaEnv | undefined { + const p = this.opts.uriResolver.parse(ref) + const refPath = _getFullPath(this.opts.uriResolver, p) + let baseId = getFullPath(this.opts.uriResolver, root.baseId, undefined) + // TODO `Object.keys(root.schema).length > 0` should not be needed - but removing breaks 2 tests + if (Object.keys(root.schema).length > 0 && refPath === baseId) { + return getJsonPointer.call(this, p, root) + } + + const id = normalizeId(refPath) + const schOrRef = this.refs[id] || this.schemas[id] + if (typeof schOrRef == "string") { + const sch = resolveSchema.call(this, root, schOrRef) + if (typeof sch?.schema !== "object") return + return getJsonPointer.call(this, p, sch) + } + + if (typeof schOrRef?.schema !== "object") return + if (!schOrRef.validate) compileSchema.call(this, schOrRef) + if (id === normalizeId(ref)) { + const {schema} = schOrRef + const {schemaId} = this.opts + const schId = schema[schemaId] + if (schId) baseId = resolveUrl(this.opts.uriResolver, baseId, schId) + return new SchemaEnv({schema, schemaId, root, baseId}) + } + return getJsonPointer.call(this, p, schOrRef) +} + +const PREVENT_SCOPE_CHANGE = new Set([ + "properties", + "patternProperties", + "enum", + "dependencies", + "definitions", +]) + +function getJsonPointer( + this: Ajv, + parsedRef: URI.URIComponents, + {baseId, schema, root}: SchemaEnv +): SchemaEnv | undefined { + if (parsedRef.fragment?.[0] !== "/") return + for (const part of parsedRef.fragment.slice(1).split("/")) { + if (typeof schema === "boolean") return + const partSchema = schema[unescapeFragment(part)] + if (partSchema === undefined) return + schema = partSchema + // TODO PREVENT_SCOPE_CHANGE could be defined in keyword def? + const schId = typeof schema === "object" && schema[this.opts.schemaId] + if (!PREVENT_SCOPE_CHANGE.has(part) && schId) { + baseId = resolveUrl(this.opts.uriResolver, baseId, schId) + } + } + let env: SchemaEnv | undefined + if (typeof schema != "boolean" && schema.$ref && !schemaHasRulesButRef(schema, this.RULES)) { + const $ref = resolveUrl(this.opts.uriResolver, baseId, schema.$ref) + env = resolveSchema.call(this, root, $ref) + } + // even though resolution failed we need to return SchemaEnv to throw exception + // so that compileAsync loads missing schema. + const {schemaId} = this.opts + env = env || new SchemaEnv({schema, schemaId, root, baseId}) + if (env.schema !== env.root.schema) return env + return undefined +} diff --git a/website/node_modules/ajv/lib/compile/jtd/parse.ts b/website/node_modules/ajv/lib/compile/jtd/parse.ts new file mode 100644 index 00000000..a0141c77 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/jtd/parse.ts @@ -0,0 +1,411 @@ +import type Ajv from "../../core" +import type {SchemaObject} from "../../types" +import {jtdForms, JTDForm, SchemaObjectMap} from "./types" +import {SchemaEnv, getCompilingSchema} from ".." +import {_, str, and, or, nil, not, CodeGen, Code, Name, SafeExpr} from "../codegen" +import MissingRefError from "../ref_error" +import N from "../names" +import {hasPropFunc} from "../../vocabularies/code" +import {hasRef} from "../../vocabularies/jtd/ref" +import {intRange, IntType} from "../../vocabularies/jtd/type" +import {parseJson, parseJsonNumber, parseJsonString} from "../../runtime/parseJson" +import {useFunc} from "../util" +import validTimestamp from "../../runtime/timestamp" + +type GenParse = (cxt: ParseCxt) => void + +const genParse: {[F in JTDForm]: GenParse} = { + elements: parseElements, + values: parseValues, + discriminator: parseDiscriminator, + properties: parseProperties, + optionalProperties: parseProperties, + enum: parseEnum, + type: parseType, + ref: parseRef, +} + +interface ParseCxt { + readonly gen: CodeGen + readonly self: Ajv // current Ajv instance + readonly schemaEnv: SchemaEnv + readonly definitions: SchemaObjectMap + schema: SchemaObject + data: Code + parseName: Name + char: Name +} + +export default function compileParser( + this: Ajv, + sch: SchemaEnv, + definitions: SchemaObjectMap +): SchemaEnv { + const _sch = getCompilingSchema.call(this, sch) + if (_sch) return _sch + const {es5, lines} = this.opts.code + const {ownProperties} = this.opts + const gen = new CodeGen(this.scope, {es5, lines, ownProperties}) + const parseName = gen.scopeName("parse") + const cxt: ParseCxt = { + self: this, + gen, + schema: sch.schema as SchemaObject, + schemaEnv: sch, + definitions, + data: N.data, + parseName, + char: gen.name("c"), + } + + let sourceCode: string | undefined + try { + this._compilations.add(sch) + sch.parseName = parseName + parserFunction(cxt) + gen.optimize(this.opts.code.optimize) + const parseFuncCode = gen.toString() + sourceCode = `${gen.scopeRefs(N.scope)}return ${parseFuncCode}` + const makeParse = new Function(`${N.scope}`, sourceCode) + const parse: (json: string) => unknown = makeParse(this.scope.get()) + this.scope.value(parseName, {ref: parse}) + sch.parse = parse + } catch (e) { + if (sourceCode) this.logger.error("Error compiling parser, function code:", sourceCode) + delete sch.parse + delete sch.parseName + throw e + } finally { + this._compilations.delete(sch) + } + return sch +} + +const undef = _`undefined` + +function parserFunction(cxt: ParseCxt): void { + const {gen, parseName, char} = cxt + gen.func(parseName, _`${N.json}, ${N.jsonPos}, ${N.jsonPart}`, false, () => { + gen.let(N.data) + gen.let(char) + gen.assign(_`${parseName}.message`, undef) + gen.assign(_`${parseName}.position`, undef) + gen.assign(N.jsonPos, _`${N.jsonPos} || 0`) + gen.const(N.jsonLen, _`${N.json}.length`) + parseCode(cxt) + skipWhitespace(cxt) + gen.if(N.jsonPart, () => { + gen.assign(_`${parseName}.position`, N.jsonPos) + gen.return(N.data) + }) + gen.if(_`${N.jsonPos} === ${N.jsonLen}`, () => gen.return(N.data)) + jsonSyntaxError(cxt) + }) +} + +function parseCode(cxt: ParseCxt): void { + let form: JTDForm | undefined + for (const key of jtdForms) { + if (key in cxt.schema) { + form = key + break + } + } + if (form) parseNullable(cxt, genParse[form]) + else parseEmpty(cxt) +} + +const parseBoolean = parseBooleanToken(true, parseBooleanToken(false, jsonSyntaxError)) + +function parseNullable(cxt: ParseCxt, parseForm: GenParse): void { + const {gen, schema, data} = cxt + if (!schema.nullable) return parseForm(cxt) + tryParseToken(cxt, "null", parseForm, () => gen.assign(data, null)) +} + +function parseElements(cxt: ParseCxt): void { + const {gen, schema, data} = cxt + parseToken(cxt, "[") + const ix = gen.let("i", 0) + gen.assign(data, _`[]`) + parseItems(cxt, "]", () => { + const el = gen.let("el") + parseCode({...cxt, schema: schema.elements, data: el}) + gen.assign(_`${data}[${ix}++]`, el) + }) +} + +function parseValues(cxt: ParseCxt): void { + const {gen, schema, data} = cxt + parseToken(cxt, "{") + gen.assign(data, _`{}`) + parseItems(cxt, "}", () => parseKeyValue(cxt, schema.values)) +} + +function parseItems(cxt: ParseCxt, endToken: string, block: () => void): void { + tryParseItems(cxt, endToken, block) + parseToken(cxt, endToken) +} + +function tryParseItems(cxt: ParseCxt, endToken: string, block: () => void): void { + const {gen} = cxt + gen.for(_`;${N.jsonPos}<${N.jsonLen} && ${jsonSlice(1)}!==${endToken};`, () => { + block() + tryParseToken(cxt, ",", () => gen.break(), hasItem) + }) + + function hasItem(): void { + tryParseToken(cxt, endToken, () => {}, jsonSyntaxError) + } +} + +function parseKeyValue(cxt: ParseCxt, schema: SchemaObject): void { + const {gen} = cxt + const key = gen.let("key") + parseString({...cxt, data: key}) + parseToken(cxt, ":") + parsePropertyValue(cxt, key, schema) +} + +function parseDiscriminator(cxt: ParseCxt): void { + const {gen, data, schema} = cxt + const {discriminator, mapping} = schema + parseToken(cxt, "{") + gen.assign(data, _`{}`) + const startPos = gen.const("pos", N.jsonPos) + const value = gen.let("value") + const tag = gen.let("tag") + tryParseItems(cxt, "}", () => { + const key = gen.let("key") + parseString({...cxt, data: key}) + parseToken(cxt, ":") + gen.if( + _`${key} === ${discriminator}`, + () => { + parseString({...cxt, data: tag}) + gen.assign(_`${data}[${key}]`, tag) + gen.break() + }, + () => parseEmpty({...cxt, data: value}) // can be discarded/skipped + ) + }) + gen.assign(N.jsonPos, startPos) + gen.if(_`${tag} === undefined`) + parsingError(cxt, str`discriminator tag not found`) + for (const tagValue in mapping) { + gen.elseIf(_`${tag} === ${tagValue}`) + parseSchemaProperties({...cxt, schema: mapping[tagValue]}, discriminator) + } + gen.else() + parsingError(cxt, str`discriminator value not in schema`) + gen.endIf() +} + +function parseProperties(cxt: ParseCxt): void { + const {gen, data} = cxt + parseToken(cxt, "{") + gen.assign(data, _`{}`) + parseSchemaProperties(cxt) +} + +function parseSchemaProperties(cxt: ParseCxt, discriminator?: string): void { + const {gen, schema, data} = cxt + const {properties, optionalProperties, additionalProperties} = schema + parseItems(cxt, "}", () => { + const key = gen.let("key") + parseString({...cxt, data: key}) + parseToken(cxt, ":") + gen.if(false) + parseDefinedProperty(cxt, key, properties) + parseDefinedProperty(cxt, key, optionalProperties) + if (discriminator) { + gen.elseIf(_`${key} === ${discriminator}`) + const tag = gen.let("tag") + parseString({...cxt, data: tag}) // can be discarded, it is already assigned + } + gen.else() + if (additionalProperties) { + parseEmpty({...cxt, data: _`${data}[${key}]`}) + } else { + parsingError(cxt, str`property ${key} not allowed`) + } + gen.endIf() + }) + if (properties) { + const hasProp = hasPropFunc(gen) + const allProps: Code = and( + ...Object.keys(properties).map((p): Code => _`${hasProp}.call(${data}, ${p})`) + ) + gen.if(not(allProps), () => parsingError(cxt, str`missing required properties`)) + } +} + +function parseDefinedProperty(cxt: ParseCxt, key: Name, schemas: SchemaObjectMap = {}): void { + const {gen} = cxt + for (const prop in schemas) { + gen.elseIf(_`${key} === ${prop}`) + parsePropertyValue(cxt, key, schemas[prop] as SchemaObject) + } +} + +function parsePropertyValue(cxt: ParseCxt, key: Name, schema: SchemaObject): void { + parseCode({...cxt, schema, data: _`${cxt.data}[${key}]`}) +} + +function parseType(cxt: ParseCxt): void { + const {gen, schema, data, self} = cxt + switch (schema.type) { + case "boolean": + parseBoolean(cxt) + break + case "string": + parseString(cxt) + break + case "timestamp": { + parseString(cxt) + const vts = useFunc(gen, validTimestamp) + const {allowDate, parseDate} = self.opts + const notValid = allowDate ? _`!${vts}(${data}, true)` : _`!${vts}(${data})` + const fail: Code = parseDate + ? or(notValid, _`(${data} = new Date(${data}), false)`, _`isNaN(${data}.valueOf())`) + : notValid + gen.if(fail, () => parsingError(cxt, str`invalid timestamp`)) + break + } + case "float32": + case "float64": + parseNumber(cxt) + break + default: { + const t = schema.type as IntType + if (!self.opts.int32range && (t === "int32" || t === "uint32")) { + parseNumber(cxt, 16) // 2 ** 53 - max safe integer + if (t === "uint32") { + gen.if(_`${data} < 0`, () => parsingError(cxt, str`integer out of range`)) + } + } else { + const [min, max, maxDigits] = intRange[t] + parseNumber(cxt, maxDigits) + gen.if(_`${data} < ${min} || ${data} > ${max}`, () => + parsingError(cxt, str`integer out of range`) + ) + } + } + } +} + +function parseString(cxt: ParseCxt): void { + parseToken(cxt, '"') + parseWith(cxt, parseJsonString) +} + +function parseEnum(cxt: ParseCxt): void { + const {gen, data, schema} = cxt + const enumSch = schema.enum + parseToken(cxt, '"') + // TODO loopEnum + gen.if(false) + for (const value of enumSch) { + const valueStr = JSON.stringify(value).slice(1) // remove starting quote + gen.elseIf(_`${jsonSlice(valueStr.length)} === ${valueStr}`) + gen.assign(data, str`${value}`) + gen.add(N.jsonPos, valueStr.length) + } + gen.else() + jsonSyntaxError(cxt) + gen.endIf() +} + +function parseNumber(cxt: ParseCxt, maxDigits?: number): void { + const {gen} = cxt + skipWhitespace(cxt) + gen.if( + _`"-0123456789".indexOf(${jsonSlice(1)}) < 0`, + () => jsonSyntaxError(cxt), + () => parseWith(cxt, parseJsonNumber, maxDigits) + ) +} + +function parseBooleanToken(bool: boolean, fail: GenParse): GenParse { + return (cxt) => { + const {gen, data} = cxt + tryParseToken( + cxt, + `${bool}`, + () => fail(cxt), + () => gen.assign(data, bool) + ) + } +} + +function parseRef(cxt: ParseCxt): void { + const {gen, self, definitions, schema, schemaEnv} = cxt + const {ref} = schema + const refSchema = definitions[ref] + if (!refSchema) throw new MissingRefError(self.opts.uriResolver, "", ref, `No definition ${ref}`) + if (!hasRef(refSchema)) return parseCode({...cxt, schema: refSchema}) + const {root} = schemaEnv + const sch = compileParser.call(self, new SchemaEnv({schema: refSchema, root}), definitions) + partialParse(cxt, getParser(gen, sch), true) +} + +function getParser(gen: CodeGen, sch: SchemaEnv): Code { + return sch.parse + ? gen.scopeValue("parse", {ref: sch.parse}) + : _`${gen.scopeValue("wrapper", {ref: sch})}.parse` +} + +function parseEmpty(cxt: ParseCxt): void { + parseWith(cxt, parseJson) +} + +function parseWith(cxt: ParseCxt, parseFunc: {code: string}, args?: SafeExpr): void { + partialParse(cxt, useFunc(cxt.gen, parseFunc), args) +} + +function partialParse(cxt: ParseCxt, parseFunc: Name, args?: SafeExpr): void { + const {gen, data} = cxt + gen.assign(data, _`${parseFunc}(${N.json}, ${N.jsonPos}${args ? _`, ${args}` : nil})`) + gen.assign(N.jsonPos, _`${parseFunc}.position`) + gen.if(_`${data} === undefined`, () => parsingError(cxt, _`${parseFunc}.message`)) +} + +function parseToken(cxt: ParseCxt, tok: string): void { + tryParseToken(cxt, tok, jsonSyntaxError) +} + +function tryParseToken(cxt: ParseCxt, tok: string, fail: GenParse, success?: GenParse): void { + const {gen} = cxt + const n = tok.length + skipWhitespace(cxt) + gen.if( + _`${jsonSlice(n)} === ${tok}`, + () => { + gen.add(N.jsonPos, n) + success?.(cxt) + }, + () => fail(cxt) + ) +} + +function skipWhitespace({gen, char: c}: ParseCxt): void { + gen.code( + _`while((${c}=${N.json}[${N.jsonPos}],${c}===" "||${c}==="\\n"||${c}==="\\r"||${c}==="\\t"))${N.jsonPos}++;` + ) +} + +function jsonSlice(len: number | Name): Code { + return len === 1 + ? _`${N.json}[${N.jsonPos}]` + : _`${N.json}.slice(${N.jsonPos}, ${N.jsonPos}+${len})` +} + +function jsonSyntaxError(cxt: ParseCxt): void { + parsingError(cxt, _`"unexpected token " + ${N.json}[${N.jsonPos}]`) +} + +function parsingError({gen, parseName}: ParseCxt, msg: Code): void { + gen.assign(_`${parseName}.message`, msg) + gen.assign(_`${parseName}.position`, N.jsonPos) + gen.return(undef) +} diff --git a/website/node_modules/ajv/lib/compile/jtd/serialize.ts b/website/node_modules/ajv/lib/compile/jtd/serialize.ts new file mode 100644 index 00000000..1d228826 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/jtd/serialize.ts @@ -0,0 +1,266 @@ +import type Ajv from "../../core" +import type {SchemaObject} from "../../types" +import {jtdForms, JTDForm, SchemaObjectMap} from "./types" +import {SchemaEnv, getCompilingSchema} from ".." +import {_, str, and, getProperty, CodeGen, Code, Name} from "../codegen" +import MissingRefError from "../ref_error" +import N from "../names" +import {isOwnProperty} from "../../vocabularies/code" +import {hasRef} from "../../vocabularies/jtd/ref" +import {useFunc} from "../util" +import quote from "../../runtime/quote" + +const genSerialize: {[F in JTDForm]: (cxt: SerializeCxt) => void} = { + elements: serializeElements, + values: serializeValues, + discriminator: serializeDiscriminator, + properties: serializeProperties, + optionalProperties: serializeProperties, + enum: serializeString, + type: serializeType, + ref: serializeRef, +} + +interface SerializeCxt { + readonly gen: CodeGen + readonly self: Ajv // current Ajv instance + readonly schemaEnv: SchemaEnv + readonly definitions: SchemaObjectMap + schema: SchemaObject + data: Code +} + +export default function compileSerializer( + this: Ajv, + sch: SchemaEnv, + definitions: SchemaObjectMap +): SchemaEnv { + const _sch = getCompilingSchema.call(this, sch) + if (_sch) return _sch + const {es5, lines} = this.opts.code + const {ownProperties} = this.opts + const gen = new CodeGen(this.scope, {es5, lines, ownProperties}) + const serializeName = gen.scopeName("serialize") + const cxt: SerializeCxt = { + self: this, + gen, + schema: sch.schema as SchemaObject, + schemaEnv: sch, + definitions, + data: N.data, + } + + let sourceCode: string | undefined + try { + this._compilations.add(sch) + sch.serializeName = serializeName + gen.func(serializeName, N.data, false, () => { + gen.let(N.json, str``) + serializeCode(cxt) + gen.return(N.json) + }) + gen.optimize(this.opts.code.optimize) + const serializeFuncCode = gen.toString() + sourceCode = `${gen.scopeRefs(N.scope)}return ${serializeFuncCode}` + const makeSerialize = new Function(`${N.scope}`, sourceCode) + const serialize: (data: unknown) => string = makeSerialize(this.scope.get()) + this.scope.value(serializeName, {ref: serialize}) + sch.serialize = serialize + } catch (e) { + if (sourceCode) this.logger.error("Error compiling serializer, function code:", sourceCode) + delete sch.serialize + delete sch.serializeName + throw e + } finally { + this._compilations.delete(sch) + } + return sch +} + +function serializeCode(cxt: SerializeCxt): void { + let form: JTDForm | undefined + for (const key of jtdForms) { + if (key in cxt.schema) { + form = key + break + } + } + serializeNullable(cxt, form ? genSerialize[form] : serializeEmpty) +} + +function serializeNullable(cxt: SerializeCxt, serializeForm: (_cxt: SerializeCxt) => void): void { + const {gen, schema, data} = cxt + if (!schema.nullable) return serializeForm(cxt) + gen.if( + _`${data} === undefined || ${data} === null`, + () => gen.add(N.json, _`"null"`), + () => serializeForm(cxt) + ) +} + +function serializeElements(cxt: SerializeCxt): void { + const {gen, schema, data} = cxt + gen.add(N.json, str`[`) + const first = gen.let("first", true) + gen.forOf("el", data, (el) => { + addComma(cxt, first) + serializeCode({...cxt, schema: schema.elements, data: el}) + }) + gen.add(N.json, str`]`) +} + +function serializeValues(cxt: SerializeCxt): void { + const {gen, schema, data} = cxt + gen.add(N.json, str`{`) + const first = gen.let("first", true) + gen.forIn("key", data, (key) => serializeKeyValue(cxt, key, schema.values, first)) + gen.add(N.json, str`}`) +} + +function serializeKeyValue(cxt: SerializeCxt, key: Name, schema: SchemaObject, first?: Name): void { + const {gen, data} = cxt + addComma(cxt, first) + serializeString({...cxt, data: key}) + gen.add(N.json, str`:`) + const value = gen.const("value", _`${data}${getProperty(key)}`) + serializeCode({...cxt, schema, data: value}) +} + +function serializeDiscriminator(cxt: SerializeCxt): void { + const {gen, schema, data} = cxt + const {discriminator} = schema + gen.add(N.json, str`{${JSON.stringify(discriminator)}:`) + const tag = gen.const("tag", _`${data}${getProperty(discriminator)}`) + serializeString({...cxt, data: tag}) + gen.if(false) + for (const tagValue in schema.mapping) { + gen.elseIf(_`${tag} === ${tagValue}`) + const sch = schema.mapping[tagValue] + serializeSchemaProperties({...cxt, schema: sch}, discriminator) + } + gen.endIf() + gen.add(N.json, str`}`) +} + +function serializeProperties(cxt: SerializeCxt): void { + const {gen} = cxt + gen.add(N.json, str`{`) + serializeSchemaProperties(cxt) + gen.add(N.json, str`}`) +} + +function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): void { + const {gen, schema, data} = cxt + const {properties, optionalProperties} = schema + const props = keys(properties) + const optProps = keys(optionalProperties) + const allProps = allProperties(props.concat(optProps)) + let first = !discriminator + let firstProp: Name | undefined + + for (const key of props) { + if (first) first = false + else gen.add(N.json, str`,`) + serializeProperty(key, properties[key], keyValue(key)) + } + if (first) firstProp = gen.let("first", true) + for (const key of optProps) { + const value = keyValue(key) + gen.if(and(_`${value} !== undefined`, isOwnProperty(gen, data, key)), () => { + addComma(cxt, firstProp) + serializeProperty(key, optionalProperties[key], value) + }) + } + if (schema.additionalProperties) { + gen.forIn("key", data, (key) => + gen.if(isAdditional(key, allProps), () => serializeKeyValue(cxt, key, {}, firstProp)) + ) + } + + function keys(ps?: SchemaObjectMap): string[] { + return ps ? Object.keys(ps) : [] + } + + function allProperties(ps: string[]): string[] { + if (discriminator) ps.push(discriminator) + if (new Set(ps).size !== ps.length) { + throw new Error("JTD: properties/optionalProperties/disciminator overlap") + } + return ps + } + + function keyValue(key: string): Name { + return gen.const("value", _`${data}${getProperty(key)}`) + } + + function serializeProperty(key: string, propSchema: SchemaObject, value: Name): void { + gen.add(N.json, str`${JSON.stringify(key)}:`) + serializeCode({...cxt, schema: propSchema, data: value}) + } + + function isAdditional(key: Name, ps: string[]): Code | true { + return ps.length ? and(...ps.map((p) => _`${key} !== ${p}`)) : true + } +} + +function serializeType(cxt: SerializeCxt): void { + const {gen, schema, data} = cxt + switch (schema.type) { + case "boolean": + gen.add(N.json, _`${data} ? "true" : "false"`) + break + case "string": + serializeString(cxt) + break + case "timestamp": + gen.if( + _`${data} instanceof Date`, + () => gen.add(N.json, _`'"' + ${data}.toISOString() + '"'`), + () => serializeString(cxt) + ) + break + default: + serializeNumber(cxt) + } +} + +function serializeString({gen, data}: SerializeCxt): void { + gen.add(N.json, _`${useFunc(gen, quote)}(${data})`) +} + +function serializeNumber({gen, data}: SerializeCxt): void { + gen.add(N.json, _`"" + ${data}`) +} + +function serializeRef(cxt: SerializeCxt): void { + const {gen, self, data, definitions, schema, schemaEnv} = cxt + const {ref} = schema + const refSchema = definitions[ref] + if (!refSchema) throw new MissingRefError(self.opts.uriResolver, "", ref, `No definition ${ref}`) + if (!hasRef(refSchema)) return serializeCode({...cxt, schema: refSchema}) + const {root} = schemaEnv + const sch = compileSerializer.call(self, new SchemaEnv({schema: refSchema, root}), definitions) + gen.add(N.json, _`${getSerialize(gen, sch)}(${data})`) +} + +function getSerialize(gen: CodeGen, sch: SchemaEnv): Code { + return sch.serialize + ? gen.scopeValue("serialize", {ref: sch.serialize}) + : _`${gen.scopeValue("wrapper", {ref: sch})}.serialize` +} + +function serializeEmpty({gen, data}: SerializeCxt): void { + gen.add(N.json, _`JSON.stringify(${data})`) +} + +function addComma({gen}: SerializeCxt, first?: Name): void { + if (first) { + gen.if( + first, + () => gen.assign(first, false), + () => gen.add(N.json, str`,`) + ) + } else { + gen.add(N.json, str`,`) + } +} diff --git a/website/node_modules/ajv/lib/compile/jtd/types.ts b/website/node_modules/ajv/lib/compile/jtd/types.ts new file mode 100644 index 00000000..7f361957 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/jtd/types.ts @@ -0,0 +1,16 @@ +import type {SchemaObject} from "../../types" + +export type SchemaObjectMap = {[Ref in string]?: SchemaObject} + +export const jtdForms = [ + "elements", + "values", + "discriminator", + "properties", + "optionalProperties", + "enum", + "type", + "ref", +] as const + +export type JTDForm = typeof jtdForms[number] diff --git a/website/node_modules/ajv/lib/compile/names.ts b/website/node_modules/ajv/lib/compile/names.ts new file mode 100644 index 00000000..b4b242e1 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/names.ts @@ -0,0 +1,27 @@ +import {Name} from "./codegen" + +const names = { + // validation function arguments + data: new Name("data"), // data passed to validation function + // args passed from referencing schema + valCxt: new Name("valCxt"), // validation/data context - should not be used directly, it is destructured to the names below + instancePath: new Name("instancePath"), + parentData: new Name("parentData"), + parentDataProperty: new Name("parentDataProperty"), + rootData: new Name("rootData"), // root data - same as the data passed to the first/top validation function + dynamicAnchors: new Name("dynamicAnchors"), // used to support recursiveRef and dynamicRef + // function scoped variables + vErrors: new Name("vErrors"), // null or array of validation errors + errors: new Name("errors"), // counter of validation errors + this: new Name("this"), + // "globals" + self: new Name("self"), + scope: new Name("scope"), + // JTD serialize/parse name for JSON string and position + json: new Name("json"), + jsonPos: new Name("jsonPos"), + jsonLen: new Name("jsonLen"), + jsonPart: new Name("jsonPart"), +} + +export default names diff --git a/website/node_modules/ajv/lib/compile/ref_error.ts b/website/node_modules/ajv/lib/compile/ref_error.ts new file mode 100644 index 00000000..386bf049 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/ref_error.ts @@ -0,0 +1,13 @@ +import {resolveUrl, normalizeId, getFullPath} from "./resolve" +import type {UriResolver} from "../types" + +export default class MissingRefError extends Error { + readonly missingRef: string + readonly missingSchema: string + + constructor(resolver: UriResolver, baseId: string, ref: string, msg?: string) { + super(msg || `can't resolve reference ${ref} from id ${baseId}`) + this.missingRef = resolveUrl(resolver, baseId, ref) + this.missingSchema = normalizeId(getFullPath(resolver, this.missingRef)) + } +} diff --git a/website/node_modules/ajv/lib/compile/resolve.ts b/website/node_modules/ajv/lib/compile/resolve.ts new file mode 100644 index 00000000..4360eab0 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/resolve.ts @@ -0,0 +1,149 @@ +import type {AnySchema, AnySchemaObject, UriResolver} from "../types" +import type Ajv from "../ajv" +import type {URIComponents} from "uri-js" +import {eachItem} from "./util" +import * as equal from "fast-deep-equal" +import * as traverse from "json-schema-traverse" + +// the hash of local references inside the schema (created by getSchemaRefs), used for inline resolution +export type LocalRefs = {[Ref in string]?: AnySchemaObject} + +// TODO refactor to use keyword definitions +const SIMPLE_INLINED = new Set([ + "type", + "format", + "pattern", + "maxLength", + "minLength", + "maxProperties", + "minProperties", + "maxItems", + "minItems", + "maximum", + "minimum", + "uniqueItems", + "multipleOf", + "required", + "enum", + "const", +]) + +export function inlineRef(schema: AnySchema, limit: boolean | number = true): boolean { + if (typeof schema == "boolean") return true + if (limit === true) return !hasRef(schema) + if (!limit) return false + return countKeys(schema) <= limit +} + +const REF_KEYWORDS = new Set([ + "$ref", + "$recursiveRef", + "$recursiveAnchor", + "$dynamicRef", + "$dynamicAnchor", +]) + +function hasRef(schema: AnySchemaObject): boolean { + for (const key in schema) { + if (REF_KEYWORDS.has(key)) return true + const sch = schema[key] + if (Array.isArray(sch) && sch.some(hasRef)) return true + if (typeof sch == "object" && hasRef(sch)) return true + } + return false +} + +function countKeys(schema: AnySchemaObject): number { + let count = 0 + for (const key in schema) { + if (key === "$ref") return Infinity + count++ + if (SIMPLE_INLINED.has(key)) continue + if (typeof schema[key] == "object") { + eachItem(schema[key], (sch) => (count += countKeys(sch))) + } + if (count === Infinity) return Infinity + } + return count +} + +export function getFullPath(resolver: UriResolver, id = "", normalize?: boolean): string { + if (normalize !== false) id = normalizeId(id) + const p = resolver.parse(id) + return _getFullPath(resolver, p) +} + +export function _getFullPath(resolver: UriResolver, p: URIComponents): string { + const serialized = resolver.serialize(p) + return serialized.split("#")[0] + "#" +} + +const TRAILING_SLASH_HASH = /#\/?$/ +export function normalizeId(id: string | undefined): string { + return id ? id.replace(TRAILING_SLASH_HASH, "") : "" +} + +export function resolveUrl(resolver: UriResolver, baseId: string, id: string): string { + id = normalizeId(id) + return resolver.resolve(baseId, id) +} + +const ANCHOR = /^[a-z_][-a-z0-9._]*$/i + +export function getSchemaRefs(this: Ajv, schema: AnySchema, baseId: string): LocalRefs { + if (typeof schema == "boolean") return {} + const {schemaId, uriResolver} = this.opts + const schId = normalizeId(schema[schemaId] || baseId) + const baseIds: {[JsonPtr in string]?: string} = {"": schId} + const pathPrefix = getFullPath(uriResolver, schId, false) + const localRefs: LocalRefs = {} + const schemaRefs: Set = new Set() + + traverse(schema, {allKeys: true}, (sch, jsonPtr, _, parentJsonPtr) => { + if (parentJsonPtr === undefined) return + const fullPath = pathPrefix + jsonPtr + let baseId = baseIds[parentJsonPtr] + if (typeof sch[schemaId] == "string") baseId = addRef.call(this, sch[schemaId]) + addAnchor.call(this, sch.$anchor) + addAnchor.call(this, sch.$dynamicAnchor) + baseIds[jsonPtr] = baseId + + function addRef(this: Ajv, ref: string): string { + // eslint-disable-next-line @typescript-eslint/unbound-method + const _resolve = this.opts.uriResolver.resolve + ref = normalizeId(baseId ? _resolve(baseId, ref) : ref) + if (schemaRefs.has(ref)) throw ambiguos(ref) + schemaRefs.add(ref) + let schOrRef = this.refs[ref] + if (typeof schOrRef == "string") schOrRef = this.refs[schOrRef] + if (typeof schOrRef == "object") { + checkAmbiguosRef(sch, schOrRef.schema, ref) + } else if (ref !== normalizeId(fullPath)) { + if (ref[0] === "#") { + checkAmbiguosRef(sch, localRefs[ref], ref) + localRefs[ref] = sch + } else { + this.refs[ref] = fullPath + } + } + return ref + } + + function addAnchor(this: Ajv, anchor: unknown): void { + if (typeof anchor == "string") { + if (!ANCHOR.test(anchor)) throw new Error(`invalid anchor "${anchor}"`) + addRef.call(this, `#${anchor}`) + } + } + }) + + return localRefs + + function checkAmbiguosRef(sch1: AnySchema, sch2: AnySchema | undefined, ref: string): void { + if (sch2 !== undefined && !equal(sch1, sch2)) throw ambiguos(ref) + } + + function ambiguos(ref: string): Error { + return new Error(`reference "${ref}" resolves to more than one schema`) + } +} diff --git a/website/node_modules/ajv/lib/compile/rules.ts b/website/node_modules/ajv/lib/compile/rules.ts new file mode 100644 index 00000000..ea65074f --- /dev/null +++ b/website/node_modules/ajv/lib/compile/rules.ts @@ -0,0 +1,50 @@ +import type {AddedKeywordDefinition} from "../types" + +const _jsonTypes = ["string", "number", "integer", "boolean", "null", "object", "array"] as const + +export type JSONType = typeof _jsonTypes[number] + +const jsonTypes: Set = new Set(_jsonTypes) + +export function isJSONType(x: unknown): x is JSONType { + return typeof x == "string" && jsonTypes.has(x) +} + +type ValidationTypes = { + [K in JSONType]: boolean | RuleGroup | undefined +} + +export interface ValidationRules { + rules: RuleGroup[] + post: RuleGroup + all: {[Key in string]?: boolean | Rule} // rules that have to be validated + keywords: {[Key in string]?: boolean} // all known keywords (superset of "all") + types: ValidationTypes +} + +export interface RuleGroup { + type?: JSONType + rules: Rule[] +} + +// This interface wraps KeywordDefinition because definition can have multiple keywords +export interface Rule { + keyword: string + definition: AddedKeywordDefinition +} + +export function getRules(): ValidationRules { + const groups: Record<"number" | "string" | "array" | "object", RuleGroup> = { + number: {type: "number", rules: []}, + string: {type: "string", rules: []}, + array: {type: "array", rules: []}, + object: {type: "object", rules: []}, + } + return { + types: {...groups, integer: true, boolean: true, null: true}, + rules: [{rules: []}, groups.number, groups.string, groups.array, groups.object], + post: {rules: []}, + all: {}, + keywords: {}, + } +} diff --git a/website/node_modules/ajv/lib/compile/util.ts b/website/node_modules/ajv/lib/compile/util.ts new file mode 100644 index 00000000..cefae51c --- /dev/null +++ b/website/node_modules/ajv/lib/compile/util.ts @@ -0,0 +1,213 @@ +import type {AnySchema, EvaluatedProperties, EvaluatedItems} from "../types" +import type {SchemaCxt, SchemaObjCxt} from "." +import {_, getProperty, Code, Name, CodeGen} from "./codegen" +import {_Code} from "./codegen/code" +import type {Rule, ValidationRules} from "./rules" + +// TODO refactor to use Set +export function toHash(arr: T[]): {[K in T]?: true} { + const hash: {[K in T]?: true} = {} + for (const item of arr) hash[item] = true + return hash +} + +export function alwaysValidSchema(it: SchemaCxt, schema: AnySchema): boolean | void { + if (typeof schema == "boolean") return schema + if (Object.keys(schema).length === 0) return true + checkUnknownRules(it, schema) + return !schemaHasRules(schema, it.self.RULES.all) +} + +export function checkUnknownRules(it: SchemaCxt, schema: AnySchema = it.schema): void { + const {opts, self} = it + if (!opts.strictSchema) return + if (typeof schema === "boolean") return + const rules = self.RULES.keywords + for (const key in schema) { + if (!rules[key]) checkStrictMode(it, `unknown keyword: "${key}"`) + } +} + +export function schemaHasRules( + schema: AnySchema, + rules: {[Key in string]?: boolean | Rule} +): boolean { + if (typeof schema == "boolean") return !schema + for (const key in schema) if (rules[key]) return true + return false +} + +export function schemaHasRulesButRef(schema: AnySchema, RULES: ValidationRules): boolean { + if (typeof schema == "boolean") return !schema + for (const key in schema) if (key !== "$ref" && RULES.all[key]) return true + return false +} + +export function schemaRefOrVal( + {topSchemaRef, schemaPath}: SchemaObjCxt, + schema: unknown, + keyword: string, + $data?: string | false +): Code | number | boolean { + if (!$data) { + if (typeof schema == "number" || typeof schema == "boolean") return schema + if (typeof schema == "string") return _`${schema}` + } + return _`${topSchemaRef}${schemaPath}${getProperty(keyword)}` +} + +export function unescapeFragment(str: string): string { + return unescapeJsonPointer(decodeURIComponent(str)) +} + +export function escapeFragment(str: string | number): string { + return encodeURIComponent(escapeJsonPointer(str)) +} + +export function escapeJsonPointer(str: string | number): string { + if (typeof str == "number") return `${str}` + return str.replace(/~/g, "~0").replace(/\//g, "~1") +} + +export function unescapeJsonPointer(str: string): string { + return str.replace(/~1/g, "/").replace(/~0/g, "~") +} + +export function eachItem(xs: T | T[], f: (x: T) => void): void { + if (Array.isArray(xs)) { + for (const x of xs) f(x) + } else { + f(xs) + } +} + +type SomeEvaluated = EvaluatedProperties | EvaluatedItems + +type MergeEvaluatedFunc = ( + gen: CodeGen, + from: Name | T, + to: Name | Exclude | undefined, + toName?: typeof Name +) => Name | T + +interface MakeMergeFuncArgs { + mergeNames: (gen: CodeGen, from: Name, to: Name) => void + mergeToName: (gen: CodeGen, from: T, to: Name) => void + mergeValues: (from: T, to: Exclude) => T + resultToName: (gen: CodeGen, res?: T) => Name +} + +function makeMergeEvaluated({ + mergeNames, + mergeToName, + mergeValues, + resultToName, +}: MakeMergeFuncArgs): MergeEvaluatedFunc { + return (gen, from, to, toName) => { + const res = + to === undefined + ? from + : to instanceof Name + ? (from instanceof Name ? mergeNames(gen, from, to) : mergeToName(gen, from, to), to) + : from instanceof Name + ? (mergeToName(gen, to, from), from) + : mergeValues(from, to) + return toName === Name && !(res instanceof Name) ? resultToName(gen, res) : res + } +} + +interface MergeEvaluated { + props: MergeEvaluatedFunc + items: MergeEvaluatedFunc +} + +export const mergeEvaluated: MergeEvaluated = { + props: makeMergeEvaluated({ + mergeNames: (gen, from, to) => + gen.if(_`${to} !== true && ${from} !== undefined`, () => { + gen.if( + _`${from} === true`, + () => gen.assign(to, true), + () => gen.assign(to, _`${to} || {}`).code(_`Object.assign(${to}, ${from})`) + ) + }), + mergeToName: (gen, from, to) => + gen.if(_`${to} !== true`, () => { + if (from === true) { + gen.assign(to, true) + } else { + gen.assign(to, _`${to} || {}`) + setEvaluated(gen, to, from) + } + }), + mergeValues: (from, to) => (from === true ? true : {...from, ...to}), + resultToName: evaluatedPropsToName, + }), + items: makeMergeEvaluated({ + mergeNames: (gen, from, to) => + gen.if(_`${to} !== true && ${from} !== undefined`, () => + gen.assign(to, _`${from} === true ? true : ${to} > ${from} ? ${to} : ${from}`) + ), + mergeToName: (gen, from, to) => + gen.if(_`${to} !== true`, () => + gen.assign(to, from === true ? true : _`${to} > ${from} ? ${to} : ${from}`) + ), + mergeValues: (from, to) => (from === true ? true : Math.max(from, to)), + resultToName: (gen, items) => gen.var("items", items), + }), +} + +export function evaluatedPropsToName(gen: CodeGen, ps?: EvaluatedProperties): Name { + if (ps === true) return gen.var("props", true) + const props = gen.var("props", _`{}`) + if (ps !== undefined) setEvaluated(gen, props, ps) + return props +} + +export function setEvaluated(gen: CodeGen, props: Name, ps: {[K in string]?: true}): void { + Object.keys(ps).forEach((p) => gen.assign(_`${props}${getProperty(p)}`, true)) +} + +const snippets: {[S in string]?: _Code} = {} + +export function useFunc(gen: CodeGen, f: {code: string}): Name { + return gen.scopeValue("func", { + ref: f, + code: snippets[f.code] || (snippets[f.code] = new _Code(f.code)), + }) +} + +export enum Type { + Num, + Str, +} + +export function getErrorPath( + dataProp: Name | string | number, + dataPropType?: Type, + jsPropertySyntax?: boolean +): Code | string { + // let path + if (dataProp instanceof Name) { + const isNumber = dataPropType === Type.Num + return jsPropertySyntax + ? isNumber + ? _`"[" + ${dataProp} + "]"` + : _`"['" + ${dataProp} + "']"` + : isNumber + ? _`"/" + ${dataProp}` + : _`"/" + ${dataProp}.replace(/~/g, "~0").replace(/\\//g, "~1")` // TODO maybe use global escapePointer + } + return jsPropertySyntax ? getProperty(dataProp).toString() : "/" + escapeJsonPointer(dataProp) +} + +export function checkStrictMode( + it: SchemaCxt, + msg: string, + mode: boolean | "log" = it.opts.strictSchema +): void { + if (!mode) return + msg = `strict mode: ${msg}` + if (mode === true) throw new Error(msg) + it.self.logger.warn(msg) +} diff --git a/website/node_modules/ajv/lib/compile/validate/applicability.ts b/website/node_modules/ajv/lib/compile/validate/applicability.ts new file mode 100644 index 00000000..478b704a --- /dev/null +++ b/website/node_modules/ajv/lib/compile/validate/applicability.ts @@ -0,0 +1,22 @@ +import type {AnySchemaObject} from "../../types" +import type {SchemaObjCxt} from ".." +import type {JSONType, RuleGroup, Rule} from "../rules" + +export function schemaHasRulesForType( + {schema, self}: SchemaObjCxt, + type: JSONType +): boolean | undefined { + const group = self.RULES.types[type] + return group && group !== true && shouldUseGroup(schema, group) +} + +export function shouldUseGroup(schema: AnySchemaObject, group: RuleGroup): boolean { + return group.rules.some((rule) => shouldUseRule(schema, rule)) +} + +export function shouldUseRule(schema: AnySchemaObject, rule: Rule): boolean | undefined { + return ( + schema[rule.keyword] !== undefined || + rule.definition.implements?.some((kwd) => schema[kwd] !== undefined) + ) +} diff --git a/website/node_modules/ajv/lib/compile/validate/boolSchema.ts b/website/node_modules/ajv/lib/compile/validate/boolSchema.ts new file mode 100644 index 00000000..15635501 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/validate/boolSchema.ts @@ -0,0 +1,47 @@ +import type {KeywordErrorDefinition, KeywordErrorCxt} from "../../types" +import type {SchemaCxt} from ".." +import {reportError} from "../errors" +import {_, Name} from "../codegen" +import N from "../names" + +const boolError: KeywordErrorDefinition = { + message: "boolean schema is false", +} + +export function topBoolOrEmptySchema(it: SchemaCxt): void { + const {gen, schema, validateName} = it + if (schema === false) { + falseSchemaError(it, false) + } else if (typeof schema == "object" && schema.$async === true) { + gen.return(N.data) + } else { + gen.assign(_`${validateName}.errors`, null) + gen.return(true) + } +} + +export function boolOrEmptySchema(it: SchemaCxt, valid: Name): void { + const {gen, schema} = it + if (schema === false) { + gen.var(valid, false) // TODO var + falseSchemaError(it) + } else { + gen.var(valid, true) // TODO var + } +} + +function falseSchemaError(it: SchemaCxt, overrideAllErrors?: boolean): void { + const {gen, data} = it + // TODO maybe some other interface should be used for non-keyword validation errors... + const cxt: KeywordErrorCxt = { + gen, + keyword: "false schema", + data, + schema: false, + schemaCode: false, + schemaValue: false, + params: {}, + it, + } + reportError(cxt, boolError, undefined, overrideAllErrors) +} diff --git a/website/node_modules/ajv/lib/compile/validate/dataType.ts b/website/node_modules/ajv/lib/compile/validate/dataType.ts new file mode 100644 index 00000000..b315c2ce --- /dev/null +++ b/website/node_modules/ajv/lib/compile/validate/dataType.ts @@ -0,0 +1,229 @@ +import type { + KeywordErrorDefinition, + KeywordErrorCxt, + ErrorObject, + AnySchemaObject, +} from "../../types" +import type {SchemaObjCxt} from ".." +import {isJSONType, JSONType} from "../rules" +import {schemaHasRulesForType} from "./applicability" +import {reportError} from "../errors" +import {_, nil, and, not, operators, Code, Name} from "../codegen" +import {toHash, schemaRefOrVal} from "../util" + +export enum DataType { + Correct, + Wrong, +} + +export function getSchemaTypes(schema: AnySchemaObject): JSONType[] { + const types = getJSONTypes(schema.type) + const hasNull = types.includes("null") + if (hasNull) { + if (schema.nullable === false) throw new Error("type: null contradicts nullable: false") + } else { + if (!types.length && schema.nullable !== undefined) { + throw new Error('"nullable" cannot be used without "type"') + } + if (schema.nullable === true) types.push("null") + } + return types +} + +export function getJSONTypes(ts: unknown | unknown[]): JSONType[] { + const types: unknown[] = Array.isArray(ts) ? ts : ts ? [ts] : [] + if (types.every(isJSONType)) return types + throw new Error("type must be JSONType or JSONType[]: " + types.join(",")) +} + +export function coerceAndCheckDataType(it: SchemaObjCxt, types: JSONType[]): boolean { + const {gen, data, opts} = it + const coerceTo = coerceToTypes(types, opts.coerceTypes) + const checkTypes = + types.length > 0 && + !(coerceTo.length === 0 && types.length === 1 && schemaHasRulesForType(it, types[0])) + if (checkTypes) { + const wrongType = checkDataTypes(types, data, opts.strictNumbers, DataType.Wrong) + gen.if(wrongType, () => { + if (coerceTo.length) coerceData(it, types, coerceTo) + else reportTypeError(it) + }) + } + return checkTypes +} + +const COERCIBLE: Set = new Set(["string", "number", "integer", "boolean", "null"]) +function coerceToTypes(types: JSONType[], coerceTypes?: boolean | "array"): JSONType[] { + return coerceTypes + ? types.filter((t) => COERCIBLE.has(t) || (coerceTypes === "array" && t === "array")) + : [] +} + +function coerceData(it: SchemaObjCxt, types: JSONType[], coerceTo: JSONType[]): void { + const {gen, data, opts} = it + const dataType = gen.let("dataType", _`typeof ${data}`) + const coerced = gen.let("coerced", _`undefined`) + if (opts.coerceTypes === "array") { + gen.if(_`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => + gen + .assign(data, _`${data}[0]`) + .assign(dataType, _`typeof ${data}`) + .if(checkDataTypes(types, data, opts.strictNumbers), () => gen.assign(coerced, data)) + ) + } + gen.if(_`${coerced} !== undefined`) + for (const t of coerceTo) { + if (COERCIBLE.has(t) || (t === "array" && opts.coerceTypes === "array")) { + coerceSpecificType(t) + } + } + gen.else() + reportTypeError(it) + gen.endIf() + + gen.if(_`${coerced} !== undefined`, () => { + gen.assign(data, coerced) + assignParentData(it, coerced) + }) + + function coerceSpecificType(t: string): void { + switch (t) { + case "string": + gen + .elseIf(_`${dataType} == "number" || ${dataType} == "boolean"`) + .assign(coerced, _`"" + ${data}`) + .elseIf(_`${data} === null`) + .assign(coerced, _`""`) + return + case "number": + gen + .elseIf( + _`${dataType} == "boolean" || ${data} === null + || (${dataType} == "string" && ${data} && ${data} == +${data})` + ) + .assign(coerced, _`+${data}`) + return + case "integer": + gen + .elseIf( + _`${dataType} === "boolean" || ${data} === null + || (${dataType} === "string" && ${data} && ${data} == +${data} && !(${data} % 1))` + ) + .assign(coerced, _`+${data}`) + return + case "boolean": + gen + .elseIf(_`${data} === "false" || ${data} === 0 || ${data} === null`) + .assign(coerced, false) + .elseIf(_`${data} === "true" || ${data} === 1`) + .assign(coerced, true) + return + case "null": + gen.elseIf(_`${data} === "" || ${data} === 0 || ${data} === false`) + gen.assign(coerced, null) + return + + case "array": + gen + .elseIf( + _`${dataType} === "string" || ${dataType} === "number" + || ${dataType} === "boolean" || ${data} === null` + ) + .assign(coerced, _`[${data}]`) + } + } +} + +function assignParentData({gen, parentData, parentDataProperty}: SchemaObjCxt, expr: Name): void { + // TODO use gen.property + gen.if(_`${parentData} !== undefined`, () => + gen.assign(_`${parentData}[${parentDataProperty}]`, expr) + ) +} + +export function checkDataType( + dataType: JSONType, + data: Name, + strictNums?: boolean | "log", + correct = DataType.Correct +): Code { + const EQ = correct === DataType.Correct ? operators.EQ : operators.NEQ + let cond: Code + switch (dataType) { + case "null": + return _`${data} ${EQ} null` + case "array": + cond = _`Array.isArray(${data})` + break + case "object": + cond = _`${data} && typeof ${data} == "object" && !Array.isArray(${data})` + break + case "integer": + cond = numCond(_`!(${data} % 1) && !isNaN(${data})`) + break + case "number": + cond = numCond() + break + default: + return _`typeof ${data} ${EQ} ${dataType}` + } + return correct === DataType.Correct ? cond : not(cond) + + function numCond(_cond: Code = nil): Code { + return and(_`typeof ${data} == "number"`, _cond, strictNums ? _`isFinite(${data})` : nil) + } +} + +export function checkDataTypes( + dataTypes: JSONType[], + data: Name, + strictNums?: boolean | "log", + correct?: DataType +): Code { + if (dataTypes.length === 1) { + return checkDataType(dataTypes[0], data, strictNums, correct) + } + let cond: Code + const types = toHash(dataTypes) + if (types.array && types.object) { + const notObj = _`typeof ${data} != "object"` + cond = types.null ? notObj : _`!${data} || ${notObj}` + delete types.null + delete types.array + delete types.object + } else { + cond = nil + } + if (types.number) delete types.integer + for (const t in types) cond = and(cond, checkDataType(t as JSONType, data, strictNums, correct)) + return cond +} + +export type TypeError = ErrorObject<"type", {type: string}> + +const typeError: KeywordErrorDefinition = { + message: ({schema}) => `must be ${schema}`, + params: ({schema, schemaValue}) => + typeof schema == "string" ? _`{type: ${schema}}` : _`{type: ${schemaValue}}`, +} + +export function reportTypeError(it: SchemaObjCxt): void { + const cxt = getTypeErrorContext(it) + reportError(cxt, typeError) +} + +function getTypeErrorContext(it: SchemaObjCxt): KeywordErrorCxt { + const {gen, data, schema} = it + const schemaCode = schemaRefOrVal(it, schema, "type") + return { + gen, + keyword: "type", + data, + schema: schema.type, + schemaCode, + schemaValue: schemaCode, + parentSchema: schema, + params: {}, + it, + } +} diff --git a/website/node_modules/ajv/lib/compile/validate/defaults.ts b/website/node_modules/ajv/lib/compile/validate/defaults.ts new file mode 100644 index 00000000..2ad3d4df --- /dev/null +++ b/website/node_modules/ajv/lib/compile/validate/defaults.ts @@ -0,0 +1,32 @@ +import type {SchemaObjCxt} from ".." +import {_, getProperty, stringify} from "../codegen" +import {checkStrictMode} from "../util" + +export function assignDefaults(it: SchemaObjCxt, ty?: string): void { + const {properties, items} = it.schema + if (ty === "object" && properties) { + for (const key in properties) { + assignDefault(it, key, properties[key].default) + } + } else if (ty === "array" && Array.isArray(items)) { + items.forEach((sch, i: number) => assignDefault(it, i, sch.default)) + } +} + +function assignDefault(it: SchemaObjCxt, prop: string | number, defaultValue: unknown): void { + const {gen, compositeRule, data, opts} = it + if (defaultValue === undefined) return + const childData = _`${data}${getProperty(prop)}` + if (compositeRule) { + checkStrictMode(it, `default is ignored for: ${childData}`) + return + } + + let condition = _`${childData} === undefined` + if (opts.useDefaults === "empty") { + condition = _`${condition} || ${childData} === null || ${childData} === ""` + } + // `${childData} === undefined` + + // (opts.useDefaults === "empty" ? ` || ${childData} === null || ${childData} === ""` : "") + gen.if(condition, _`${childData} = ${stringify(defaultValue)}`) +} diff --git a/website/node_modules/ajv/lib/compile/validate/index.ts b/website/node_modules/ajv/lib/compile/validate/index.ts new file mode 100644 index 00000000..15ecabd8 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/validate/index.ts @@ -0,0 +1,582 @@ +import type { + AddedKeywordDefinition, + AnySchema, + AnySchemaObject, + KeywordErrorCxt, + KeywordCxtParams, +} from "../../types" +import type {SchemaCxt, SchemaObjCxt} from ".." +import type {InstanceOptions} from "../../core" +import {boolOrEmptySchema, topBoolOrEmptySchema} from "./boolSchema" +import {coerceAndCheckDataType, getSchemaTypes} from "./dataType" +import {shouldUseGroup, shouldUseRule} from "./applicability" +import {checkDataType, checkDataTypes, reportTypeError, DataType} from "./dataType" +import {assignDefaults} from "./defaults" +import {funcKeywordCode, macroKeywordCode, validateKeywordUsage, validSchemaType} from "./keyword" +import {getSubschema, extendSubschemaData, SubschemaArgs, extendSubschemaMode} from "./subschema" +import {_, nil, str, or, not, getProperty, Block, Code, Name, CodeGen} from "../codegen" +import N from "../names" +import {resolveUrl} from "../resolve" +import { + schemaRefOrVal, + schemaHasRulesButRef, + checkUnknownRules, + checkStrictMode, + unescapeJsonPointer, + mergeEvaluated, +} from "../util" +import type {JSONType, Rule, RuleGroup} from "../rules" +import { + ErrorPaths, + reportError, + reportExtraError, + resetErrorsCount, + keyword$DataError, +} from "../errors" + +// schema compilation - generates validation function, subschemaCode (below) is used for subschemas +export function validateFunctionCode(it: SchemaCxt): void { + if (isSchemaObj(it)) { + checkKeywords(it) + if (schemaCxtHasRules(it)) { + topSchemaObjCode(it) + return + } + } + validateFunction(it, () => topBoolOrEmptySchema(it)) +} + +function validateFunction( + {gen, validateName, schema, schemaEnv, opts}: SchemaCxt, + body: Block +): void { + if (opts.code.es5) { + gen.func(validateName, _`${N.data}, ${N.valCxt}`, schemaEnv.$async, () => { + gen.code(_`"use strict"; ${funcSourceUrl(schema, opts)}`) + destructureValCxtES5(gen, opts) + gen.code(body) + }) + } else { + gen.func(validateName, _`${N.data}, ${destructureValCxt(opts)}`, schemaEnv.$async, () => + gen.code(funcSourceUrl(schema, opts)).code(body) + ) + } +} + +function destructureValCxt(opts: InstanceOptions): Code { + return _`{${N.instancePath}="", ${N.parentData}, ${N.parentDataProperty}, ${N.rootData}=${ + N.data + }${opts.dynamicRef ? _`, ${N.dynamicAnchors}={}` : nil}}={}` +} + +function destructureValCxtES5(gen: CodeGen, opts: InstanceOptions): void { + gen.if( + N.valCxt, + () => { + gen.var(N.instancePath, _`${N.valCxt}.${N.instancePath}`) + gen.var(N.parentData, _`${N.valCxt}.${N.parentData}`) + gen.var(N.parentDataProperty, _`${N.valCxt}.${N.parentDataProperty}`) + gen.var(N.rootData, _`${N.valCxt}.${N.rootData}`) + if (opts.dynamicRef) gen.var(N.dynamicAnchors, _`${N.valCxt}.${N.dynamicAnchors}`) + }, + () => { + gen.var(N.instancePath, _`""`) + gen.var(N.parentData, _`undefined`) + gen.var(N.parentDataProperty, _`undefined`) + gen.var(N.rootData, N.data) + if (opts.dynamicRef) gen.var(N.dynamicAnchors, _`{}`) + } + ) +} + +function topSchemaObjCode(it: SchemaObjCxt): void { + const {schema, opts, gen} = it + validateFunction(it, () => { + if (opts.$comment && schema.$comment) commentKeyword(it) + checkNoDefault(it) + gen.let(N.vErrors, null) + gen.let(N.errors, 0) + if (opts.unevaluated) resetEvaluated(it) + typeAndKeywords(it) + returnResults(it) + }) + return +} + +function resetEvaluated(it: SchemaObjCxt): void { + // TODO maybe some hook to execute it in the end to check whether props/items are Name, as in assignEvaluated + const {gen, validateName} = it + it.evaluated = gen.const("evaluated", _`${validateName}.evaluated`) + gen.if(_`${it.evaluated}.dynamicProps`, () => gen.assign(_`${it.evaluated}.props`, _`undefined`)) + gen.if(_`${it.evaluated}.dynamicItems`, () => gen.assign(_`${it.evaluated}.items`, _`undefined`)) +} + +function funcSourceUrl(schema: AnySchema, opts: InstanceOptions): Code { + const schId = typeof schema == "object" && schema[opts.schemaId] + return schId && (opts.code.source || opts.code.process) ? _`/*# sourceURL=${schId} */` : nil +} + +// schema compilation - this function is used recursively to generate code for sub-schemas +function subschemaCode(it: SchemaCxt, valid: Name): void { + if (isSchemaObj(it)) { + checkKeywords(it) + if (schemaCxtHasRules(it)) { + subSchemaObjCode(it, valid) + return + } + } + boolOrEmptySchema(it, valid) +} + +function schemaCxtHasRules({schema, self}: SchemaCxt): boolean { + if (typeof schema == "boolean") return !schema + for (const key in schema) if (self.RULES.all[key]) return true + return false +} + +function isSchemaObj(it: SchemaCxt): it is SchemaObjCxt { + return typeof it.schema != "boolean" +} + +function subSchemaObjCode(it: SchemaObjCxt, valid: Name): void { + const {schema, gen, opts} = it + if (opts.$comment && schema.$comment) commentKeyword(it) + updateContext(it) + checkAsyncSchema(it) + const errsCount = gen.const("_errs", N.errors) + typeAndKeywords(it, errsCount) + // TODO var + gen.var(valid, _`${errsCount} === ${N.errors}`) +} + +function checkKeywords(it: SchemaObjCxt): void { + checkUnknownRules(it) + checkRefsAndKeywords(it) +} + +function typeAndKeywords(it: SchemaObjCxt, errsCount?: Name): void { + if (it.opts.jtd) return schemaKeywords(it, [], false, errsCount) + const types = getSchemaTypes(it.schema) + const checkedTypes = coerceAndCheckDataType(it, types) + schemaKeywords(it, types, !checkedTypes, errsCount) +} + +function checkRefsAndKeywords(it: SchemaObjCxt): void { + const {schema, errSchemaPath, opts, self} = it + if (schema.$ref && opts.ignoreKeywordsWithRef && schemaHasRulesButRef(schema, self.RULES)) { + self.logger.warn(`$ref: keywords ignored in schema at path "${errSchemaPath}"`) + } +} + +function checkNoDefault(it: SchemaObjCxt): void { + const {schema, opts} = it + if (schema.default !== undefined && opts.useDefaults && opts.strictSchema) { + checkStrictMode(it, "default is ignored in the schema root") + } +} + +function updateContext(it: SchemaObjCxt): void { + const schId = it.schema[it.opts.schemaId] + if (schId) it.baseId = resolveUrl(it.opts.uriResolver, it.baseId, schId) +} + +function checkAsyncSchema(it: SchemaObjCxt): void { + if (it.schema.$async && !it.schemaEnv.$async) throw new Error("async schema in sync schema") +} + +function commentKeyword({gen, schemaEnv, schema, errSchemaPath, opts}: SchemaObjCxt): void { + const msg = schema.$comment + if (opts.$comment === true) { + gen.code(_`${N.self}.logger.log(${msg})`) + } else if (typeof opts.$comment == "function") { + const schemaPath = str`${errSchemaPath}/$comment` + const rootName = gen.scopeValue("root", {ref: schemaEnv.root}) + gen.code(_`${N.self}.opts.$comment(${msg}, ${schemaPath}, ${rootName}.schema)`) + } +} + +function returnResults(it: SchemaCxt): void { + const {gen, schemaEnv, validateName, ValidationError, opts} = it + if (schemaEnv.$async) { + // TODO assign unevaluated + gen.if( + _`${N.errors} === 0`, + () => gen.return(N.data), + () => gen.throw(_`new ${ValidationError as Name}(${N.vErrors})`) + ) + } else { + gen.assign(_`${validateName}.errors`, N.vErrors) + if (opts.unevaluated) assignEvaluated(it) + gen.return(_`${N.errors} === 0`) + } +} + +function assignEvaluated({gen, evaluated, props, items}: SchemaCxt): void { + if (props instanceof Name) gen.assign(_`${evaluated}.props`, props) + if (items instanceof Name) gen.assign(_`${evaluated}.items`, items) +} + +function schemaKeywords( + it: SchemaObjCxt, + types: JSONType[], + typeErrors: boolean, + errsCount?: Name +): void { + const {gen, schema, data, allErrors, opts, self} = it + const {RULES} = self + if (schema.$ref && (opts.ignoreKeywordsWithRef || !schemaHasRulesButRef(schema, RULES))) { + gen.block(() => keywordCode(it, "$ref", (RULES.all.$ref as Rule).definition)) // TODO typecast + return + } + if (!opts.jtd) checkStrictTypes(it, types) + gen.block(() => { + for (const group of RULES.rules) groupKeywords(group) + groupKeywords(RULES.post) + }) + + function groupKeywords(group: RuleGroup): void { + if (!shouldUseGroup(schema, group)) return + if (group.type) { + gen.if(checkDataType(group.type, data, opts.strictNumbers)) + iterateKeywords(it, group) + if (types.length === 1 && types[0] === group.type && typeErrors) { + gen.else() + reportTypeError(it) + } + gen.endIf() + } else { + iterateKeywords(it, group) + } + // TODO make it "ok" call? + if (!allErrors) gen.if(_`${N.errors} === ${errsCount || 0}`) + } +} + +function iterateKeywords(it: SchemaObjCxt, group: RuleGroup): void { + const { + gen, + schema, + opts: {useDefaults}, + } = it + if (useDefaults) assignDefaults(it, group.type) + gen.block(() => { + for (const rule of group.rules) { + if (shouldUseRule(schema, rule)) { + keywordCode(it, rule.keyword, rule.definition, group.type) + } + } + }) +} + +function checkStrictTypes(it: SchemaObjCxt, types: JSONType[]): void { + if (it.schemaEnv.meta || !it.opts.strictTypes) return + checkContextTypes(it, types) + if (!it.opts.allowUnionTypes) checkMultipleTypes(it, types) + checkKeywordTypes(it, it.dataTypes) +} + +function checkContextTypes(it: SchemaObjCxt, types: JSONType[]): void { + if (!types.length) return + if (!it.dataTypes.length) { + it.dataTypes = types + return + } + types.forEach((t) => { + if (!includesType(it.dataTypes, t)) { + strictTypesError(it, `type "${t}" not allowed by context "${it.dataTypes.join(",")}"`) + } + }) + narrowSchemaTypes(it, types) +} + +function checkMultipleTypes(it: SchemaObjCxt, ts: JSONType[]): void { + if (ts.length > 1 && !(ts.length === 2 && ts.includes("null"))) { + strictTypesError(it, "use allowUnionTypes to allow union type keyword") + } +} + +function checkKeywordTypes(it: SchemaObjCxt, ts: JSONType[]): void { + const rules = it.self.RULES.all + for (const keyword in rules) { + const rule = rules[keyword] + if (typeof rule == "object" && shouldUseRule(it.schema, rule)) { + const {type} = rule.definition + if (type.length && !type.some((t) => hasApplicableType(ts, t))) { + strictTypesError(it, `missing type "${type.join(",")}" for keyword "${keyword}"`) + } + } + } +} + +function hasApplicableType(schTs: JSONType[], kwdT: JSONType): boolean { + return schTs.includes(kwdT) || (kwdT === "number" && schTs.includes("integer")) +} + +function includesType(ts: JSONType[], t: JSONType): boolean { + return ts.includes(t) || (t === "integer" && ts.includes("number")) +} + +function narrowSchemaTypes(it: SchemaObjCxt, withTypes: JSONType[]): void { + const ts: JSONType[] = [] + for (const t of it.dataTypes) { + if (includesType(withTypes, t)) ts.push(t) + else if (withTypes.includes("integer") && t === "number") ts.push("integer") + } + it.dataTypes = ts +} + +function strictTypesError(it: SchemaObjCxt, msg: string): void { + const schemaPath = it.schemaEnv.baseId + it.errSchemaPath + msg += ` at "${schemaPath}" (strictTypes)` + checkStrictMode(it, msg, it.opts.strictTypes) +} + +export class KeywordCxt implements KeywordErrorCxt { + readonly gen: CodeGen + readonly allErrors?: boolean + readonly keyword: string + readonly data: Name // Name referencing the current level of the data instance + readonly $data?: string | false + schema: any // keyword value in the schema + readonly schemaValue: Code | number | boolean // Code reference to keyword schema value or primitive value + readonly schemaCode: Code | number | boolean // Code reference to resolved schema value (different if schema is $data) + readonly schemaType: JSONType[] // allowed type(s) of keyword value in the schema + readonly parentSchema: AnySchemaObject + readonly errsCount?: Name // Name reference to the number of validation errors collected before this keyword, + // requires option trackErrors in keyword definition + params: KeywordCxtParams // object to pass parameters to error messages from keyword code + readonly it: SchemaObjCxt // schema compilation context (schema is guaranteed to be an object, not boolean) + readonly def: AddedKeywordDefinition + + constructor(it: SchemaObjCxt, def: AddedKeywordDefinition, keyword: string) { + validateKeywordUsage(it, def, keyword) + this.gen = it.gen + this.allErrors = it.allErrors + this.keyword = keyword + this.data = it.data + this.schema = it.schema[keyword] + this.$data = def.$data && it.opts.$data && this.schema && this.schema.$data + this.schemaValue = schemaRefOrVal(it, this.schema, keyword, this.$data) + this.schemaType = def.schemaType + this.parentSchema = it.schema + this.params = {} + this.it = it + this.def = def + + if (this.$data) { + this.schemaCode = it.gen.const("vSchema", getData(this.$data, it)) + } else { + this.schemaCode = this.schemaValue + if (!validSchemaType(this.schema, def.schemaType, def.allowUndefined)) { + throw new Error(`${keyword} value must be ${JSON.stringify(def.schemaType)}`) + } + } + + if ("code" in def ? def.trackErrors : def.errors !== false) { + this.errsCount = it.gen.const("_errs", N.errors) + } + } + + result(condition: Code, successAction?: () => void, failAction?: () => void): void { + this.failResult(not(condition), successAction, failAction) + } + + failResult(condition: Code, successAction?: () => void, failAction?: () => void): void { + this.gen.if(condition) + if (failAction) failAction() + else this.error() + if (successAction) { + this.gen.else() + successAction() + if (this.allErrors) this.gen.endIf() + } else { + if (this.allErrors) this.gen.endIf() + else this.gen.else() + } + } + + pass(condition: Code, failAction?: () => void): void { + this.failResult(not(condition), undefined, failAction) + } + + fail(condition?: Code): void { + if (condition === undefined) { + this.error() + if (!this.allErrors) this.gen.if(false) // this branch will be removed by gen.optimize + return + } + this.gen.if(condition) + this.error() + if (this.allErrors) this.gen.endIf() + else this.gen.else() + } + + fail$data(condition: Code): void { + if (!this.$data) return this.fail(condition) + const {schemaCode} = this + this.fail(_`${schemaCode} !== undefined && (${or(this.invalid$data(), condition)})`) + } + + error(append?: boolean, errorParams?: KeywordCxtParams, errorPaths?: ErrorPaths): void { + if (errorParams) { + this.setParams(errorParams) + this._error(append, errorPaths) + this.setParams({}) + return + } + this._error(append, errorPaths) + } + + private _error(append?: boolean, errorPaths?: ErrorPaths): void { + ;(append ? reportExtraError : reportError)(this, this.def.error, errorPaths) + } + + $dataError(): void { + reportError(this, this.def.$dataError || keyword$DataError) + } + + reset(): void { + if (this.errsCount === undefined) throw new Error('add "trackErrors" to keyword definition') + resetErrorsCount(this.gen, this.errsCount) + } + + ok(cond: Code | boolean): void { + if (!this.allErrors) this.gen.if(cond) + } + + setParams(obj: KeywordCxtParams, assign?: true): void { + if (assign) Object.assign(this.params, obj) + else this.params = obj + } + + block$data(valid: Name, codeBlock: () => void, $dataValid: Code = nil): void { + this.gen.block(() => { + this.check$data(valid, $dataValid) + codeBlock() + }) + } + + check$data(valid: Name = nil, $dataValid: Code = nil): void { + if (!this.$data) return + const {gen, schemaCode, schemaType, def} = this + gen.if(or(_`${schemaCode} === undefined`, $dataValid)) + if (valid !== nil) gen.assign(valid, true) + if (schemaType.length || def.validateSchema) { + gen.elseIf(this.invalid$data()) + this.$dataError() + if (valid !== nil) gen.assign(valid, false) + } + gen.else() + } + + invalid$data(): Code { + const {gen, schemaCode, schemaType, def, it} = this + return or(wrong$DataType(), invalid$DataSchema()) + + function wrong$DataType(): Code { + if (schemaType.length) { + /* istanbul ignore if */ + if (!(schemaCode instanceof Name)) throw new Error("ajv implementation error") + const st = Array.isArray(schemaType) ? schemaType : [schemaType] + return _`${checkDataTypes(st, schemaCode, it.opts.strictNumbers, DataType.Wrong)}` + } + return nil + } + + function invalid$DataSchema(): Code { + if (def.validateSchema) { + const validateSchemaRef = gen.scopeValue("validate$data", {ref: def.validateSchema}) // TODO value.code for standalone + return _`!${validateSchemaRef}(${schemaCode})` + } + return nil + } + } + + subschema(appl: SubschemaArgs, valid: Name): SchemaCxt { + const subschema = getSubschema(this.it, appl) + extendSubschemaData(subschema, this.it, appl) + extendSubschemaMode(subschema, appl) + const nextContext = {...this.it, ...subschema, items: undefined, props: undefined} + subschemaCode(nextContext, valid) + return nextContext + } + + mergeEvaluated(schemaCxt: SchemaCxt, toName?: typeof Name): void { + const {it, gen} = this + if (!it.opts.unevaluated) return + if (it.props !== true && schemaCxt.props !== undefined) { + it.props = mergeEvaluated.props(gen, schemaCxt.props, it.props, toName) + } + if (it.items !== true && schemaCxt.items !== undefined) { + it.items = mergeEvaluated.items(gen, schemaCxt.items, it.items, toName) + } + } + + mergeValidEvaluated(schemaCxt: SchemaCxt, valid: Name): boolean | void { + const {it, gen} = this + if (it.opts.unevaluated && (it.props !== true || it.items !== true)) { + gen.if(valid, () => this.mergeEvaluated(schemaCxt, Name)) + return true + } + } +} + +function keywordCode( + it: SchemaObjCxt, + keyword: string, + def: AddedKeywordDefinition, + ruleType?: JSONType +): void { + const cxt = new KeywordCxt(it, def, keyword) + if ("code" in def) { + def.code(cxt, ruleType) + } else if (cxt.$data && def.validate) { + funcKeywordCode(cxt, def) + } else if ("macro" in def) { + macroKeywordCode(cxt, def) + } else if (def.compile || def.validate) { + funcKeywordCode(cxt, def) + } +} + +const JSON_POINTER = /^\/(?:[^~]|~0|~1)*$/ +const RELATIVE_JSON_POINTER = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/ +export function getData( + $data: string, + {dataLevel, dataNames, dataPathArr}: SchemaCxt +): Code | number { + let jsonPointer + let data: Code + if ($data === "") return N.rootData + if ($data[0] === "/") { + if (!JSON_POINTER.test($data)) throw new Error(`Invalid JSON-pointer: ${$data}`) + jsonPointer = $data + data = N.rootData + } else { + const matches = RELATIVE_JSON_POINTER.exec($data) + if (!matches) throw new Error(`Invalid JSON-pointer: ${$data}`) + const up: number = +matches[1] + jsonPointer = matches[2] + if (jsonPointer === "#") { + if (up >= dataLevel) throw new Error(errorMsg("property/index", up)) + return dataPathArr[dataLevel - up] + } + if (up > dataLevel) throw new Error(errorMsg("data", up)) + data = dataNames[dataLevel - up] + if (!jsonPointer) return data + } + + let expr = data + const segments = jsonPointer.split("/") + for (const segment of segments) { + if (segment) { + data = _`${data}${getProperty(unescapeJsonPointer(segment))}` + expr = _`${expr} && ${data}` + } + } + return expr + + function errorMsg(pointerType: string, up: number): string { + return `Cannot access ${pointerType} ${up} levels up, current level is ${dataLevel}` + } +} diff --git a/website/node_modules/ajv/lib/compile/validate/keyword.ts b/website/node_modules/ajv/lib/compile/validate/keyword.ts new file mode 100644 index 00000000..f854aa71 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/validate/keyword.ts @@ -0,0 +1,171 @@ +import type {KeywordCxt} from "." +import type { + AnySchema, + SchemaValidateFunction, + AnyValidateFunction, + AddedKeywordDefinition, + MacroKeywordDefinition, + FuncKeywordDefinition, +} from "../../types" +import type {SchemaObjCxt} from ".." +import {_, nil, not, stringify, Code, Name, CodeGen} from "../codegen" +import N from "../names" +import type {JSONType} from "../rules" +import {callValidateCode} from "../../vocabularies/code" +import {extendErrors} from "../errors" + +type KeywordCompilationResult = AnySchema | SchemaValidateFunction | AnyValidateFunction + +export function macroKeywordCode(cxt: KeywordCxt, def: MacroKeywordDefinition): void { + const {gen, keyword, schema, parentSchema, it} = cxt + const macroSchema = def.macro.call(it.self, schema, parentSchema, it) + const schemaRef = useKeyword(gen, keyword, macroSchema) + if (it.opts.validateSchema !== false) it.self.validateSchema(macroSchema, true) + + const valid = gen.name("valid") + cxt.subschema( + { + schema: macroSchema, + schemaPath: nil, + errSchemaPath: `${it.errSchemaPath}/${keyword}`, + topSchemaRef: schemaRef, + compositeRule: true, + }, + valid + ) + cxt.pass(valid, () => cxt.error(true)) +} + +export function funcKeywordCode(cxt: KeywordCxt, def: FuncKeywordDefinition): void { + const {gen, keyword, schema, parentSchema, $data, it} = cxt + checkAsyncKeyword(it, def) + const validate = + !$data && def.compile ? def.compile.call(it.self, schema, parentSchema, it) : def.validate + const validateRef = useKeyword(gen, keyword, validate) + const valid = gen.let("valid") + cxt.block$data(valid, validateKeyword) + cxt.ok(def.valid ?? valid) + + function validateKeyword(): void { + if (def.errors === false) { + assignValid() + if (def.modifying) modifyData(cxt) + reportErrs(() => cxt.error()) + } else { + const ruleErrs = def.async ? validateAsync() : validateSync() + if (def.modifying) modifyData(cxt) + reportErrs(() => addErrs(cxt, ruleErrs)) + } + } + + function validateAsync(): Name { + const ruleErrs = gen.let("ruleErrs", null) + gen.try( + () => assignValid(_`await `), + (e) => + gen.assign(valid, false).if( + _`${e} instanceof ${it.ValidationError as Name}`, + () => gen.assign(ruleErrs, _`${e}.errors`), + () => gen.throw(e) + ) + ) + return ruleErrs + } + + function validateSync(): Code { + const validateErrs = _`${validateRef}.errors` + gen.assign(validateErrs, null) + assignValid(nil) + return validateErrs + } + + function assignValid(_await: Code = def.async ? _`await ` : nil): void { + const passCxt = it.opts.passContext ? N.this : N.self + const passSchema = !(("compile" in def && !$data) || def.schema === false) + gen.assign( + valid, + _`${_await}${callValidateCode(cxt, validateRef, passCxt, passSchema)}`, + def.modifying + ) + } + + function reportErrs(errors: () => void): void { + gen.if(not(def.valid ?? valid), errors) + } +} + +function modifyData(cxt: KeywordCxt): void { + const {gen, data, it} = cxt + gen.if(it.parentData, () => gen.assign(data, _`${it.parentData}[${it.parentDataProperty}]`)) +} + +function addErrs(cxt: KeywordCxt, errs: Code): void { + const {gen} = cxt + gen.if( + _`Array.isArray(${errs})`, + () => { + gen + .assign(N.vErrors, _`${N.vErrors} === null ? ${errs} : ${N.vErrors}.concat(${errs})`) + .assign(N.errors, _`${N.vErrors}.length`) + extendErrors(cxt) + }, + () => cxt.error() + ) +} + +function checkAsyncKeyword({schemaEnv}: SchemaObjCxt, def: FuncKeywordDefinition): void { + if (def.async && !schemaEnv.$async) throw new Error("async keyword in sync schema") +} + +function useKeyword(gen: CodeGen, keyword: string, result?: KeywordCompilationResult): Name { + if (result === undefined) throw new Error(`keyword "${keyword}" failed to compile`) + return gen.scopeValue( + "keyword", + typeof result == "function" ? {ref: result} : {ref: result, code: stringify(result)} + ) +} + +export function validSchemaType( + schema: unknown, + schemaType: JSONType[], + allowUndefined = false +): boolean { + // TODO add tests + return ( + !schemaType.length || + schemaType.some((st) => + st === "array" + ? Array.isArray(schema) + : st === "object" + ? schema && typeof schema == "object" && !Array.isArray(schema) + : typeof schema == st || (allowUndefined && typeof schema == "undefined") + ) + ) +} + +export function validateKeywordUsage( + {schema, opts, self, errSchemaPath}: SchemaObjCxt, + def: AddedKeywordDefinition, + keyword: string +): void { + /* istanbul ignore if */ + if (Array.isArray(def.keyword) ? !def.keyword.includes(keyword) : def.keyword !== keyword) { + throw new Error("ajv implementation error") + } + + const deps = def.dependencies + if (deps?.some((kwd) => !Object.prototype.hasOwnProperty.call(schema, kwd))) { + throw new Error(`parent schema must have dependencies of ${keyword}: ${deps.join(",")}`) + } + + if (def.validateSchema) { + const valid = def.validateSchema(schema[keyword]) + if (!valid) { + const msg = + `keyword "${keyword}" value is invalid at path "${errSchemaPath}": ` + + self.errorsText(def.validateSchema.errors) + if (opts.validateSchema === "log") self.logger.error(msg) + else throw new Error(msg) + } + } +} diff --git a/website/node_modules/ajv/lib/compile/validate/subschema.ts b/website/node_modules/ajv/lib/compile/validate/subschema.ts new file mode 100644 index 00000000..9072ed77 --- /dev/null +++ b/website/node_modules/ajv/lib/compile/validate/subschema.ts @@ -0,0 +1,135 @@ +import type {AnySchema} from "../../types" +import type {SchemaObjCxt} from ".." +import {_, str, getProperty, Code, Name} from "../codegen" +import {escapeFragment, getErrorPath, Type} from "../util" +import type {JSONType} from "../rules" + +export interface SubschemaContext { + // TODO use Optional? align with SchemCxt property types + schema: AnySchema + schemaPath: Code + errSchemaPath: string + topSchemaRef?: Code + errorPath?: Code + dataLevel?: number + dataTypes?: JSONType[] + data?: Name + parentData?: Name + parentDataProperty?: Code | number + dataNames?: Name[] + dataPathArr?: (Code | number)[] + propertyName?: Name + jtdDiscriminator?: string + jtdMetadata?: boolean + compositeRule?: true + createErrors?: boolean + allErrors?: boolean +} + +export type SubschemaArgs = Partial<{ + keyword: string + schemaProp: string | number + schema: AnySchema + schemaPath: Code + errSchemaPath: string + topSchemaRef: Code + data: Name | Code + dataProp: Code | string | number + dataTypes: JSONType[] + definedProperties: Set + propertyName: Name + dataPropType: Type + jtdDiscriminator: string + jtdMetadata: boolean + compositeRule: true + createErrors: boolean + allErrors: boolean +}> + +export function getSubschema( + it: SchemaObjCxt, + {keyword, schemaProp, schema, schemaPath, errSchemaPath, topSchemaRef}: SubschemaArgs +): SubschemaContext { + if (keyword !== undefined && schema !== undefined) { + throw new Error('both "keyword" and "schema" passed, only one allowed') + } + + if (keyword !== undefined) { + const sch = it.schema[keyword] + return schemaProp === undefined + ? { + schema: sch, + schemaPath: _`${it.schemaPath}${getProperty(keyword)}`, + errSchemaPath: `${it.errSchemaPath}/${keyword}`, + } + : { + schema: sch[schemaProp], + schemaPath: _`${it.schemaPath}${getProperty(keyword)}${getProperty(schemaProp)}`, + errSchemaPath: `${it.errSchemaPath}/${keyword}/${escapeFragment(schemaProp)}`, + } + } + + if (schema !== undefined) { + if (schemaPath === undefined || errSchemaPath === undefined || topSchemaRef === undefined) { + throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"') + } + return { + schema, + schemaPath, + topSchemaRef, + errSchemaPath, + } + } + + throw new Error('either "keyword" or "schema" must be passed') +} + +export function extendSubschemaData( + subschema: SubschemaContext, + it: SchemaObjCxt, + {dataProp, dataPropType: dpType, data, dataTypes, propertyName}: SubschemaArgs +): void { + if (data !== undefined && dataProp !== undefined) { + throw new Error('both "data" and "dataProp" passed, only one allowed') + } + + const {gen} = it + + if (dataProp !== undefined) { + const {errorPath, dataPathArr, opts} = it + const nextData = gen.let("data", _`${it.data}${getProperty(dataProp)}`, true) + dataContextProps(nextData) + subschema.errorPath = str`${errorPath}${getErrorPath(dataProp, dpType, opts.jsPropertySyntax)}` + subschema.parentDataProperty = _`${dataProp}` + subschema.dataPathArr = [...dataPathArr, subschema.parentDataProperty] + } + + if (data !== undefined) { + const nextData = data instanceof Name ? data : gen.let("data", data, true) // replaceable if used once? + dataContextProps(nextData) + if (propertyName !== undefined) subschema.propertyName = propertyName + // TODO something is possibly wrong here with not changing parentDataProperty and not appending dataPathArr + } + + if (dataTypes) subschema.dataTypes = dataTypes + + function dataContextProps(_nextData: Name): void { + subschema.data = _nextData + subschema.dataLevel = it.dataLevel + 1 + subschema.dataTypes = [] + it.definedProperties = new Set() + subschema.parentData = it.data + subschema.dataNames = [...it.dataNames, _nextData] + } +} + +export function extendSubschemaMode( + subschema: SubschemaContext, + {jtdDiscriminator, jtdMetadata, compositeRule, createErrors, allErrors}: SubschemaArgs +): void { + if (compositeRule !== undefined) subschema.compositeRule = compositeRule + if (createErrors !== undefined) subschema.createErrors = createErrors + if (allErrors !== undefined) subschema.allErrors = allErrors + subschema.jtdDiscriminator = jtdDiscriminator // not inherited + subschema.jtdMetadata = jtdMetadata // not inherited +} diff --git a/website/node_modules/ajv/lib/core.ts b/website/node_modules/ajv/lib/core.ts new file mode 100644 index 00000000..3686ffe7 --- /dev/null +++ b/website/node_modules/ajv/lib/core.ts @@ -0,0 +1,887 @@ +export { + Format, + FormatDefinition, + AsyncFormatDefinition, + KeywordDefinition, + KeywordErrorDefinition, + CodeKeywordDefinition, + MacroKeywordDefinition, + FuncKeywordDefinition, + Vocabulary, + Schema, + SchemaObject, + AnySchemaObject, + AsyncSchema, + AnySchema, + ValidateFunction, + AsyncValidateFunction, + AnyValidateFunction, + ErrorObject, + ErrorNoParams, +} from "./types" + +export {SchemaCxt, SchemaObjCxt} from "./compile" +export interface Plugin { + (ajv: Ajv, options?: Opts): Ajv + [prop: string]: any +} + +export {KeywordCxt} from "./compile/validate" +export {DefinedError} from "./vocabularies/errors" +export {JSONType} from "./compile/rules" +export {JSONSchemaType} from "./types/json-schema" +export {JTDSchemaType, SomeJTDSchemaType, JTDDataType} from "./types/jtd-schema" +export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./compile/codegen" + +import type { + Schema, + AnySchema, + AnySchemaObject, + SchemaObject, + AsyncSchema, + Vocabulary, + KeywordDefinition, + AddedKeywordDefinition, + AnyValidateFunction, + ValidateFunction, + AsyncValidateFunction, + ErrorObject, + Format, + AddedFormat, + RegExpEngine, + UriResolver, +} from "./types" +import type {JSONSchemaType} from "./types/json-schema" +import type {JTDSchemaType, SomeJTDSchemaType, JTDDataType} from "./types/jtd-schema" +import ValidationError from "./runtime/validation_error" +import MissingRefError from "./compile/ref_error" +import {getRules, ValidationRules, Rule, RuleGroup, JSONType} from "./compile/rules" +import {SchemaEnv, compileSchema, resolveSchema} from "./compile" +import {Code, ValueScope} from "./compile/codegen" +import {normalizeId, getSchemaRefs} from "./compile/resolve" +import {getJSONTypes} from "./compile/validate/dataType" +import {eachItem} from "./compile/util" +import * as $dataRefSchema from "./refs/data.json" + +import DefaultUriResolver from "./runtime/uri" + +const defaultRegExp: RegExpEngine = (str, flags) => new RegExp(str, flags) +defaultRegExp.code = "new RegExp" + +const META_IGNORE_OPTIONS: (keyof Options)[] = ["removeAdditional", "useDefaults", "coerceTypes"] +const EXT_SCOPE_NAMES = new Set([ + "validate", + "serialize", + "parse", + "wrapper", + "root", + "schema", + "keyword", + "pattern", + "formats", + "validate$data", + "func", + "obj", + "Error", +]) + +export type Options = CurrentOptions & DeprecatedOptions + +export interface CurrentOptions { + // strict mode options (NEW) + strict?: boolean | "log" + strictSchema?: boolean | "log" + strictNumbers?: boolean | "log" + strictTypes?: boolean | "log" + strictTuples?: boolean | "log" + strictRequired?: boolean | "log" + allowMatchingProperties?: boolean // disables a strict mode restriction + allowUnionTypes?: boolean + validateFormats?: boolean + // validation and reporting options: + $data?: boolean + allErrors?: boolean + verbose?: boolean + discriminator?: boolean + unicodeRegExp?: boolean + timestamp?: "string" | "date" // JTD only + parseDate?: boolean // JTD only + allowDate?: boolean // JTD only + $comment?: + | true + | ((comment: string, schemaPath?: string, rootSchema?: AnySchemaObject) => unknown) + formats?: {[Name in string]?: Format} + keywords?: Vocabulary + schemas?: AnySchema[] | {[Key in string]?: AnySchema} + logger?: Logger | false + loadSchema?: (uri: string) => Promise + // options to modify validated data: + removeAdditional?: boolean | "all" | "failing" + useDefaults?: boolean | "empty" + coerceTypes?: boolean | "array" + // advanced options: + next?: boolean // NEW + unevaluated?: boolean // NEW + dynamicRef?: boolean // NEW + schemaId?: "id" | "$id" + jtd?: boolean // NEW + meta?: SchemaObject | boolean + defaultMeta?: string | AnySchemaObject + validateSchema?: boolean | "log" + addUsedSchema?: boolean + inlineRefs?: boolean | number + passContext?: boolean + loopRequired?: number + loopEnum?: number // NEW + ownProperties?: boolean + multipleOfPrecision?: number + int32range?: boolean // JTD only + messages?: boolean + code?: CodeOptions // NEW + uriResolver?: UriResolver +} + +export interface CodeOptions { + es5?: boolean + esm?: boolean + lines?: boolean + optimize?: boolean | number + formats?: Code // code to require (or construct) map of available formats - for standalone code + source?: boolean + process?: (code: string, schema?: SchemaEnv) => string + regExp?: RegExpEngine +} + +interface InstanceCodeOptions extends CodeOptions { + regExp: RegExpEngine + optimize: number +} + +interface DeprecatedOptions { + /** @deprecated */ + ignoreKeywordsWithRef?: boolean + /** @deprecated */ + jsPropertySyntax?: boolean // added instead of jsonPointers + /** @deprecated */ + unicode?: boolean +} + +interface RemovedOptions { + format?: boolean + errorDataPath?: "object" | "property" + nullable?: boolean // "nullable" keyword is supported by default + jsonPointers?: boolean + extendRefs?: true | "ignore" | "fail" + missingRefs?: true | "ignore" | "fail" + processCode?: (code: string, schema?: SchemaEnv) => string + sourceCode?: boolean + strictDefaults?: boolean + strictKeywords?: boolean + uniqueItems?: boolean + unknownFormats?: true | string[] | "ignore" + cache?: any + serialize?: (schema: AnySchema) => unknown + ajvErrors?: boolean +} + +type OptionsInfo = { + [K in keyof T]-?: string | undefined +} + +const removedOptions: OptionsInfo = { + errorDataPath: "", + format: "`validateFormats: false` can be used instead.", + nullable: '"nullable" keyword is supported by default.', + jsonPointers: "Deprecated jsPropertySyntax can be used instead.", + extendRefs: "Deprecated ignoreKeywordsWithRef can be used instead.", + missingRefs: "Pass empty schema with $id that should be ignored to ajv.addSchema.", + processCode: "Use option `code: {process: (code, schemaEnv: object) => string}`", + sourceCode: "Use option `code: {source: true}`", + strictDefaults: "It is default now, see option `strict`.", + strictKeywords: "It is default now, see option `strict`.", + uniqueItems: '"uniqueItems" keyword is always validated.', + unknownFormats: "Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).", + cache: "Map is used as cache, schema object as key.", + serialize: "Map is used as cache, schema object as key.", + ajvErrors: "It is default now.", +} + +const deprecatedOptions: OptionsInfo = { + ignoreKeywordsWithRef: "", + jsPropertySyntax: "", + unicode: '"minLength"/"maxLength" account for unicode characters by default.', +} + +type RequiredInstanceOptions = { + [K in + | "strictSchema" + | "strictNumbers" + | "strictTypes" + | "strictTuples" + | "strictRequired" + | "inlineRefs" + | "loopRequired" + | "loopEnum" + | "meta" + | "messages" + | "schemaId" + | "addUsedSchema" + | "validateSchema" + | "validateFormats" + | "int32range" + | "unicodeRegExp" + | "uriResolver"]: NonNullable +} & {code: InstanceCodeOptions} + +export type InstanceOptions = Options & RequiredInstanceOptions + +const MAX_EXPRESSION = 200 + +// eslint-disable-next-line complexity +function requiredOptions(o: Options): RequiredInstanceOptions { + const s = o.strict + const _optz = o.code?.optimize + const optimize = _optz === true || _optz === undefined ? 1 : _optz || 0 + const regExp = o.code?.regExp ?? defaultRegExp + const uriResolver = o.uriResolver ?? DefaultUriResolver + return { + strictSchema: o.strictSchema ?? s ?? true, + strictNumbers: o.strictNumbers ?? s ?? true, + strictTypes: o.strictTypes ?? s ?? "log", + strictTuples: o.strictTuples ?? s ?? "log", + strictRequired: o.strictRequired ?? s ?? false, + code: o.code ? {...o.code, optimize, regExp} : {optimize, regExp}, + loopRequired: o.loopRequired ?? MAX_EXPRESSION, + loopEnum: o.loopEnum ?? MAX_EXPRESSION, + meta: o.meta ?? true, + messages: o.messages ?? true, + inlineRefs: o.inlineRefs ?? true, + schemaId: o.schemaId ?? "$id", + addUsedSchema: o.addUsedSchema ?? true, + validateSchema: o.validateSchema ?? true, + validateFormats: o.validateFormats ?? true, + unicodeRegExp: o.unicodeRegExp ?? true, + int32range: o.int32range ?? true, + uriResolver: uriResolver, + } +} + +export interface Logger { + log(...args: unknown[]): unknown + warn(...args: unknown[]): unknown + error(...args: unknown[]): unknown +} + +export default class Ajv { + opts: InstanceOptions + errors?: ErrorObject[] | null // errors from the last validation + logger: Logger + // shared external scope values for compiled functions + readonly scope: ValueScope + readonly schemas: {[Key in string]?: SchemaEnv} = {} + readonly refs: {[Ref in string]?: SchemaEnv | string} = {} + readonly formats: {[Name in string]?: AddedFormat} = {} + readonly RULES: ValidationRules + readonly _compilations: Set = new Set() + private readonly _loading: {[Ref in string]?: Promise} = {} + private readonly _cache: Map = new Map() + private readonly _metaOpts: InstanceOptions + + static ValidationError = ValidationError + static MissingRefError = MissingRefError + + constructor(opts: Options = {}) { + opts = this.opts = {...opts, ...requiredOptions(opts)} + const {es5, lines} = this.opts.code + + this.scope = new ValueScope({scope: {}, prefixes: EXT_SCOPE_NAMES, es5, lines}) + this.logger = getLogger(opts.logger) + const formatOpt = opts.validateFormats + opts.validateFormats = false + + this.RULES = getRules() + checkOptions.call(this, removedOptions, opts, "NOT SUPPORTED") + checkOptions.call(this, deprecatedOptions, opts, "DEPRECATED", "warn") + this._metaOpts = getMetaSchemaOptions.call(this) + + if (opts.formats) addInitialFormats.call(this) + this._addVocabularies() + this._addDefaultMetaSchema() + if (opts.keywords) addInitialKeywords.call(this, opts.keywords) + if (typeof opts.meta == "object") this.addMetaSchema(opts.meta) + addInitialSchemas.call(this) + opts.validateFormats = formatOpt + } + + _addVocabularies(): void { + this.addKeyword("$async") + } + + _addDefaultMetaSchema(): void { + const {$data, meta, schemaId} = this.opts + let _dataRefSchema: SchemaObject = $dataRefSchema + if (schemaId === "id") { + _dataRefSchema = {...$dataRefSchema} + _dataRefSchema.id = _dataRefSchema.$id + delete _dataRefSchema.$id + } + if (meta && $data) this.addMetaSchema(_dataRefSchema, _dataRefSchema[schemaId], false) + } + + defaultMeta(): string | AnySchemaObject | undefined { + const {meta, schemaId} = this.opts + return (this.opts.defaultMeta = typeof meta == "object" ? meta[schemaId] || meta : undefined) + } + + // Validate data using schema + // AnySchema will be compiled and cached using schema itself as a key for Map + validate(schema: Schema | string, data: unknown): boolean + validate(schemaKeyRef: AnySchema | string, data: unknown): boolean | Promise + validate(schema: Schema | JSONSchemaType | string, data: unknown): data is T + // Separated for type inference to work + // eslint-disable-next-line @typescript-eslint/unified-signatures + validate(schema: JTDSchemaType, data: unknown): data is T + // This overload is only intended for typescript inference, the first + // argument prevents manual type annotation from matching this overload + validate( + schema: T, + data: unknown + ): data is JTDDataType + validate(schema: AsyncSchema, data: unknown | T): Promise + validate(schemaKeyRef: AnySchema | string, data: unknown): data is T | Promise + validate( + schemaKeyRef: AnySchema | string, // key, ref or schema object + data: unknown | T // to be validated + ): boolean | Promise { + let v: AnyValidateFunction | undefined + if (typeof schemaKeyRef == "string") { + v = this.getSchema(schemaKeyRef) + if (!v) throw new Error(`no schema with key or ref "${schemaKeyRef}"`) + } else { + v = this.compile(schemaKeyRef) + } + + const valid = v(data) + if (!("$async" in v)) this.errors = v.errors + return valid + } + + // Create validation function for passed schema + // _meta: true if schema is a meta-schema. Used internally to compile meta schemas of user-defined keywords. + compile(schema: Schema | JSONSchemaType, _meta?: boolean): ValidateFunction + // Separated for type inference to work + // eslint-disable-next-line @typescript-eslint/unified-signatures + compile(schema: JTDSchemaType, _meta?: boolean): ValidateFunction + // This overload is only intended for typescript inference, the first + // argument prevents manual type annotation from matching this overload + compile( + schema: T, + _meta?: boolean + ): ValidateFunction> + compile(schema: AsyncSchema, _meta?: boolean): AsyncValidateFunction + compile(schema: AnySchema, _meta?: boolean): AnyValidateFunction + compile(schema: AnySchema, _meta?: boolean): AnyValidateFunction { + const sch = this._addSchema(schema, _meta) + return (sch.validate || this._compileSchemaEnv(sch)) as AnyValidateFunction + } + + // Creates validating function for passed schema with asynchronous loading of missing schemas. + // `loadSchema` option should be a function that accepts schema uri and returns promise that resolves with the schema. + // TODO allow passing schema URI + // meta - optional true to compile meta-schema + compileAsync( + schema: SchemaObject | JSONSchemaType, + _meta?: boolean + ): Promise> + // Separated for type inference to work + // eslint-disable-next-line @typescript-eslint/unified-signatures + compileAsync(schema: JTDSchemaType, _meta?: boolean): Promise> + compileAsync(schema: AsyncSchema, meta?: boolean): Promise> + // eslint-disable-next-line @typescript-eslint/unified-signatures + compileAsync( + schema: AnySchemaObject, + meta?: boolean + ): Promise> + compileAsync( + schema: AnySchemaObject, + meta?: boolean + ): Promise> { + if (typeof this.opts.loadSchema != "function") { + throw new Error("options.loadSchema should be a function") + } + const {loadSchema} = this.opts + return runCompileAsync.call(this, schema, meta) + + async function runCompileAsync( + this: Ajv, + _schema: AnySchemaObject, + _meta?: boolean + ): Promise { + await loadMetaSchema.call(this, _schema.$schema) + const sch = this._addSchema(_schema, _meta) + return sch.validate || _compileAsync.call(this, sch) + } + + async function loadMetaSchema(this: Ajv, $ref?: string): Promise { + if ($ref && !this.getSchema($ref)) { + await runCompileAsync.call(this, {$ref}, true) + } + } + + async function _compileAsync(this: Ajv, sch: SchemaEnv): Promise { + try { + return this._compileSchemaEnv(sch) + } catch (e) { + if (!(e instanceof MissingRefError)) throw e + checkLoaded.call(this, e) + await loadMissingSchema.call(this, e.missingSchema) + return _compileAsync.call(this, sch) + } + } + + function checkLoaded(this: Ajv, {missingSchema: ref, missingRef}: MissingRefError): void { + if (this.refs[ref]) { + throw new Error(`AnySchema ${ref} is loaded but ${missingRef} cannot be resolved`) + } + } + + async function loadMissingSchema(this: Ajv, ref: string): Promise { + const _schema = await _loadSchema.call(this, ref) + if (!this.refs[ref]) await loadMetaSchema.call(this, _schema.$schema) + if (!this.refs[ref]) this.addSchema(_schema, ref, meta) + } + + async function _loadSchema(this: Ajv, ref: string): Promise { + const p = this._loading[ref] + if (p) return p + try { + return await (this._loading[ref] = loadSchema(ref)) + } finally { + delete this._loading[ref] + } + } + } + + // Adds schema to the instance + addSchema( + schema: AnySchema | AnySchema[], // If array is passed, `key` will be ignored + key?: string, // Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`. + _meta?: boolean, // true if schema is a meta-schema. Used internally, addMetaSchema should be used instead. + _validateSchema = this.opts.validateSchema // false to skip schema validation. Used internally, option validateSchema should be used instead. + ): Ajv { + if (Array.isArray(schema)) { + for (const sch of schema) this.addSchema(sch, undefined, _meta, _validateSchema) + return this + } + let id: string | undefined + if (typeof schema === "object") { + const {schemaId} = this.opts + id = schema[schemaId] + if (id !== undefined && typeof id != "string") { + throw new Error(`schema ${schemaId} must be string`) + } + } + key = normalizeId(key || id) + this._checkUnique(key) + this.schemas[key] = this._addSchema(schema, _meta, key, _validateSchema, true) + return this + } + + // Add schema that will be used to validate other schemas + // options in META_IGNORE_OPTIONS are alway set to false + addMetaSchema( + schema: AnySchemaObject, + key?: string, // schema key + _validateSchema = this.opts.validateSchema // false to skip schema validation, can be used to override validateSchema option for meta-schema + ): Ajv { + this.addSchema(schema, key, true, _validateSchema) + return this + } + + // Validate schema against its meta-schema + validateSchema(schema: AnySchema, throwOrLogError?: boolean): boolean | Promise { + if (typeof schema == "boolean") return true + let $schema: string | AnySchemaObject | undefined + $schema = schema.$schema + if ($schema !== undefined && typeof $schema != "string") { + throw new Error("$schema must be a string") + } + $schema = $schema || this.opts.defaultMeta || this.defaultMeta() + if (!$schema) { + this.logger.warn("meta-schema not available") + this.errors = null + return true + } + const valid = this.validate($schema, schema) + if (!valid && throwOrLogError) { + const message = "schema is invalid: " + this.errorsText() + if (this.opts.validateSchema === "log") this.logger.error(message) + else throw new Error(message) + } + return valid + } + + // Get compiled schema by `key` or `ref`. + // (`key` that was passed to `addSchema` or full schema reference - `schema.$id` or resolved id) + getSchema(keyRef: string): AnyValidateFunction | undefined { + let sch + while (typeof (sch = getSchEnv.call(this, keyRef)) == "string") keyRef = sch + if (sch === undefined) { + const {schemaId} = this.opts + const root = new SchemaEnv({schema: {}, schemaId}) + sch = resolveSchema.call(this, root, keyRef) + if (!sch) return + this.refs[keyRef] = sch + } + return (sch.validate || this._compileSchemaEnv(sch)) as AnyValidateFunction | undefined + } + + // Remove cached schema(s). + // If no parameter is passed all schemas but meta-schemas are removed. + // If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed. + // Even if schema is referenced by other schemas it still can be removed as other schemas have local references. + removeSchema(schemaKeyRef?: AnySchema | string | RegExp): Ajv { + if (schemaKeyRef instanceof RegExp) { + this._removeAllSchemas(this.schemas, schemaKeyRef) + this._removeAllSchemas(this.refs, schemaKeyRef) + return this + } + switch (typeof schemaKeyRef) { + case "undefined": + this._removeAllSchemas(this.schemas) + this._removeAllSchemas(this.refs) + this._cache.clear() + return this + case "string": { + const sch = getSchEnv.call(this, schemaKeyRef) + if (typeof sch == "object") this._cache.delete(sch.schema) + delete this.schemas[schemaKeyRef] + delete this.refs[schemaKeyRef] + return this + } + case "object": { + const cacheKey = schemaKeyRef + this._cache.delete(cacheKey) + let id = schemaKeyRef[this.opts.schemaId] + if (id) { + id = normalizeId(id) + delete this.schemas[id] + delete this.refs[id] + } + return this + } + default: + throw new Error("ajv.removeSchema: invalid parameter") + } + } + + // add "vocabulary" - a collection of keywords + addVocabulary(definitions: Vocabulary): Ajv { + for (const def of definitions) this.addKeyword(def) + return this + } + + addKeyword( + kwdOrDef: string | KeywordDefinition, + def?: KeywordDefinition // deprecated + ): Ajv { + let keyword: string | string[] + if (typeof kwdOrDef == "string") { + keyword = kwdOrDef + if (typeof def == "object") { + this.logger.warn("these parameters are deprecated, see docs for addKeyword") + def.keyword = keyword + } + } else if (typeof kwdOrDef == "object" && def === undefined) { + def = kwdOrDef + keyword = def.keyword + if (Array.isArray(keyword) && !keyword.length) { + throw new Error("addKeywords: keyword must be string or non-empty array") + } + } else { + throw new Error("invalid addKeywords parameters") + } + + checkKeyword.call(this, keyword, def) + if (!def) { + eachItem(keyword, (kwd) => addRule.call(this, kwd)) + return this + } + keywordMetaschema.call(this, def) + const definition: AddedKeywordDefinition = { + ...def, + type: getJSONTypes(def.type), + schemaType: getJSONTypes(def.schemaType), + } + eachItem( + keyword, + definition.type.length === 0 + ? (k) => addRule.call(this, k, definition) + : (k) => definition.type.forEach((t) => addRule.call(this, k, definition, t)) + ) + return this + } + + getKeyword(keyword: string): AddedKeywordDefinition | boolean { + const rule = this.RULES.all[keyword] + return typeof rule == "object" ? rule.definition : !!rule + } + + // Remove keyword + removeKeyword(keyword: string): Ajv { + // TODO return type should be Ajv + const {RULES} = this + delete RULES.keywords[keyword] + delete RULES.all[keyword] + for (const group of RULES.rules) { + const i = group.rules.findIndex((rule) => rule.keyword === keyword) + if (i >= 0) group.rules.splice(i, 1) + } + return this + } + + // Add format + addFormat(name: string, format: Format): Ajv { + if (typeof format == "string") format = new RegExp(format) + this.formats[name] = format + return this + } + + errorsText( + errors: ErrorObject[] | null | undefined = this.errors, // optional array of validation errors + {separator = ", ", dataVar = "data"}: ErrorsTextOptions = {} // optional options with properties `separator` and `dataVar` + ): string { + if (!errors || errors.length === 0) return "No errors" + return errors + .map((e) => `${dataVar}${e.instancePath} ${e.message}`) + .reduce((text, msg) => text + separator + msg) + } + + $dataMetaSchema(metaSchema: AnySchemaObject, keywordsJsonPointers: string[]): AnySchemaObject { + const rules = this.RULES.all + metaSchema = JSON.parse(JSON.stringify(metaSchema)) + for (const jsonPointer of keywordsJsonPointers) { + const segments = jsonPointer.split("/").slice(1) // first segment is an empty string + let keywords = metaSchema + for (const seg of segments) keywords = keywords[seg] as AnySchemaObject + + for (const key in rules) { + const rule = rules[key] + if (typeof rule != "object") continue + const {$data} = rule.definition + const schema = keywords[key] as AnySchemaObject | undefined + if ($data && schema) keywords[key] = schemaOrData(schema) + } + } + + return metaSchema + } + + private _removeAllSchemas(schemas: {[Ref in string]?: SchemaEnv | string}, regex?: RegExp): void { + for (const keyRef in schemas) { + const sch = schemas[keyRef] + if (!regex || regex.test(keyRef)) { + if (typeof sch == "string") { + delete schemas[keyRef] + } else if (sch && !sch.meta) { + this._cache.delete(sch.schema) + delete schemas[keyRef] + } + } + } + } + + _addSchema( + schema: AnySchema, + meta?: boolean, + baseId?: string, + validateSchema = this.opts.validateSchema, + addSchema = this.opts.addUsedSchema + ): SchemaEnv { + let id: string | undefined + const {schemaId} = this.opts + if (typeof schema == "object") { + id = schema[schemaId] + } else { + if (this.opts.jtd) throw new Error("schema must be object") + else if (typeof schema != "boolean") throw new Error("schema must be object or boolean") + } + let sch = this._cache.get(schema) + if (sch !== undefined) return sch + + baseId = normalizeId(id || baseId) + const localRefs = getSchemaRefs.call(this, schema, baseId) + sch = new SchemaEnv({schema, schemaId, meta, baseId, localRefs}) + this._cache.set(sch.schema, sch) + if (addSchema && !baseId.startsWith("#")) { + // TODO atm it is allowed to overwrite schemas without id (instead of not adding them) + if (baseId) this._checkUnique(baseId) + this.refs[baseId] = sch + } + if (validateSchema) this.validateSchema(schema, true) + return sch + } + + private _checkUnique(id: string): void { + if (this.schemas[id] || this.refs[id]) { + throw new Error(`schema with key or id "${id}" already exists`) + } + } + + private _compileSchemaEnv(sch: SchemaEnv): AnyValidateFunction { + if (sch.meta) this._compileMetaSchema(sch) + else compileSchema.call(this, sch) + + /* istanbul ignore if */ + if (!sch.validate) throw new Error("ajv implementation error") + return sch.validate + } + + private _compileMetaSchema(sch: SchemaEnv): void { + const currentOpts = this.opts + this.opts = this._metaOpts + try { + compileSchema.call(this, sch) + } finally { + this.opts = currentOpts + } + } +} + +export interface ErrorsTextOptions { + separator?: string + dataVar?: string +} + +function checkOptions( + this: Ajv, + checkOpts: OptionsInfo, + options: Options & RemovedOptions, + msg: string, + log: "warn" | "error" = "error" +): void { + for (const key in checkOpts) { + const opt = key as keyof typeof checkOpts + if (opt in options) this.logger[log](`${msg}: option ${key}. ${checkOpts[opt]}`) + } +} + +function getSchEnv(this: Ajv, keyRef: string): SchemaEnv | string | undefined { + keyRef = normalizeId(keyRef) // TODO tests fail without this line + return this.schemas[keyRef] || this.refs[keyRef] +} + +function addInitialSchemas(this: Ajv): void { + const optsSchemas = this.opts.schemas + if (!optsSchemas) return + if (Array.isArray(optsSchemas)) this.addSchema(optsSchemas) + else for (const key in optsSchemas) this.addSchema(optsSchemas[key] as AnySchema, key) +} + +function addInitialFormats(this: Ajv): void { + for (const name in this.opts.formats) { + const format = this.opts.formats[name] + if (format) this.addFormat(name, format) + } +} + +function addInitialKeywords( + this: Ajv, + defs: Vocabulary | {[K in string]?: KeywordDefinition} +): void { + if (Array.isArray(defs)) { + this.addVocabulary(defs) + return + } + this.logger.warn("keywords option as map is deprecated, pass array") + for (const keyword in defs) { + const def = defs[keyword] as KeywordDefinition + if (!def.keyword) def.keyword = keyword + this.addKeyword(def) + } +} + +function getMetaSchemaOptions(this: Ajv): InstanceOptions { + const metaOpts = {...this.opts} + for (const opt of META_IGNORE_OPTIONS) delete metaOpts[opt] + return metaOpts +} + +const noLogs = {log() {}, warn() {}, error() {}} + +function getLogger(logger?: Partial | false): Logger { + if (logger === false) return noLogs + if (logger === undefined) return console + if (logger.log && logger.warn && logger.error) return logger as Logger + throw new Error("logger must implement log, warn and error methods") +} + +const KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i + +function checkKeyword(this: Ajv, keyword: string | string[], def?: KeywordDefinition): void { + const {RULES} = this + eachItem(keyword, (kwd) => { + if (RULES.keywords[kwd]) throw new Error(`Keyword ${kwd} is already defined`) + if (!KEYWORD_NAME.test(kwd)) throw new Error(`Keyword ${kwd} has invalid name`) + }) + if (!def) return + if (def.$data && !("code" in def || "validate" in def)) { + throw new Error('$data keyword must have "code" or "validate" function') + } +} + +function addRule( + this: Ajv, + keyword: string, + definition?: AddedKeywordDefinition, + dataType?: JSONType +): void { + const post = definition?.post + if (dataType && post) throw new Error('keyword with "post" flag cannot have "type"') + const {RULES} = this + let ruleGroup = post ? RULES.post : RULES.rules.find(({type: t}) => t === dataType) + if (!ruleGroup) { + ruleGroup = {type: dataType, rules: []} + RULES.rules.push(ruleGroup) + } + RULES.keywords[keyword] = true + if (!definition) return + + const rule: Rule = { + keyword, + definition: { + ...definition, + type: getJSONTypes(definition.type), + schemaType: getJSONTypes(definition.schemaType), + }, + } + if (definition.before) addBeforeRule.call(this, ruleGroup, rule, definition.before) + else ruleGroup.rules.push(rule) + RULES.all[keyword] = rule + definition.implements?.forEach((kwd) => this.addKeyword(kwd)) +} + +function addBeforeRule(this: Ajv, ruleGroup: RuleGroup, rule: Rule, before: string): void { + const i = ruleGroup.rules.findIndex((_rule) => _rule.keyword === before) + if (i >= 0) { + ruleGroup.rules.splice(i, 0, rule) + } else { + ruleGroup.rules.push(rule) + this.logger.warn(`rule ${before} is not defined`) + } +} + +function keywordMetaschema(this: Ajv, def: KeywordDefinition): void { + let {metaSchema} = def + if (metaSchema === undefined) return + if (def.$data && this.opts.$data) metaSchema = schemaOrData(metaSchema) + def.validateSchema = this.compile(metaSchema, true) +} + +const $dataRef = { + $ref: "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#", +} + +function schemaOrData(schema: AnySchema): AnySchemaObject { + return {anyOf: [schema, $dataRef]} +} diff --git a/website/node_modules/ajv/lib/jtd.ts b/website/node_modules/ajv/lib/jtd.ts new file mode 100644 index 00000000..96eb7b9d --- /dev/null +++ b/website/node_modules/ajv/lib/jtd.ts @@ -0,0 +1,131 @@ +import type {AnySchemaObject, SchemaObject, JTDParser} from "./types" +import type {JTDSchemaType, SomeJTDSchemaType, JTDDataType} from "./types/jtd-schema" +import AjvCore, {CurrentOptions} from "./core" +import jtdVocabulary from "./vocabularies/jtd" +import jtdMetaSchema from "./refs/jtd-schema" +import compileSerializer from "./compile/jtd/serialize" +import compileParser from "./compile/jtd/parse" +import {SchemaEnv} from "./compile" + +const META_SCHEMA_ID = "JTD-meta-schema" + +type JTDOptions = CurrentOptions & { + // strict mode options not supported with JTD: + strict?: never + allowMatchingProperties?: never + allowUnionTypes?: never + validateFormats?: never + // validation and reporting options not supported with JTD: + $data?: never + verbose?: boolean + $comment?: never + formats?: never + loadSchema?: never + // options to modify validated data: + useDefaults?: never + coerceTypes?: never + // advanced options: + next?: never + unevaluated?: never + dynamicRef?: never + meta?: boolean + defaultMeta?: never + inlineRefs?: boolean + loopRequired?: never + multipleOfPrecision?: never +} + +class Ajv extends AjvCore { + constructor(opts: JTDOptions = {}) { + super({ + ...opts, + jtd: true, + }) + } + + _addVocabularies(): void { + super._addVocabularies() + this.addVocabulary(jtdVocabulary) + } + + _addDefaultMetaSchema(): void { + super._addDefaultMetaSchema() + if (!this.opts.meta) return + this.addMetaSchema(jtdMetaSchema, META_SCHEMA_ID, false) + } + + defaultMeta(): string | AnySchemaObject | undefined { + return (this.opts.defaultMeta = + super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined)) + } + + compileSerializer(schema: SchemaObject): (data: T) => string + // Separated for type inference to work + // eslint-disable-next-line @typescript-eslint/unified-signatures + compileSerializer(schema: JTDSchemaType): (data: T) => string + compileSerializer(schema: SchemaObject): (data: T) => string { + const sch = this._addSchema(schema) + return sch.serialize || this._compileSerializer(sch) + } + + compileParser(schema: SchemaObject): JTDParser + // Separated for type inference to work + // eslint-disable-next-line @typescript-eslint/unified-signatures + compileParser(schema: JTDSchemaType): JTDParser + compileParser(schema: SchemaObject): JTDParser { + const sch = this._addSchema(schema) + return (sch.parse || this._compileParser(sch)) as JTDParser + } + + private _compileSerializer(sch: SchemaEnv): (data: T) => string { + compileSerializer.call(this, sch, (sch.schema as AnySchemaObject).definitions || {}) + /* istanbul ignore if */ + if (!sch.serialize) throw new Error("ajv implementation error") + return sch.serialize + } + + private _compileParser(sch: SchemaEnv): JTDParser { + compileParser.call(this, sch, (sch.schema as AnySchemaObject).definitions || {}) + /* istanbul ignore if */ + if (!sch.parse) throw new Error("ajv implementation error") + return sch.parse + } +} + +module.exports = exports = Ajv +Object.defineProperty(exports, "__esModule", {value: true}) + +export default Ajv + +export { + Format, + FormatDefinition, + AsyncFormatDefinition, + KeywordDefinition, + KeywordErrorDefinition, + CodeKeywordDefinition, + MacroKeywordDefinition, + FuncKeywordDefinition, + Vocabulary, + Schema, + SchemaObject, + AnySchemaObject, + AsyncSchema, + AnySchema, + ValidateFunction, + AsyncValidateFunction, + ErrorObject, + ErrorNoParams, + JTDParser, +} from "./types" + +export {Plugin, Options, CodeOptions, InstanceOptions, Logger, ErrorsTextOptions} from "./core" +export {SchemaCxt, SchemaObjCxt} from "./compile" +export {KeywordCxt} from "./compile/validate" +export {JTDErrorObject} from "./vocabularies/jtd" +export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./compile/codegen" + +export {JTDSchemaType, SomeJTDSchemaType, JTDDataType} +export {JTDOptions} +export {default as ValidationError} from "./runtime/validation_error" +export {default as MissingRefError} from "./compile/ref_error" diff --git a/website/node_modules/ajv/lib/refs/data.json b/website/node_modules/ajv/lib/refs/data.json new file mode 100644 index 00000000..9ffc9f5c --- /dev/null +++ b/website/node_modules/ajv/lib/refs/data.json @@ -0,0 +1,13 @@ +{ + "$id": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#", + "description": "Meta-schema for $data reference (JSON AnySchema extension proposal)", + "type": "object", + "required": ["$data"], + "properties": { + "$data": { + "type": "string", + "anyOf": [{"format": "relative-json-pointer"}, {"format": "json-pointer"}] + } + }, + "additionalProperties": false +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2019-09/index.ts b/website/node_modules/ajv/lib/refs/json-schema-2019-09/index.ts new file mode 100644 index 00000000..b6ea7195 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2019-09/index.ts @@ -0,0 +1,28 @@ +import type Ajv from "../../core" +import type {AnySchemaObject} from "../../types" +import * as metaSchema from "./schema.json" +import * as applicator from "./meta/applicator.json" +import * as content from "./meta/content.json" +import * as core from "./meta/core.json" +import * as format from "./meta/format.json" +import * as metadata from "./meta/meta-data.json" +import * as validation from "./meta/validation.json" + +const META_SUPPORT_DATA = ["/properties"] + +export default function addMetaSchema2019(this: Ajv, $data?: boolean): Ajv { + ;[ + metaSchema, + applicator, + content, + core, + with$data(this, format), + metadata, + with$data(this, validation), + ].forEach((sch) => this.addMetaSchema(sch, undefined, false)) + return this + + function with$data(ajv: Ajv, sch: AnySchemaObject): AnySchemaObject { + return $data ? ajv.$dataMetaSchema(sch, META_SUPPORT_DATA) : sch + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/applicator.json b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/applicator.json new file mode 100644 index 00000000..c5e91cf2 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/applicator.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/applicator", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/applicator": true + }, + "$recursiveAnchor": true, + + "title": "Applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "additionalItems": {"$recursiveRef": "#"}, + "unevaluatedItems": {"$recursiveRef": "#"}, + "items": { + "anyOf": [{"$recursiveRef": "#"}, {"$ref": "#/$defs/schemaArray"}] + }, + "contains": {"$recursiveRef": "#"}, + "additionalProperties": {"$recursiveRef": "#"}, + "unevaluatedProperties": {"$recursiveRef": "#"}, + "properties": { + "type": "object", + "additionalProperties": {"$recursiveRef": "#"}, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": {"$recursiveRef": "#"}, + "propertyNames": {"format": "regex"}, + "default": {} + }, + "dependentSchemas": { + "type": "object", + "additionalProperties": { + "$recursiveRef": "#" + } + }, + "propertyNames": {"$recursiveRef": "#"}, + "if": {"$recursiveRef": "#"}, + "then": {"$recursiveRef": "#"}, + "else": {"$recursiveRef": "#"}, + "allOf": {"$ref": "#/$defs/schemaArray"}, + "anyOf": {"$ref": "#/$defs/schemaArray"}, + "oneOf": {"$ref": "#/$defs/schemaArray"}, + "not": {"$recursiveRef": "#"} + }, + "$defs": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$recursiveRef": "#"} + } + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/content.json b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/content.json new file mode 100644 index 00000000..b8f63734 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/content.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/content", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/content": true + }, + "$recursiveAnchor": true, + + "title": "Content vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "contentMediaType": {"type": "string"}, + "contentEncoding": {"type": "string"}, + "contentSchema": {"$recursiveRef": "#"} + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/core.json b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/core.json new file mode 100644 index 00000000..f71adbff --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/core.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/core", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/core": true + }, + "$recursiveAnchor": true, + + "title": "Core vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference", + "$comment": "Non-empty fragments not allowed.", + "pattern": "^[^#]*#?$" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$anchor": { + "type": "string", + "pattern": "^[A-Za-z][-A-Za-z0-9.:_]*$" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$recursiveRef": { + "type": "string", + "format": "uri-reference" + }, + "$recursiveAnchor": { + "type": "boolean", + "default": false + }, + "$vocabulary": { + "type": "object", + "propertyNames": { + "type": "string", + "format": "uri" + }, + "additionalProperties": { + "type": "boolean" + } + }, + "$comment": { + "type": "string" + }, + "$defs": { + "type": "object", + "additionalProperties": {"$recursiveRef": "#"}, + "default": {} + } + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/format.json b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/format.json new file mode 100644 index 00000000..03ccfce2 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/format.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/format", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/format": true + }, + "$recursiveAnchor": true, + + "title": "Format vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "format": {"type": "string"} + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/meta-data.json b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/meta-data.json new file mode 100644 index 00000000..0e194326 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/meta-data.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/meta-data", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/meta-data": true + }, + "$recursiveAnchor": true, + + "title": "Meta-data vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "deprecated": { + "type": "boolean", + "default": false + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + } + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/validation.json b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/validation.json new file mode 100644 index 00000000..7027a127 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2019-09/meta/validation.json @@ -0,0 +1,90 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/validation", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/validation": true + }, + "$recursiveAnchor": true, + + "title": "Validation vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": {"$ref": "#/$defs/nonNegativeInteger"}, + "minLength": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": {"$ref": "#/$defs/nonNegativeInteger"}, + "minItems": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxContains": {"$ref": "#/$defs/nonNegativeInteger"}, + "minContains": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 1 + }, + "maxProperties": {"$ref": "#/$defs/nonNegativeInteger"}, + "minProperties": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "required": {"$ref": "#/$defs/stringArray"}, + "dependentRequired": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/stringArray" + } + }, + "const": true, + "enum": { + "type": "array", + "items": true + }, + "type": { + "anyOf": [ + {"$ref": "#/$defs/simpleTypes"}, + { + "type": "array", + "items": {"$ref": "#/$defs/simpleTypes"}, + "minItems": 1, + "uniqueItems": true + } + ] + } + }, + "$defs": { + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 0 + }, + "simpleTypes": { + "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + "stringArray": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true, + "default": [] + } + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2019-09/schema.json b/website/node_modules/ajv/lib/refs/json-schema-2019-09/schema.json new file mode 100644 index 00000000..54eb7157 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2019-09/schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/schema", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/core": true, + "https://json-schema.org/draft/2019-09/vocab/applicator": true, + "https://json-schema.org/draft/2019-09/vocab/validation": true, + "https://json-schema.org/draft/2019-09/vocab/meta-data": true, + "https://json-schema.org/draft/2019-09/vocab/format": false, + "https://json-schema.org/draft/2019-09/vocab/content": true + }, + "$recursiveAnchor": true, + + "title": "Core and Validation specifications meta-schema", + "allOf": [ + {"$ref": "meta/core"}, + {"$ref": "meta/applicator"}, + {"$ref": "meta/validation"}, + {"$ref": "meta/meta-data"}, + {"$ref": "meta/format"}, + {"$ref": "meta/content"} + ], + "type": ["object", "boolean"], + "properties": { + "definitions": { + "$comment": "While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.", + "type": "object", + "additionalProperties": {"$recursiveRef": "#"}, + "default": {} + }, + "dependencies": { + "$comment": "\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"", + "type": "object", + "additionalProperties": { + "anyOf": [{"$recursiveRef": "#"}, {"$ref": "meta/validation#/$defs/stringArray"}] + } + } + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2020-12/index.ts b/website/node_modules/ajv/lib/refs/json-schema-2020-12/index.ts new file mode 100644 index 00000000..8e850d08 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2020-12/index.ts @@ -0,0 +1,30 @@ +import type Ajv from "../../core" +import type {AnySchemaObject} from "../../types" +import * as metaSchema from "./schema.json" +import * as applicator from "./meta/applicator.json" +import * as unevaluated from "./meta/unevaluated.json" +import * as content from "./meta/content.json" +import * as core from "./meta/core.json" +import * as format from "./meta/format-annotation.json" +import * as metadata from "./meta/meta-data.json" +import * as validation from "./meta/validation.json" + +const META_SUPPORT_DATA = ["/properties"] + +export default function addMetaSchema2020(this: Ajv, $data?: boolean): Ajv { + ;[ + metaSchema, + applicator, + unevaluated, + content, + core, + with$data(this, format), + metadata, + with$data(this, validation), + ].forEach((sch) => this.addMetaSchema(sch, undefined, false)) + return this + + function with$data(ajv: Ajv, sch: AnySchemaObject): AnySchemaObject { + return $data ? ajv.$dataMetaSchema(sch, META_SUPPORT_DATA) : sch + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/applicator.json b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/applicator.json new file mode 100644 index 00000000..674c913d --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/applicator.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/applicator", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/applicator": true + }, + "$dynamicAnchor": "meta", + + "title": "Applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "prefixItems": {"$ref": "#/$defs/schemaArray"}, + "items": {"$dynamicRef": "#meta"}, + "contains": {"$dynamicRef": "#meta"}, + "additionalProperties": {"$dynamicRef": "#meta"}, + "properties": { + "type": "object", + "additionalProperties": {"$dynamicRef": "#meta"}, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": {"$dynamicRef": "#meta"}, + "propertyNames": {"format": "regex"}, + "default": {} + }, + "dependentSchemas": { + "type": "object", + "additionalProperties": {"$dynamicRef": "#meta"}, + "default": {} + }, + "propertyNames": {"$dynamicRef": "#meta"}, + "if": {"$dynamicRef": "#meta"}, + "then": {"$dynamicRef": "#meta"}, + "else": {"$dynamicRef": "#meta"}, + "allOf": {"$ref": "#/$defs/schemaArray"}, + "anyOf": {"$ref": "#/$defs/schemaArray"}, + "oneOf": {"$ref": "#/$defs/schemaArray"}, + "not": {"$dynamicRef": "#meta"} + }, + "$defs": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$dynamicRef": "#meta"} + } + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/content.json b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/content.json new file mode 100644 index 00000000..2ae23ddb --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/content.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/content", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/content": true + }, + "$dynamicAnchor": "meta", + + "title": "Content vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "contentEncoding": {"type": "string"}, + "contentMediaType": {"type": "string"}, + "contentSchema": {"$dynamicRef": "#meta"} + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/core.json b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/core.json new file mode 100644 index 00000000..4c8e5cb6 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/core.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/core", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true + }, + "$dynamicAnchor": "meta", + + "title": "Core vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "$id": { + "$ref": "#/$defs/uriReferenceString", + "$comment": "Non-empty fragments not allowed.", + "pattern": "^[^#]*#?$" + }, + "$schema": {"$ref": "#/$defs/uriString"}, + "$ref": {"$ref": "#/$defs/uriReferenceString"}, + "$anchor": {"$ref": "#/$defs/anchorString"}, + "$dynamicRef": {"$ref": "#/$defs/uriReferenceString"}, + "$dynamicAnchor": {"$ref": "#/$defs/anchorString"}, + "$vocabulary": { + "type": "object", + "propertyNames": {"$ref": "#/$defs/uriString"}, + "additionalProperties": { + "type": "boolean" + } + }, + "$comment": { + "type": "string" + }, + "$defs": { + "type": "object", + "additionalProperties": {"$dynamicRef": "#meta"} + } + }, + "$defs": { + "anchorString": { + "type": "string", + "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$" + }, + "uriString": { + "type": "string", + "format": "uri" + }, + "uriReferenceString": { + "type": "string", + "format": "uri-reference" + } + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/format-annotation.json b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/format-annotation.json new file mode 100644 index 00000000..83c26e35 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/format-annotation.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/format-annotation", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/format-annotation": true + }, + "$dynamicAnchor": "meta", + + "title": "Format vocabulary meta-schema for annotation results", + "type": ["object", "boolean"], + "properties": { + "format": {"type": "string"} + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/meta-data.json b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/meta-data.json new file mode 100644 index 00000000..11946fb5 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/meta-data.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/meta-data", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/meta-data": true + }, + "$dynamicAnchor": "meta", + + "title": "Meta-data vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "deprecated": { + "type": "boolean", + "default": false + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + } + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/unevaluated.json b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/unevaluated.json new file mode 100644 index 00000000..5e4b203b --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/unevaluated.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/unevaluated", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/unevaluated": true + }, + "$dynamicAnchor": "meta", + + "title": "Unevaluated applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "unevaluatedItems": {"$dynamicRef": "#meta"}, + "unevaluatedProperties": {"$dynamicRef": "#meta"} + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/validation.json b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/validation.json new file mode 100644 index 00000000..e0ae13d9 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2020-12/meta/validation.json @@ -0,0 +1,90 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/validation", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/validation": true + }, + "$dynamicAnchor": "meta", + + "title": "Validation vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "type": { + "anyOf": [ + {"$ref": "#/$defs/simpleTypes"}, + { + "type": "array", + "items": {"$ref": "#/$defs/simpleTypes"}, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "const": true, + "enum": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": {"$ref": "#/$defs/nonNegativeInteger"}, + "minLength": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": {"$ref": "#/$defs/nonNegativeInteger"}, + "minItems": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxContains": {"$ref": "#/$defs/nonNegativeInteger"}, + "minContains": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 1 + }, + "maxProperties": {"$ref": "#/$defs/nonNegativeInteger"}, + "minProperties": {"$ref": "#/$defs/nonNegativeIntegerDefault0"}, + "required": {"$ref": "#/$defs/stringArray"}, + "dependentRequired": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/stringArray" + } + } + }, + "$defs": { + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 0 + }, + "simpleTypes": { + "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + "stringArray": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true, + "default": [] + } + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-2020-12/schema.json b/website/node_modules/ajv/lib/refs/json-schema-2020-12/schema.json new file mode 100644 index 00000000..1c68270f --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-2020-12/schema.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/schema", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true, + "https://json-schema.org/draft/2020-12/vocab/applicator": true, + "https://json-schema.org/draft/2020-12/vocab/unevaluated": true, + "https://json-schema.org/draft/2020-12/vocab/validation": true, + "https://json-schema.org/draft/2020-12/vocab/meta-data": true, + "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, + "https://json-schema.org/draft/2020-12/vocab/content": true + }, + "$dynamicAnchor": "meta", + + "title": "Core and Validation specifications meta-schema", + "allOf": [ + {"$ref": "meta/core"}, + {"$ref": "meta/applicator"}, + {"$ref": "meta/unevaluated"}, + {"$ref": "meta/validation"}, + {"$ref": "meta/meta-data"}, + {"$ref": "meta/format-annotation"}, + {"$ref": "meta/content"} + ], + "type": ["object", "boolean"], + "$comment": "This meta-schema also defines keywords that have appeared in previous drafts in order to prevent incompatible extensions as they remain in common use.", + "properties": { + "definitions": { + "$comment": "\"definitions\" has been replaced by \"$defs\".", + "type": "object", + "additionalProperties": {"$dynamicRef": "#meta"}, + "deprecated": true, + "default": {} + }, + "dependencies": { + "$comment": "\"dependencies\" has been split and replaced by \"dependentSchemas\" and \"dependentRequired\" in order to serve their differing semantics.", + "type": "object", + "additionalProperties": { + "anyOf": [{"$dynamicRef": "#meta"}, {"$ref": "meta/validation#/$defs/stringArray"}] + }, + "deprecated": true, + "default": {} + }, + "$recursiveAnchor": { + "$comment": "\"$recursiveAnchor\" has been replaced by \"$dynamicAnchor\".", + "$ref": "meta/core#/$defs/anchorString", + "deprecated": true + }, + "$recursiveRef": { + "$comment": "\"$recursiveRef\" has been replaced by \"$dynamicRef\".", + "$ref": "meta/core#/$defs/uriReferenceString", + "deprecated": true + } + } +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-draft-06.json b/website/node_modules/ajv/lib/refs/json-schema-draft-06.json new file mode 100644 index 00000000..5410064b --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-draft-06.json @@ -0,0 +1,137 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "http://json-schema.org/draft-06/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$ref": "#"} + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [{"$ref": "#/definitions/nonNegativeInteger"}, {"default": 0}] + }, + "simpleTypes": { + "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + "stringArray": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "examples": { + "type": "array", + "items": {} + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": {"$ref": "#/definitions/nonNegativeInteger"}, + "minLength": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": {"$ref": "#"}, + "items": { + "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/schemaArray"}], + "default": {} + }, + "maxItems": {"$ref": "#/definitions/nonNegativeInteger"}, + "minItems": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": {"$ref": "#"}, + "maxProperties": {"$ref": "#/definitions/nonNegativeInteger"}, + "minProperties": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "required": {"$ref": "#/definitions/stringArray"}, + "additionalProperties": {"$ref": "#"}, + "definitions": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/stringArray"}] + } + }, + "propertyNames": {"$ref": "#"}, + "const": {}, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + {"$ref": "#/definitions/simpleTypes"}, + { + "type": "array", + "items": {"$ref": "#/definitions/simpleTypes"}, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": {"type": "string"}, + "allOf": {"$ref": "#/definitions/schemaArray"}, + "anyOf": {"$ref": "#/definitions/schemaArray"}, + "oneOf": {"$ref": "#/definitions/schemaArray"}, + "not": {"$ref": "#"} + }, + "default": {} +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-draft-07.json b/website/node_modules/ajv/lib/refs/json-schema-draft-07.json new file mode 100644 index 00000000..6a748510 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-draft-07.json @@ -0,0 +1,151 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://json-schema.org/draft-07/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$ref": "#"} + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [{"$ref": "#/definitions/nonNegativeInteger"}, {"default": 0}] + }, + "simpleTypes": { + "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + "stringArray": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": {"$ref": "#/definitions/nonNegativeInteger"}, + "minLength": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": {"$ref": "#"}, + "items": { + "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/schemaArray"}], + "default": true + }, + "maxItems": {"$ref": "#/definitions/nonNegativeInteger"}, + "minItems": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": {"$ref": "#"}, + "maxProperties": {"$ref": "#/definitions/nonNegativeInteger"}, + "minProperties": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, + "required": {"$ref": "#/definitions/stringArray"}, + "additionalProperties": {"$ref": "#"}, + "definitions": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": {"$ref": "#"}, + "propertyNames": {"format": "regex"}, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/stringArray"}] + } + }, + "propertyNames": {"$ref": "#"}, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + {"$ref": "#/definitions/simpleTypes"}, + { + "type": "array", + "items": {"$ref": "#/definitions/simpleTypes"}, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": {"type": "string"}, + "contentMediaType": {"type": "string"}, + "contentEncoding": {"type": "string"}, + "if": {"$ref": "#"}, + "then": {"$ref": "#"}, + "else": {"$ref": "#"}, + "allOf": {"$ref": "#/definitions/schemaArray"}, + "anyOf": {"$ref": "#/definitions/schemaArray"}, + "oneOf": {"$ref": "#/definitions/schemaArray"}, + "not": {"$ref": "#"} + }, + "default": true +} diff --git a/website/node_modules/ajv/lib/refs/json-schema-secure.json b/website/node_modules/ajv/lib/refs/json-schema-secure.json new file mode 100644 index 00000000..3968abd5 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/json-schema-secure.json @@ -0,0 +1,88 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#", + "title": "Meta-schema for the security assessment of JSON Schemas", + "description": "If a JSON AnySchema fails validation against this meta-schema, it may be unsafe to validate untrusted data", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$ref": "#"} + } + }, + "dependencies": { + "patternProperties": { + "description": "prevent slow validation of large property names", + "required": ["propertyNames"], + "properties": { + "propertyNames": { + "required": ["maxLength"] + } + } + }, + "uniqueItems": { + "description": "prevent slow validation of large non-scalar arrays", + "if": { + "properties": { + "uniqueItems": {"const": true}, + "items": { + "properties": { + "type": { + "anyOf": [ + { + "enum": ["object", "array"] + }, + { + "type": "array", + "contains": {"enum": ["object", "array"]} + } + ] + } + } + } + } + }, + "then": { + "required": ["maxItems"] + } + }, + "pattern": { + "description": "prevent slow pattern matching of large strings", + "required": ["maxLength"] + }, + "format": { + "description": "prevent slow format validation of large strings", + "required": ["maxLength"] + } + }, + "properties": { + "additionalItems": {"$ref": "#"}, + "additionalProperties": {"$ref": "#"}, + "dependencies": { + "additionalProperties": { + "anyOf": [{"type": "array"}, {"$ref": "#"}] + } + }, + "items": { + "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/schemaArray"}] + }, + "definitions": { + "additionalProperties": {"$ref": "#"} + }, + "patternProperties": { + "additionalProperties": {"$ref": "#"} + }, + "properties": { + "additionalProperties": {"$ref": "#"} + }, + "if": {"$ref": "#"}, + "then": {"$ref": "#"}, + "else": {"$ref": "#"}, + "allOf": {"$ref": "#/definitions/schemaArray"}, + "anyOf": {"$ref": "#/definitions/schemaArray"}, + "oneOf": {"$ref": "#/definitions/schemaArray"}, + "not": {"$ref": "#"}, + "contains": {"$ref": "#"}, + "propertyNames": {"$ref": "#"} + } +} diff --git a/website/node_modules/ajv/lib/refs/jtd-schema.ts b/website/node_modules/ajv/lib/refs/jtd-schema.ts new file mode 100644 index 00000000..c0198128 --- /dev/null +++ b/website/node_modules/ajv/lib/refs/jtd-schema.ts @@ -0,0 +1,130 @@ +import {SchemaObject} from "../types" + +type MetaSchema = (root: boolean) => SchemaObject + +const shared: MetaSchema = (root) => { + const sch: SchemaObject = { + nullable: {type: "boolean"}, + metadata: { + optionalProperties: { + union: {elements: {ref: "schema"}}, + }, + additionalProperties: true, + }, + } + if (root) sch.definitions = {values: {ref: "schema"}} + return sch +} + +const emptyForm: MetaSchema = (root) => ({ + optionalProperties: shared(root), +}) + +const refForm: MetaSchema = (root) => ({ + properties: { + ref: {type: "string"}, + }, + optionalProperties: shared(root), +}) + +const typeForm: MetaSchema = (root) => ({ + properties: { + type: { + enum: [ + "boolean", + "timestamp", + "string", + "float32", + "float64", + "int8", + "uint8", + "int16", + "uint16", + "int32", + "uint32", + ], + }, + }, + optionalProperties: shared(root), +}) + +const enumForm: MetaSchema = (root) => ({ + properties: { + enum: {elements: {type: "string"}}, + }, + optionalProperties: shared(root), +}) + +const elementsForm: MetaSchema = (root) => ({ + properties: { + elements: {ref: "schema"}, + }, + optionalProperties: shared(root), +}) + +const propertiesForm: MetaSchema = (root) => ({ + properties: { + properties: {values: {ref: "schema"}}, + }, + optionalProperties: { + optionalProperties: {values: {ref: "schema"}}, + additionalProperties: {type: "boolean"}, + ...shared(root), + }, +}) + +const optionalPropertiesForm: MetaSchema = (root) => ({ + properties: { + optionalProperties: {values: {ref: "schema"}}, + }, + optionalProperties: { + additionalProperties: {type: "boolean"}, + ...shared(root), + }, +}) + +const discriminatorForm: MetaSchema = (root) => ({ + properties: { + discriminator: {type: "string"}, + mapping: { + values: { + metadata: { + union: [propertiesForm(false), optionalPropertiesForm(false)], + }, + }, + }, + }, + optionalProperties: shared(root), +}) + +const valuesForm: MetaSchema = (root) => ({ + properties: { + values: {ref: "schema"}, + }, + optionalProperties: shared(root), +}) + +const schema: MetaSchema = (root) => ({ + metadata: { + union: [ + emptyForm, + refForm, + typeForm, + enumForm, + elementsForm, + propertiesForm, + optionalPropertiesForm, + discriminatorForm, + valuesForm, + ].map((s) => s(root)), + }, +}) + +const jtdMetaSchema: SchemaObject = { + definitions: { + schema: schema(false), + }, + ...schema(true), +} + +export default jtdMetaSchema diff --git a/website/node_modules/ajv/lib/runtime/equal.ts b/website/node_modules/ajv/lib/runtime/equal.ts new file mode 100644 index 00000000..3cb00631 --- /dev/null +++ b/website/node_modules/ajv/lib/runtime/equal.ts @@ -0,0 +1,7 @@ +// https://github.com/ajv-validator/ajv/issues/889 +import * as equal from "fast-deep-equal" + +type Equal = typeof equal & {code: string} +;(equal as Equal).code = 'require("ajv/dist/runtime/equal").default' + +export default equal as Equal diff --git a/website/node_modules/ajv/lib/runtime/parseJson.ts b/website/node_modules/ajv/lib/runtime/parseJson.ts new file mode 100644 index 00000000..92579afe --- /dev/null +++ b/website/node_modules/ajv/lib/runtime/parseJson.ts @@ -0,0 +1,176 @@ +const rxParseJson = /position\s(\d+)$/ + +export function parseJson(s: string, pos: number): unknown { + let endPos: number | undefined + parseJson.message = undefined + let matches: RegExpExecArray | null + if (pos) s = s.slice(pos) + try { + parseJson.position = pos + s.length + return JSON.parse(s) + } catch (e) { + matches = rxParseJson.exec((e as Error).message) + if (!matches) { + parseJson.message = "unexpected end" + return undefined + } + endPos = +matches[1] + const c = s[endPos] + s = s.slice(0, endPos) + parseJson.position = pos + endPos + try { + return JSON.parse(s) + } catch (e1) { + parseJson.message = `unexpected token ${c}` + return undefined + } + } +} + +parseJson.message = undefined as string | undefined +parseJson.position = 0 as number +parseJson.code = 'require("ajv/dist/runtime/parseJson").parseJson' + +export function parseJsonNumber(s: string, pos: number, maxDigits?: number): number | undefined { + let numStr = "" + let c: string + parseJsonNumber.message = undefined + if (s[pos] === "-") { + numStr += "-" + pos++ + } + if (s[pos] === "0") { + numStr += "0" + pos++ + } else { + if (!parseDigits(maxDigits)) { + errorMessage() + return undefined + } + } + if (maxDigits) { + parseJsonNumber.position = pos + return +numStr + } + if (s[pos] === ".") { + numStr += "." + pos++ + if (!parseDigits()) { + errorMessage() + return undefined + } + } + if (((c = s[pos]), c === "e" || c === "E")) { + numStr += "e" + pos++ + if (((c = s[pos]), c === "+" || c === "-")) { + numStr += c + pos++ + } + if (!parseDigits()) { + errorMessage() + return undefined + } + } + parseJsonNumber.position = pos + return +numStr + + function parseDigits(maxLen?: number): boolean { + let digit = false + while (((c = s[pos]), c >= "0" && c <= "9" && (maxLen === undefined || maxLen-- > 0))) { + digit = true + numStr += c + pos++ + } + return digit + } + + function errorMessage(): void { + parseJsonNumber.position = pos + parseJsonNumber.message = pos < s.length ? `unexpected token ${s[pos]}` : "unexpected end" + } +} + +parseJsonNumber.message = undefined as string | undefined +parseJsonNumber.position = 0 as number +parseJsonNumber.code = 'require("ajv/dist/runtime/parseJson").parseJsonNumber' + +const escapedChars: {[X in string]?: string} = { + b: "\b", + f: "\f", + n: "\n", + r: "\r", + t: "\t", + '"': '"', + "/": "/", + "\\": "\\", +} + +const CODE_A: number = "a".charCodeAt(0) +const CODE_0: number = "0".charCodeAt(0) + +export function parseJsonString(s: string, pos: number): string | undefined { + let str = "" + let c: string | undefined + parseJsonString.message = undefined + // eslint-disable-next-line no-constant-condition, @typescript-eslint/no-unnecessary-condition + while (true) { + c = s[pos++] + if (c === '"') break + if (c === "\\") { + c = s[pos] + if (c in escapedChars) { + str += escapedChars[c] + pos++ + } else if (c === "u") { + pos++ + let count = 4 + let code = 0 + while (count--) { + code <<= 4 + c = s[pos] + if (c === undefined) { + errorMessage("unexpected end") + return undefined + } + c = c.toLowerCase() + if (c >= "a" && c <= "f") { + code += c.charCodeAt(0) - CODE_A + 10 + } else if (c >= "0" && c <= "9") { + code += c.charCodeAt(0) - CODE_0 + } else { + errorMessage(`unexpected token ${c}`) + return undefined + } + pos++ + } + str += String.fromCharCode(code) + } else { + errorMessage(`unexpected token ${c}`) + return undefined + } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + } else if (c === undefined) { + errorMessage("unexpected end") + return undefined + } else { + if (c.charCodeAt(0) >= 0x20) { + str += c + } else { + errorMessage(`unexpected token ${c}`) + return undefined + } + } + } + parseJsonString.position = pos + return str + + function errorMessage(msg: string): void { + parseJsonString.position = pos + parseJsonString.message = msg + } +} + +parseJsonString.message = undefined as string | undefined +parseJsonString.position = 0 as number +parseJsonString.code = 'require("ajv/dist/runtime/parseJson").parseJsonString' diff --git a/website/node_modules/ajv/lib/runtime/quote.ts b/website/node_modules/ajv/lib/runtime/quote.ts new file mode 100644 index 00000000..1160e6a2 --- /dev/null +++ b/website/node_modules/ajv/lib/runtime/quote.ts @@ -0,0 +1,31 @@ +const rxEscapable = + // eslint-disable-next-line no-control-regex, no-misleading-character-class + /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g + +const escaped: {[K in string]?: string} = { + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + '"': '\\"', + "\\": "\\\\", +} + +export default function quote(s: string): string { + rxEscapable.lastIndex = 0 + return ( + '"' + + (rxEscapable.test(s) + ? s.replace(rxEscapable, (a) => { + const c = escaped[a] + return typeof c === "string" + ? c + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) + }) + : s) + + '"' + ) +} + +quote.code = 'require("ajv/dist/runtime/quote").default' diff --git a/website/node_modules/ajv/lib/runtime/re2.ts b/website/node_modules/ajv/lib/runtime/re2.ts new file mode 100644 index 00000000..0c769bc7 --- /dev/null +++ b/website/node_modules/ajv/lib/runtime/re2.ts @@ -0,0 +1,6 @@ +import * as re2 from "re2" + +type Re2 = typeof re2 & {code: string} +;(re2 as Re2).code = 'require("ajv/dist/runtime/re2").default' + +export default re2 as Re2 diff --git a/website/node_modules/ajv/lib/runtime/timestamp.ts b/website/node_modules/ajv/lib/runtime/timestamp.ts new file mode 100644 index 00000000..1625f9a4 --- /dev/null +++ b/website/node_modules/ajv/lib/runtime/timestamp.ts @@ -0,0 +1,46 @@ +const DT_SEPARATOR = /t|\s/i +const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ +const TIME = /^(\d\d):(\d\d):(\d\d)(?:\.\d+)?(?:z|([+-]\d\d)(?::?(\d\d))?)$/i +const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + +export default function validTimestamp(str: string, allowDate: boolean): boolean { + // http://tools.ietf.org/html/rfc3339#section-5.6 + const dt: string[] = str.split(DT_SEPARATOR) + return ( + (dt.length === 2 && validDate(dt[0]) && validTime(dt[1])) || + (allowDate && dt.length === 1 && validDate(dt[0])) + ) +} + +function validDate(str: string): boolean { + const matches: string[] | null = DATE.exec(str) + if (!matches) return false + const y: number = +matches[1] + const m: number = +matches[2] + const d: number = +matches[3] + return ( + m >= 1 && + m <= 12 && + d >= 1 && + (d <= DAYS[m] || + // leap year: https://tools.ietf.org/html/rfc3339#appendix-C + (m === 2 && d === 29 && (y % 100 === 0 ? y % 400 === 0 : y % 4 === 0))) + ) +} + +function validTime(str: string): boolean { + const matches: string[] | null = TIME.exec(str) + if (!matches) return false + const hr: number = +matches[1] + const min: number = +matches[2] + const sec: number = +matches[3] + const tzH: number = +(matches[4] || 0) + const tzM: number = +(matches[5] || 0) + return ( + (hr <= 23 && min <= 59 && sec <= 59) || + // leap second + (hr - tzH === 23 && min - tzM === 59 && sec === 60) + ) +} + +validTimestamp.code = 'require("ajv/dist/runtime/timestamp").default' diff --git a/website/node_modules/ajv/lib/runtime/ucs2length.ts b/website/node_modules/ajv/lib/runtime/ucs2length.ts new file mode 100644 index 00000000..47d8292b --- /dev/null +++ b/website/node_modules/ajv/lib/runtime/ucs2length.ts @@ -0,0 +1,20 @@ +// https://mathiasbynens.be/notes/javascript-encoding +// https://github.com/bestiejs/punycode.js - punycode.ucs2.decode +export default function ucs2length(str: string): number { + const len = str.length + let length = 0 + let pos = 0 + let value: number + while (pos < len) { + length++ + value = str.charCodeAt(pos++) + if (value >= 0xd800 && value <= 0xdbff && pos < len) { + // high surrogate, and there is a next character + value = str.charCodeAt(pos) + if ((value & 0xfc00) === 0xdc00) pos++ // low surrogate + } + } + return length +} + +ucs2length.code = 'require("ajv/dist/runtime/ucs2length").default' diff --git a/website/node_modules/ajv/lib/runtime/uri.ts b/website/node_modules/ajv/lib/runtime/uri.ts new file mode 100644 index 00000000..7dd35f9d --- /dev/null +++ b/website/node_modules/ajv/lib/runtime/uri.ts @@ -0,0 +1,6 @@ +import * as uri from "uri-js" + +type URI = typeof uri & {code: string} +;(uri as URI).code = 'require("ajv/dist/runtime/uri").default' + +export default uri as URI diff --git a/website/node_modules/ajv/lib/runtime/validation_error.ts b/website/node_modules/ajv/lib/runtime/validation_error.ts new file mode 100644 index 00000000..2d19a46a --- /dev/null +++ b/website/node_modules/ajv/lib/runtime/validation_error.ts @@ -0,0 +1,13 @@ +import type {ErrorObject} from "../types" + +export default class ValidationError extends Error { + readonly errors: Partial[] + readonly ajv: true + readonly validation: true + + constructor(errors: Partial[]) { + super("validation failed") + this.errors = errors + this.ajv = this.validation = true + } +} diff --git a/website/node_modules/ajv/lib/standalone/index.ts b/website/node_modules/ajv/lib/standalone/index.ts new file mode 100644 index 00000000..b6129ce9 --- /dev/null +++ b/website/node_modules/ajv/lib/standalone/index.ts @@ -0,0 +1,100 @@ +import type AjvCore from "../core" +import type {AnyValidateFunction, SourceCode} from "../types" +import type {SchemaEnv} from "../compile" +import {UsedScopeValues, UsedValueState, ValueScopeName, varKinds} from "../compile/codegen/scope" +import {_, nil, _Code, Code, getProperty, getEsmExportName} from "../compile/codegen/code" + +function standaloneCode( + ajv: AjvCore, + refsOrFunc?: {[K in string]?: string} | AnyValidateFunction +): string { + if (!ajv.opts.code.source) { + throw new Error("moduleCode: ajv instance must have code.source option") + } + const {_n} = ajv.scope.opts + return typeof refsOrFunc == "function" + ? funcExportCode(refsOrFunc.source) + : refsOrFunc !== undefined + ? multiExportsCode(refsOrFunc, getValidate) + : multiExportsCode(ajv.schemas, (sch) => + sch.meta ? undefined : ajv.compile(sch.schema) + ) + + function getValidate(id: string): AnyValidateFunction { + const v = ajv.getSchema(id) + if (!v) throw new Error(`moduleCode: no schema with id ${id}`) + return v + } + + function funcExportCode(source?: SourceCode): string { + const usedValues: UsedScopeValues = {} + const n = source?.validateName + const vCode = validateCode(usedValues, source) + if (ajv.opts.code.esm) { + // Always do named export as `validate` rather than the variable `n` which is `validateXX` for known export value + return `"use strict";${_n}export const validate = ${n};${_n}export default ${n};${_n}${vCode}` + } + return `"use strict";${_n}module.exports = ${n};${_n}module.exports.default = ${n};${_n}${vCode}` + } + + function multiExportsCode( + schemas: {[K in string]?: T}, + getValidateFunc: (schOrId: T) => AnyValidateFunction | undefined + ): string { + const usedValues: UsedScopeValues = {} + let code = _`"use strict";` + for (const name in schemas) { + const v = getValidateFunc(schemas[name] as T) + if (v) { + const vCode = validateCode(usedValues, v.source) + const exportSyntax = ajv.opts.code.esm + ? _`export const ${getEsmExportName(name)}` + : _`exports${getProperty(name)}` + code = _`${code}${_n}${exportSyntax} = ${v.source?.validateName};${_n}${vCode}` + } + } + return `${code}` + } + + function validateCode(usedValues: UsedScopeValues, s?: SourceCode): Code { + if (!s) throw new Error('moduleCode: function does not have "source" property') + if (usedState(s.validateName) === UsedValueState.Completed) return nil + setUsedState(s.validateName, UsedValueState.Started) + + const scopeCode = ajv.scope.scopeCode(s.scopeValues, usedValues, refValidateCode) + const code = new _Code(`${scopeCode}${_n}${s.validateCode}`) + return s.evaluated ? _`${code}${s.validateName}.evaluated = ${s.evaluated};${_n}` : code + + function refValidateCode(n: ValueScopeName): Code | undefined { + const vRef = n.value?.ref + if (n.prefix === "validate" && typeof vRef == "function") { + const v = vRef as AnyValidateFunction + return validateCode(usedValues, v.source) + } else if ((n.prefix === "root" || n.prefix === "wrapper") && typeof vRef == "object") { + const {validate, validateName} = vRef as SchemaEnv + if (!validateName) throw new Error("ajv internal error") + const def = ajv.opts.code.es5 ? varKinds.var : varKinds.const + const wrapper = _`${def} ${n} = {validate: ${validateName}};` + if (usedState(validateName) === UsedValueState.Started) return wrapper + const vCode = validateCode(usedValues, validate?.source) + return _`${wrapper}${_n}${vCode}` + } + return undefined + } + + function usedState(name: ValueScopeName): UsedValueState | undefined { + return usedValues[name.prefix]?.get(name) + } + + function setUsedState(name: ValueScopeName, state: UsedValueState): void { + const {prefix} = name + const names = (usedValues[prefix] = usedValues[prefix] || new Map()) + names.set(name, state) + } + } +} + +module.exports = exports = standaloneCode +Object.defineProperty(exports, "__esModule", {value: true}) + +export default standaloneCode diff --git a/website/node_modules/ajv/lib/standalone/instance.ts b/website/node_modules/ajv/lib/standalone/instance.ts new file mode 100644 index 00000000..c4b2c30b --- /dev/null +++ b/website/node_modules/ajv/lib/standalone/instance.ts @@ -0,0 +1,36 @@ +import Ajv, {AnySchema, AnyValidateFunction, ErrorObject} from "../core" +import standaloneCode from "." +import * as requireFromString from "require-from-string" + +export default class AjvPack { + errors?: ErrorObject[] | null // errors from the last validation + constructor(readonly ajv: Ajv) {} + + validate(schemaKeyRef: AnySchema | string, data: unknown): boolean | Promise { + return Ajv.prototype.validate.call(this, schemaKeyRef, data) + } + + compile(schema: AnySchema, meta?: boolean): AnyValidateFunction { + return this.getStandalone(this.ajv.compile(schema, meta)) + } + + getSchema(keyRef: string): AnyValidateFunction | undefined { + const v = this.ajv.getSchema(keyRef) + if (!v) return undefined + return this.getStandalone(v) + } + + private getStandalone(v: AnyValidateFunction): AnyValidateFunction { + return requireFromString(standaloneCode(this.ajv, v)) as AnyValidateFunction + } + + addSchema(...args: Parameters): AjvPack { + this.ajv.addSchema.call(this.ajv, ...args) + return this + } + + addKeyword(...args: Parameters): AjvPack { + this.ajv.addKeyword.call(this.ajv, ...args) + return this + } +} diff --git a/website/node_modules/ajv/lib/types/index.ts b/website/node_modules/ajv/lib/types/index.ts new file mode 100644 index 00000000..123d9df1 --- /dev/null +++ b/website/node_modules/ajv/lib/types/index.ts @@ -0,0 +1,240 @@ +import * as URI from "uri-js" +import type {CodeGen, Code, Name, ScopeValueSets, ValueScopeName} from "../compile/codegen" +import type {SchemaEnv, SchemaCxt, SchemaObjCxt} from "../compile" +import type {JSONType} from "../compile/rules" +import type {KeywordCxt} from "../compile/validate" +import type Ajv from "../core" + +interface _SchemaObject { + id?: string + $id?: string + $schema?: string + [x: string]: any // TODO +} + +export interface SchemaObject extends _SchemaObject { + id?: string + $id?: string + $schema?: string + $async?: false + [x: string]: any // TODO +} + +export interface AsyncSchema extends _SchemaObject { + $async: true +} + +export type AnySchemaObject = SchemaObject | AsyncSchema + +export type Schema = SchemaObject | boolean + +export type AnySchema = Schema | AsyncSchema + +export type SchemaMap = {[Key in string]?: AnySchema} + +export interface SourceCode { + validateName: ValueScopeName + validateCode: string + scopeValues: ScopeValueSets + evaluated?: Code +} + +export interface DataValidationCxt { + instancePath: string + parentData: {[K in T]: any} // object or array + parentDataProperty: T // string or number + rootData: Record | any[] + dynamicAnchors: {[Ref in string]?: ValidateFunction} +} + +export interface ValidateFunction { + (this: Ajv | any, data: any, dataCxt?: DataValidationCxt): data is T + errors?: null | ErrorObject[] + evaluated?: Evaluated + schema: AnySchema + schemaEnv: SchemaEnv + source?: SourceCode +} + +export interface JTDParser { + (json: string): T | undefined + message?: string + position?: number +} + +export type EvaluatedProperties = {[K in string]?: true} | true + +export type EvaluatedItems = number | true + +export interface Evaluated { + // determined at compile time if staticProps/Items is true + props?: EvaluatedProperties + items?: EvaluatedItems + // whether props/items determined at compile time + dynamicProps: boolean + dynamicItems: boolean +} + +export interface AsyncValidateFunction extends ValidateFunction { + (...args: Parameters>): Promise + $async: true +} + +export type AnyValidateFunction = ValidateFunction | AsyncValidateFunction + +export interface ErrorObject, S = unknown> { + keyword: K + instancePath: string + schemaPath: string + params: P + // Added to validation errors of "propertyNames" keyword schema + propertyName?: string + // Excluded if option `messages` set to false. + message?: string + // These are added with the `verbose` option. + schema?: S + parentSchema?: AnySchemaObject + data?: unknown +} + +export type ErrorNoParams = ErrorObject, S> + +interface _KeywordDef { + keyword: string | string[] + type?: JSONType | JSONType[] // data types that keyword applies to + schemaType?: JSONType | JSONType[] // allowed type(s) of keyword value in the schema + allowUndefined?: boolean // used for keywords that can be invoked by other keywords, not being present in the schema + $data?: boolean // keyword supports [$data reference](../../docs/guide/combining-schemas.md#data-reference) + implements?: string[] // other schema keywords that this keyword implements + before?: string // keyword should be executed before this keyword (should be applicable to the same type) + post?: boolean // keyword should be executed after other keywords without post flag + metaSchema?: AnySchemaObject // meta-schema for keyword schema value - it is better to use schemaType where applicable + validateSchema?: AnyValidateFunction // compiled keyword metaSchema - should not be passed + dependencies?: string[] // keywords that must be present in the same schema + error?: KeywordErrorDefinition + $dataError?: KeywordErrorDefinition +} + +export interface CodeKeywordDefinition extends _KeywordDef { + code: (cxt: KeywordCxt, ruleType?: string) => void + trackErrors?: boolean +} + +export type MacroKeywordFunc = ( + schema: any, + parentSchema: AnySchemaObject, + it: SchemaCxt +) => AnySchema + +export type CompileKeywordFunc = ( + schema: any, + parentSchema: AnySchemaObject, + it: SchemaObjCxt +) => DataValidateFunction + +export interface DataValidateFunction { + (...args: Parameters): boolean | Promise + errors?: Partial[] +} + +export interface SchemaValidateFunction { + (schema: any, data: any, parentSchema?: AnySchemaObject, dataCxt?: DataValidationCxt): + | boolean + | Promise + errors?: Partial[] +} + +export interface FuncKeywordDefinition extends _KeywordDef { + validate?: SchemaValidateFunction | DataValidateFunction + compile?: CompileKeywordFunc + // schema: false makes validate not to expect schema (DataValidateFunction) + schema?: boolean // requires "validate" + modifying?: boolean + async?: boolean + valid?: boolean + errors?: boolean | "full" +} + +export interface MacroKeywordDefinition extends FuncKeywordDefinition { + macro: MacroKeywordFunc +} + +export type KeywordDefinition = + | CodeKeywordDefinition + | FuncKeywordDefinition + | MacroKeywordDefinition + +export type AddedKeywordDefinition = KeywordDefinition & { + type: JSONType[] + schemaType: JSONType[] +} + +export interface KeywordErrorDefinition { + message: string | Code | ((cxt: KeywordErrorCxt) => string | Code) + params?: Code | ((cxt: KeywordErrorCxt) => Code) +} + +export type Vocabulary = (KeywordDefinition | string)[] + +export interface KeywordErrorCxt { + gen: CodeGen + keyword: string + data: Name + $data?: string | false + schema: any // TODO + parentSchema?: AnySchemaObject + schemaCode: Code | number | boolean + schemaValue: Code | number | boolean + schemaType?: JSONType[] + errsCount?: Name + params: KeywordCxtParams + it: SchemaCxt +} + +export type KeywordCxtParams = {[P in string]?: Code | string | number} + +export type FormatValidator = (data: T) => boolean + +export type FormatCompare = (data1: T, data2: T) => number | undefined + +export type AsyncFormatValidator = (data: T) => Promise + +export interface FormatDefinition { + type?: T extends string ? "string" | undefined : "number" + validate: FormatValidator | (T extends string ? string | RegExp : never) + async?: false | undefined + compare?: FormatCompare +} + +export interface AsyncFormatDefinition { + type?: T extends string ? "string" | undefined : "number" + validate: AsyncFormatValidator + async: true + compare?: FormatCompare +} + +export type AddedFormat = + | true + | RegExp + | FormatValidator + | FormatDefinition + | FormatDefinition + | AsyncFormatDefinition + | AsyncFormatDefinition + +export type Format = AddedFormat | string + +export interface RegExpEngine { + (pattern: string, u: string): RegExpLike + code: string +} + +export interface RegExpLike { + test: (s: string) => boolean +} + +export interface UriResolver { + parse(uri: string): URI.URIComponents + resolve(base: string, path: string): string + serialize(component: URI.URIComponents): string +} diff --git a/website/node_modules/ajv/lib/types/json-schema.ts b/website/node_modules/ajv/lib/types/json-schema.ts new file mode 100644 index 00000000..281a38bd --- /dev/null +++ b/website/node_modules/ajv/lib/types/json-schema.ts @@ -0,0 +1,187 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ +type StrictNullChecksWrapper = undefined extends null + ? `strictNullChecks must be true in tsconfig to use ${Name}` + : Type + +type UnionToIntersection = (U extends any ? (_: U) => void : never) extends (_: infer I) => void + ? I + : never + +export type SomeJSONSchema = UncheckedJSONSchemaType + +type UncheckedPartialSchema = Partial> + +export type PartialSchema = StrictNullChecksWrapper<"PartialSchema", UncheckedPartialSchema> + +type JSONType = IsPartial extends true + ? T | undefined + : T + +interface NumberKeywords { + minimum?: number + maximum?: number + exclusiveMinimum?: number + exclusiveMaximum?: number + multipleOf?: number + format?: string +} + +interface StringKeywords { + minLength?: number + maxLength?: number + pattern?: string + format?: string +} + +type UncheckedJSONSchemaType = ( + | // these two unions allow arbitrary unions of types + { + anyOf: readonly UncheckedJSONSchemaType[] + } + | { + oneOf: readonly UncheckedJSONSchemaType[] + } + // this union allows for { type: (primitive)[] } style schemas + | ({ + type: readonly (T extends number + ? JSONType<"number" | "integer", IsPartial> + : T extends string + ? JSONType<"string", IsPartial> + : T extends boolean + ? JSONType<"boolean", IsPartial> + : never)[] + } & UnionToIntersection< + T extends number + ? NumberKeywords + : T extends string + ? StringKeywords + : T extends boolean + ? // eslint-disable-next-line @typescript-eslint/ban-types + {} + : never + >) + // this covers "normal" types; it's last so typescript looks to it first for errors + | ((T extends number + ? { + type: JSONType<"number" | "integer", IsPartial> + } & NumberKeywords + : T extends string + ? { + type: JSONType<"string", IsPartial> + } & StringKeywords + : T extends boolean + ? { + type: JSONType<"boolean", IsPartial> + } + : T extends readonly [any, ...any[]] + ? { + // JSON AnySchema for tuple + type: JSONType<"array", IsPartial> + items: { + readonly [K in keyof T]-?: UncheckedJSONSchemaType & Nullable + } & {length: T["length"]} + minItems: T["length"] + } & ({maxItems: T["length"]} | {additionalItems: false}) + : T extends readonly any[] + ? { + type: JSONType<"array", IsPartial> + items: UncheckedJSONSchemaType + contains?: UncheckedPartialSchema + minItems?: number + maxItems?: number + minContains?: number + maxContains?: number + uniqueItems?: true + additionalItems?: never + } + : T extends Record + ? { + // JSON AnySchema for records and dictionaries + // "required" is not optional because it is often forgotten + // "properties" are optional for more concise dictionary schemas + // "patternProperties" and can be only used with interfaces that have string index + type: JSONType<"object", IsPartial> + additionalProperties?: boolean | UncheckedJSONSchemaType + unevaluatedProperties?: boolean | UncheckedJSONSchemaType + properties?: IsPartial extends true + ? Partial> + : UncheckedPropertiesSchema + patternProperties?: Record> + propertyNames?: Omit, "type"> & {type?: "string"} + dependencies?: {[K in keyof T]?: Readonly<(keyof T)[]> | UncheckedPartialSchema} + dependentRequired?: {[K in keyof T]?: Readonly<(keyof T)[]>} + dependentSchemas?: {[K in keyof T]?: UncheckedPartialSchema} + minProperties?: number + maxProperties?: number + } & (IsPartial extends true // "required" is not necessary if it's a non-partial type with no required keys // are listed it only asserts that optional cannot be listed. // "required" type does not guarantee that all required properties + ? {required: Readonly<(keyof T)[]>} + : [UncheckedRequiredMembers] extends [never] + ? {required?: Readonly[]>} + : {required: Readonly[]>}) + : T extends null + ? { + type: JSONType<"null", IsPartial> + nullable: true + } + : never) & { + allOf?: Readonly[]> + anyOf?: Readonly[]> + oneOf?: Readonly[]> + if?: UncheckedPartialSchema + then?: UncheckedPartialSchema + else?: UncheckedPartialSchema + not?: UncheckedPartialSchema + }) +) & { + [keyword: string]: any + $id?: string + $ref?: string + $defs?: Record> + definitions?: Record> +} + +export type JSONSchemaType = StrictNullChecksWrapper< + "JSONSchemaType", + UncheckedJSONSchemaType +> + +type Known = + | {[key: string]: Known} + | [Known, ...Known[]] + | Known[] + | number + | string + | boolean + | null + +type UncheckedPropertiesSchema = { + [K in keyof T]-?: (UncheckedJSONSchemaType & Nullable) | {$ref: string} +} + +export type PropertiesSchema = StrictNullChecksWrapper< + "PropertiesSchema", + UncheckedPropertiesSchema +> + +type UncheckedRequiredMembers = { + [K in keyof T]-?: undefined extends T[K] ? never : K +}[keyof T] + +export type RequiredMembers = StrictNullChecksWrapper< + "RequiredMembers", + UncheckedRequiredMembers +> + +type Nullable = undefined extends T + ? { + nullable: true + const?: null // any non-null value would fail `const: null`, `null` would fail any other value in const + enum?: Readonly<(T | null)[]> // `null` must be explicitly included in "enum" for `null` to pass + default?: T | null + } + : { + nullable?: false + const?: T + enum?: Readonly + default?: T + } diff --git a/website/node_modules/ajv/lib/types/jtd-schema.ts b/website/node_modules/ajv/lib/types/jtd-schema.ts new file mode 100644 index 00000000..61b2bde8 --- /dev/null +++ b/website/node_modules/ajv/lib/types/jtd-schema.ts @@ -0,0 +1,273 @@ +/** numeric strings */ +type NumberType = "float32" | "float64" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" + +/** string strings */ +type StringType = "string" | "timestamp" + +/** Generic JTD Schema without inference of the represented type */ +export type SomeJTDSchemaType = ( + | // ref + {ref: string} + // primitives + | {type: NumberType | StringType | "boolean"} + // enum + | {enum: string[]} + // elements + | {elements: SomeJTDSchemaType} + // values + | {values: SomeJTDSchemaType} + // properties + | { + properties: Record + optionalProperties?: Record + additionalProperties?: boolean + } + | { + properties?: Record + optionalProperties: Record + additionalProperties?: boolean + } + // discriminator + | {discriminator: string; mapping: Record} + // empty + // NOTE see the end of + // https://github.com/typescript-eslint/typescript-eslint/issues/2063#issuecomment-675156492 + // eslint-disable-next-line @typescript-eslint/ban-types + | {} +) & { + nullable?: boolean + metadata?: Record + definitions?: Record +} + +/** required keys of an object, not undefined */ +type RequiredKeys = { + [K in keyof T]-?: undefined extends T[K] ? never : K +}[keyof T] + +/** optional or undifined-able keys of an object */ +type OptionalKeys = { + [K in keyof T]-?: undefined extends T[K] ? K : never +}[keyof T] + +/** type is true if T is a union type */ +type IsUnion_ = false extends ( + T extends unknown ? ([U] extends [T] ? false : true) : never +) + ? false + : true +type IsUnion = IsUnion_ + +/** type is true if T is identically E */ +type TypeEquality = [T] extends [E] ? ([E] extends [T] ? true : false) : false + +/** type is true if T or null is identically E or null*/ +type NullTypeEquality = TypeEquality + +/** gets only the string literals of a type or null if a type isn't a string literal */ +type EnumString = [T] extends [never] + ? null + : T extends string + ? string extends T + ? null + : T + : null + +/** true if type is a union of string literals */ +type IsEnum = null extends EnumString ? false : true + +/** true only if all types are array types (not tuples) */ +// NOTE relies on the fact that tuples don't have an index at 0.5, but arrays +// have an index at every number +type IsElements = false extends IsUnion + ? [T] extends [readonly unknown[]] + ? undefined extends T[0.5] + ? false + : true + : false + : false + +/** true if the the type is a values type */ +type IsValues = false extends IsUnion ? TypeEquality : false + +/** true if type is a properties type and Union is false, or type is a discriminator type and Union is true */ +type IsRecord = Union extends IsUnion + ? null extends EnumString + ? false + : true + : false + +/** true if type represents an empty record */ +type IsEmptyRecord = [T] extends [Record] + ? [T] extends [never] + ? false + : true + : false + +/** actual schema */ +export type JTDSchemaType = Record> = ( + | // refs - where null wasn't specified, must match exactly + (null extends EnumString + ? never + : + | ({[K in keyof D]: [T] extends [D[K]] ? {ref: K} : never}[keyof D] & {nullable?: false}) + // nulled refs - if ref is nullable and nullable is specified, then it can + // match either null or non-null definitions + | (null extends T + ? { + [K in keyof D]: [Exclude] extends [Exclude] + ? {ref: K} + : never + }[keyof D] & {nullable: true} + : never)) + // empty - empty schemas also treat nullable differently in that it's now fully ignored + | (unknown extends T ? {nullable?: boolean} : never) + // all other types // numbers - only accepts the type number + | ((true extends NullTypeEquality + ? {type: NumberType} + : // booleans - accepts the type boolean + true extends NullTypeEquality + ? {type: "boolean"} + : // strings - only accepts the type string + true extends NullTypeEquality + ? {type: StringType} + : // strings - only accepts the type Date + true extends NullTypeEquality + ? {type: "timestamp"} + : // enums - only accepts union of string literals + // TODO we can't actually check that everything in the union was specified + true extends IsEnum> + ? {enum: EnumString>[]} + : // arrays - only accepts arrays, could be array of unions to be resolved later + true extends IsElements> + ? T extends readonly (infer E)[] + ? { + elements: JTDSchemaType + } + : never + : // empty properties + true extends IsEmptyRecord> + ? + | {properties: Record; optionalProperties?: Record} + | {optionalProperties: Record} + : // values + true extends IsValues> + ? T extends Record + ? { + values: JTDSchemaType + } + : never + : // properties + true extends IsRecord, false> + ? ([RequiredKeys>] extends [never] + ? { + properties?: Record + } + : { + properties: {[K in RequiredKeys]: JTDSchemaType} + }) & + ([OptionalKeys>] extends [never] + ? { + optionalProperties?: Record + } + : { + optionalProperties: { + [K in OptionalKeys]: JTDSchemaType, D> + } + }) & { + additionalProperties?: boolean + } + : // discriminator + true extends IsRecord, true> + ? { + [K in keyof Exclude]-?: Exclude[K] extends string + ? { + discriminator: K + mapping: { + // TODO currently allows descriminator to be present in schema + [M in Exclude[K]]: JTDSchemaType< + Omit ? T : never, K>, + D + > + } + } + : never + }[keyof Exclude] + : never) & + (null extends T + ? { + nullable: true + } + : {nullable?: false})) +) & { + // extra properties + metadata?: Record + // TODO these should only be allowed at the top level + definitions?: {[K in keyof D]: JTDSchemaType} +} + +type JTDDataDef> = + | // ref + (S extends {ref: string} + ? D extends {[K in S["ref"]]: infer V} + ? JTDDataDef + : never + : // type + S extends {type: NumberType} + ? number + : S extends {type: "boolean"} + ? boolean + : S extends {type: "string"} + ? string + : S extends {type: "timestamp"} + ? string | Date + : // enum + S extends {enum: readonly (infer E)[]} + ? string extends E + ? never + : [E] extends [string] + ? E + : never + : // elements + S extends {elements: infer E} + ? JTDDataDef[] + : // properties + S extends { + properties: Record + optionalProperties?: Record + additionalProperties?: boolean + } + ? {-readonly [K in keyof S["properties"]]-?: JTDDataDef} & { + -readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef< + S["optionalProperties"][K], + D + > + } & ([S["additionalProperties"]] extends [true] ? Record : unknown) + : S extends { + properties?: Record + optionalProperties: Record + additionalProperties?: boolean + } + ? {-readonly [K in keyof S["properties"]]-?: JTDDataDef} & { + -readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef< + S["optionalProperties"][K], + D + > + } & ([S["additionalProperties"]] extends [true] ? Record : unknown) + : // values + S extends {values: infer V} + ? Record> + : // discriminator + S extends {discriminator: infer M; mapping: Record} + ? [M] extends [string] + ? { + [K in keyof S["mapping"]]: JTDDataDef & {[KM in M]: K} + }[keyof S["mapping"]] + : never + : // empty + unknown) + | (S extends {nullable: true} ? null : never) + +export type JTDDataType = S extends {definitions: Record} + ? JTDDataDef + : JTDDataDef> diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/additionalItems.ts b/website/node_modules/ajv/lib/vocabularies/applicator/additionalItems.ts new file mode 100644 index 00000000..755e5b3d --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/additionalItems.ts @@ -0,0 +1,56 @@ +import type { + CodeKeywordDefinition, + ErrorObject, + KeywordErrorDefinition, + AnySchema, +} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str, not, Name} from "../../compile/codegen" +import {alwaysValidSchema, checkStrictMode, Type} from "../../compile/util" + +export type AdditionalItemsError = ErrorObject<"additionalItems", {limit: number}, AnySchema> + +const error: KeywordErrorDefinition = { + message: ({params: {len}}) => str`must NOT have more than ${len} items`, + params: ({params: {len}}) => _`{limit: ${len}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "additionalItems" as const, + type: "array", + schemaType: ["boolean", "object"], + before: "uniqueItems", + error, + code(cxt: KeywordCxt) { + const {parentSchema, it} = cxt + const {items} = parentSchema + if (!Array.isArray(items)) { + checkStrictMode(it, '"additionalItems" is ignored when "items" is not an array of schemas') + return + } + validateAdditionalItems(cxt, items) + }, +} + +export function validateAdditionalItems(cxt: KeywordCxt, items: AnySchema[]): void { + const {gen, schema, data, keyword, it} = cxt + it.items = true + const len = gen.const("len", _`${data}.length`) + if (schema === false) { + cxt.setParams({len: items.length}) + cxt.pass(_`${len} <= ${items.length}`) + } else if (typeof schema == "object" && !alwaysValidSchema(it, schema)) { + const valid = gen.var("valid", _`${len} <= ${items.length}`) // TODO var + gen.if(not(valid), () => validateItems(valid)) + cxt.ok(valid) + } + + function validateItems(valid: Name): void { + gen.forRange("i", items.length, len, (i) => { + cxt.subschema({keyword, dataProp: i, dataPropType: Type.Num}, valid) + if (!it.allErrors) gen.if(not(valid), () => gen.break()) + }) + } +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/additionalProperties.ts b/website/node_modules/ajv/lib/vocabularies/applicator/additionalProperties.ts new file mode 100644 index 00000000..bfb511ce --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/additionalProperties.ts @@ -0,0 +1,118 @@ +import type { + CodeKeywordDefinition, + AddedKeywordDefinition, + ErrorObject, + KeywordErrorDefinition, + AnySchema, +} from "../../types" +import {allSchemaProperties, usePattern, isOwnProperty} from "../code" +import {_, nil, or, not, Code, Name} from "../../compile/codegen" +import N from "../../compile/names" +import type {SubschemaArgs} from "../../compile/validate/subschema" +import {alwaysValidSchema, schemaRefOrVal, Type} from "../../compile/util" + +export type AdditionalPropertiesError = ErrorObject< + "additionalProperties", + {additionalProperty: string}, + AnySchema +> + +const error: KeywordErrorDefinition = { + message: "must NOT have additional properties", + params: ({params}) => _`{additionalProperty: ${params.additionalProperty}}`, +} + +const def: CodeKeywordDefinition & AddedKeywordDefinition = { + keyword: "additionalProperties", + type: ["object"], + schemaType: ["boolean", "object"], + allowUndefined: true, + trackErrors: true, + error, + code(cxt) { + const {gen, schema, parentSchema, data, errsCount, it} = cxt + /* istanbul ignore if */ + if (!errsCount) throw new Error("ajv implementation error") + const {allErrors, opts} = it + it.props = true + if (opts.removeAdditional !== "all" && alwaysValidSchema(it, schema)) return + const props = allSchemaProperties(parentSchema.properties) + const patProps = allSchemaProperties(parentSchema.patternProperties) + checkAdditionalProperties() + cxt.ok(_`${errsCount} === ${N.errors}`) + + function checkAdditionalProperties(): void { + gen.forIn("key", data, (key: Name) => { + if (!props.length && !patProps.length) additionalPropertyCode(key) + else gen.if(isAdditional(key), () => additionalPropertyCode(key)) + }) + } + + function isAdditional(key: Name): Code { + let definedProp: Code + if (props.length > 8) { + // TODO maybe an option instead of hard-coded 8? + const propsSchema = schemaRefOrVal(it, parentSchema.properties, "properties") + definedProp = isOwnProperty(gen, propsSchema as Code, key) + } else if (props.length) { + definedProp = or(...props.map((p) => _`${key} === ${p}`)) + } else { + definedProp = nil + } + if (patProps.length) { + definedProp = or(definedProp, ...patProps.map((p) => _`${usePattern(cxt, p)}.test(${key})`)) + } + return not(definedProp) + } + + function deleteAdditional(key: Name): void { + gen.code(_`delete ${data}[${key}]`) + } + + function additionalPropertyCode(key: Name): void { + if (opts.removeAdditional === "all" || (opts.removeAdditional && schema === false)) { + deleteAdditional(key) + return + } + + if (schema === false) { + cxt.setParams({additionalProperty: key}) + cxt.error() + if (!allErrors) gen.break() + return + } + + if (typeof schema == "object" && !alwaysValidSchema(it, schema)) { + const valid = gen.name("valid") + if (opts.removeAdditional === "failing") { + applyAdditionalSchema(key, valid, false) + gen.if(not(valid), () => { + cxt.reset() + deleteAdditional(key) + }) + } else { + applyAdditionalSchema(key, valid) + if (!allErrors) gen.if(not(valid), () => gen.break()) + } + } + } + + function applyAdditionalSchema(key: Name, valid: Name, errors?: false): void { + const subschema: SubschemaArgs = { + keyword: "additionalProperties", + dataProp: key, + dataPropType: Type.Str, + } + if (errors === false) { + Object.assign(subschema, { + compositeRule: true, + createErrors: false, + allErrors: false, + }) + } + cxt.subschema(subschema, valid) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/allOf.ts b/website/node_modules/ajv/lib/vocabularies/applicator/allOf.ts new file mode 100644 index 00000000..cdfa86ff --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/allOf.ts @@ -0,0 +1,22 @@ +import type {CodeKeywordDefinition, AnySchema} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {alwaysValidSchema} from "../../compile/util" + +const def: CodeKeywordDefinition = { + keyword: "allOf", + schemaType: "array", + code(cxt: KeywordCxt) { + const {gen, schema, it} = cxt + /* istanbul ignore if */ + if (!Array.isArray(schema)) throw new Error("ajv implementation error") + const valid = gen.name("valid") + schema.forEach((sch: AnySchema, i: number) => { + if (alwaysValidSchema(it, sch)) return + const schCxt = cxt.subschema({keyword: "allOf", schemaProp: i}, valid) + cxt.ok(valid) + cxt.mergeEvaluated(schCxt) + }) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/anyOf.ts b/website/node_modules/ajv/lib/vocabularies/applicator/anyOf.ts new file mode 100644 index 00000000..bd331b5a --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/anyOf.ts @@ -0,0 +1,14 @@ +import type {CodeKeywordDefinition, ErrorNoParams, AnySchema} from "../../types" +import {validateUnion} from "../code" + +export type AnyOfError = ErrorNoParams<"anyOf", AnySchema[]> + +const def: CodeKeywordDefinition = { + keyword: "anyOf", + schemaType: "array", + trackErrors: true, + code: validateUnion, + error: {message: "must match a schema in anyOf"}, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/contains.ts b/website/node_modules/ajv/lib/vocabularies/applicator/contains.ts new file mode 100644 index 00000000..d88675c6 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/contains.ts @@ -0,0 +1,109 @@ +import type { + CodeKeywordDefinition, + KeywordErrorDefinition, + ErrorObject, + AnySchema, +} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str, Name} from "../../compile/codegen" +import {alwaysValidSchema, checkStrictMode, Type} from "../../compile/util" + +export type ContainsError = ErrorObject< + "contains", + {minContains: number; maxContains?: number}, + AnySchema +> + +const error: KeywordErrorDefinition = { + message: ({params: {min, max}}) => + max === undefined + ? str`must contain at least ${min} valid item(s)` + : str`must contain at least ${min} and no more than ${max} valid item(s)`, + params: ({params: {min, max}}) => + max === undefined ? _`{minContains: ${min}}` : _`{minContains: ${min}, maxContains: ${max}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "contains", + type: "array", + schemaType: ["object", "boolean"], + before: "uniqueItems", + trackErrors: true, + error, + code(cxt: KeywordCxt) { + const {gen, schema, parentSchema, data, it} = cxt + let min: number + let max: number | undefined + const {minContains, maxContains} = parentSchema + if (it.opts.next) { + min = minContains === undefined ? 1 : minContains + max = maxContains + } else { + min = 1 + } + const len = gen.const("len", _`${data}.length`) + cxt.setParams({min, max}) + if (max === undefined && min === 0) { + checkStrictMode(it, `"minContains" == 0 without "maxContains": "contains" keyword ignored`) + return + } + if (max !== undefined && min > max) { + checkStrictMode(it, `"minContains" > "maxContains" is always invalid`) + cxt.fail() + return + } + if (alwaysValidSchema(it, schema)) { + let cond = _`${len} >= ${min}` + if (max !== undefined) cond = _`${cond} && ${len} <= ${max}` + cxt.pass(cond) + return + } + + it.items = true + const valid = gen.name("valid") + if (max === undefined && min === 1) { + validateItems(valid, () => gen.if(valid, () => gen.break())) + } else if (min === 0) { + gen.let(valid, true) + if (max !== undefined) gen.if(_`${data}.length > 0`, validateItemsWithCount) + } else { + gen.let(valid, false) + validateItemsWithCount() + } + cxt.result(valid, () => cxt.reset()) + + function validateItemsWithCount(): void { + const schValid = gen.name("_valid") + const count = gen.let("count", 0) + validateItems(schValid, () => gen.if(schValid, () => checkLimits(count))) + } + + function validateItems(_valid: Name, block: () => void): void { + gen.forRange("i", 0, len, (i) => { + cxt.subschema( + { + keyword: "contains", + dataProp: i, + dataPropType: Type.Num, + compositeRule: true, + }, + _valid + ) + block() + }) + } + + function checkLimits(count: Name): void { + gen.code(_`${count}++`) + if (max === undefined) { + gen.if(_`${count} >= ${min}`, () => gen.assign(valid, true).break()) + } else { + gen.if(_`${count} > ${max}`, () => gen.assign(valid, false).break()) + if (min === 1) gen.assign(valid, true) + else gen.if(_`${count} >= ${min}`, () => gen.assign(valid, true)) + } + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/dependencies.ts b/website/node_modules/ajv/lib/vocabularies/applicator/dependencies.ts new file mode 100644 index 00000000..f6761128 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/dependencies.ts @@ -0,0 +1,112 @@ +import type { + CodeKeywordDefinition, + ErrorObject, + KeywordErrorDefinition, + SchemaMap, + AnySchema, +} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str} from "../../compile/codegen" +import {alwaysValidSchema} from "../../compile/util" +import {checkReportMissingProp, checkMissingProp, reportMissingProp, propertyInData} from "../code" + +export type PropertyDependencies = {[K in string]?: string[]} + +export interface DependenciesErrorParams { + property: string + missingProperty: string + depsCount: number + deps: string // TODO change to string[] +} + +type SchemaDependencies = SchemaMap + +export type DependenciesError = ErrorObject< + "dependencies", + DependenciesErrorParams, + {[K in string]?: string[] | AnySchema} +> + +export const error: KeywordErrorDefinition = { + message: ({params: {property, depsCount, deps}}) => { + const property_ies = depsCount === 1 ? "property" : "properties" + return str`must have ${property_ies} ${deps} when property ${property} is present` + }, + params: ({params: {property, depsCount, deps, missingProperty}}) => + _`{property: ${property}, + missingProperty: ${missingProperty}, + depsCount: ${depsCount}, + deps: ${deps}}`, // TODO change to reference +} + +const def: CodeKeywordDefinition = { + keyword: "dependencies", + type: "object", + schemaType: "object", + error, + code(cxt: KeywordCxt) { + const [propDeps, schDeps] = splitDependencies(cxt) + validatePropertyDeps(cxt, propDeps) + validateSchemaDeps(cxt, schDeps) + }, +} + +function splitDependencies({schema}: KeywordCxt): [PropertyDependencies, SchemaDependencies] { + const propertyDeps: PropertyDependencies = {} + const schemaDeps: SchemaDependencies = {} + for (const key in schema) { + if (key === "__proto__") continue + const deps = Array.isArray(schema[key]) ? propertyDeps : schemaDeps + deps[key] = schema[key] + } + return [propertyDeps, schemaDeps] +} + +export function validatePropertyDeps( + cxt: KeywordCxt, + propertyDeps: {[K in string]?: string[]} = cxt.schema +): void { + const {gen, data, it} = cxt + if (Object.keys(propertyDeps).length === 0) return + const missing = gen.let("missing") + for (const prop in propertyDeps) { + const deps = propertyDeps[prop] as string[] + if (deps.length === 0) continue + const hasProperty = propertyInData(gen, data, prop, it.opts.ownProperties) + cxt.setParams({ + property: prop, + depsCount: deps.length, + deps: deps.join(", "), + }) + if (it.allErrors) { + gen.if(hasProperty, () => { + for (const depProp of deps) { + checkReportMissingProp(cxt, depProp) + } + }) + } else { + gen.if(_`${hasProperty} && (${checkMissingProp(cxt, deps, missing)})`) + reportMissingProp(cxt, missing) + gen.else() + } + } +} + +export function validateSchemaDeps(cxt: KeywordCxt, schemaDeps: SchemaMap = cxt.schema): void { + const {gen, data, keyword, it} = cxt + const valid = gen.name("valid") + for (const prop in schemaDeps) { + if (alwaysValidSchema(it, schemaDeps[prop] as AnySchema)) continue + gen.if( + propertyInData(gen, data, prop, it.opts.ownProperties), + () => { + const schCxt = cxt.subschema({keyword, schemaProp: prop}, valid) + cxt.mergeValidEvaluated(schCxt, valid) + }, + () => gen.var(valid, true) // TODO var + ) + cxt.ok(valid) + } +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/dependentSchemas.ts b/website/node_modules/ajv/lib/vocabularies/applicator/dependentSchemas.ts new file mode 100644 index 00000000..dbd3ae45 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/dependentSchemas.ts @@ -0,0 +1,11 @@ +import type {CodeKeywordDefinition} from "../../types" +import {validateSchemaDeps} from "./dependencies" + +const def: CodeKeywordDefinition = { + keyword: "dependentSchemas", + type: "object", + schemaType: "object", + code: (cxt) => validateSchemaDeps(cxt), +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/if.ts b/website/node_modules/ajv/lib/vocabularies/applicator/if.ts new file mode 100644 index 00000000..5a40d5e3 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/if.ts @@ -0,0 +1,80 @@ +import type { + CodeKeywordDefinition, + ErrorObject, + KeywordErrorDefinition, + AnySchema, +} from "../../types" +import type {SchemaObjCxt} from "../../compile" +import type {KeywordCxt} from "../../compile/validate" +import {_, str, not, Name} from "../../compile/codegen" +import {alwaysValidSchema, checkStrictMode} from "../../compile/util" + +export type IfKeywordError = ErrorObject<"if", {failingKeyword: string}, AnySchema> + +const error: KeywordErrorDefinition = { + message: ({params}) => str`must match "${params.ifClause}" schema`, + params: ({params}) => _`{failingKeyword: ${params.ifClause}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "if", + schemaType: ["object", "boolean"], + trackErrors: true, + error, + code(cxt: KeywordCxt) { + const {gen, parentSchema, it} = cxt + if (parentSchema.then === undefined && parentSchema.else === undefined) { + checkStrictMode(it, '"if" without "then" and "else" is ignored') + } + const hasThen = hasSchema(it, "then") + const hasElse = hasSchema(it, "else") + if (!hasThen && !hasElse) return + + const valid = gen.let("valid", true) + const schValid = gen.name("_valid") + validateIf() + cxt.reset() + + if (hasThen && hasElse) { + const ifClause = gen.let("ifClause") + cxt.setParams({ifClause}) + gen.if(schValid, validateClause("then", ifClause), validateClause("else", ifClause)) + } else if (hasThen) { + gen.if(schValid, validateClause("then")) + } else { + gen.if(not(schValid), validateClause("else")) + } + + cxt.pass(valid, () => cxt.error(true)) + + function validateIf(): void { + const schCxt = cxt.subschema( + { + keyword: "if", + compositeRule: true, + createErrors: false, + allErrors: false, + }, + schValid + ) + cxt.mergeEvaluated(schCxt) + } + + function validateClause(keyword: string, ifClause?: Name): () => void { + return () => { + const schCxt = cxt.subschema({keyword}, schValid) + gen.assign(valid, schValid) + cxt.mergeValidEvaluated(schCxt, valid) + if (ifClause) gen.assign(ifClause, _`${keyword}`) + else cxt.setParams({ifClause: keyword}) + } + } + }, +} + +function hasSchema(it: SchemaObjCxt, keyword: string): boolean { + const schema = it.schema[keyword] + return schema !== undefined && !alwaysValidSchema(it, schema) +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/index.ts b/website/node_modules/ajv/lib/vocabularies/applicator/index.ts new file mode 100644 index 00000000..fc527169 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/index.ts @@ -0,0 +1,53 @@ +import type {ErrorNoParams, Vocabulary} from "../../types" +import additionalItems, {AdditionalItemsError} from "./additionalItems" +import prefixItems from "./prefixItems" +import items from "./items" +import items2020, {ItemsError} from "./items2020" +import contains, {ContainsError} from "./contains" +import dependencies, {DependenciesError} from "./dependencies" +import propertyNames, {PropertyNamesError} from "./propertyNames" +import additionalProperties, {AdditionalPropertiesError} from "./additionalProperties" +import properties from "./properties" +import patternProperties from "./patternProperties" +import notKeyword, {NotKeywordError} from "./not" +import anyOf, {AnyOfError} from "./anyOf" +import oneOf, {OneOfError} from "./oneOf" +import allOf from "./allOf" +import ifKeyword, {IfKeywordError} from "./if" +import thenElse from "./thenElse" + +export default function getApplicator(draft2020 = false): Vocabulary { + const applicator = [ + // any + notKeyword, + anyOf, + oneOf, + allOf, + ifKeyword, + thenElse, + // object + propertyNames, + additionalProperties, + dependencies, + properties, + patternProperties, + ] + // array + if (draft2020) applicator.push(prefixItems, items2020) + else applicator.push(additionalItems, items) + applicator.push(contains) + return applicator +} + +export type ApplicatorKeywordError = + | ErrorNoParams<"false schema"> + | AdditionalItemsError + | ItemsError + | ContainsError + | AdditionalPropertiesError + | DependenciesError + | IfKeywordError + | AnyOfError + | OneOfError + | NotKeywordError + | PropertyNamesError diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/items.ts b/website/node_modules/ajv/lib/vocabularies/applicator/items.ts new file mode 100644 index 00000000..033cb397 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/items.ts @@ -0,0 +1,59 @@ +import type {CodeKeywordDefinition, AnySchema, AnySchemaObject} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_} from "../../compile/codegen" +import {alwaysValidSchema, mergeEvaluated, checkStrictMode} from "../../compile/util" +import {validateArray} from "../code" + +const def: CodeKeywordDefinition = { + keyword: "items", + type: "array", + schemaType: ["object", "array", "boolean"], + before: "uniqueItems", + code(cxt: KeywordCxt) { + const {schema, it} = cxt + if (Array.isArray(schema)) return validateTuple(cxt, "additionalItems", schema) + it.items = true + if (alwaysValidSchema(it, schema)) return + cxt.ok(validateArray(cxt)) + }, +} + +export function validateTuple( + cxt: KeywordCxt, + extraItems: string, + schArr: AnySchema[] = cxt.schema +): void { + const {gen, parentSchema, data, keyword, it} = cxt + checkStrictTuple(parentSchema) + if (it.opts.unevaluated && schArr.length && it.items !== true) { + it.items = mergeEvaluated.items(gen, schArr.length, it.items) + } + const valid = gen.name("valid") + const len = gen.const("len", _`${data}.length`) + schArr.forEach((sch: AnySchema, i: number) => { + if (alwaysValidSchema(it, sch)) return + gen.if(_`${len} > ${i}`, () => + cxt.subschema( + { + keyword, + schemaProp: i, + dataProp: i, + }, + valid + ) + ) + cxt.ok(valid) + }) + + function checkStrictTuple(sch: AnySchemaObject): void { + const {opts, errSchemaPath} = it + const l = schArr.length + const fullTuple = l === sch.minItems && (l === sch.maxItems || sch[extraItems] === false) + if (opts.strictTuples && !fullTuple) { + const msg = `"${keyword}" is ${l}-tuple, but minItems or maxItems/${extraItems} are not specified or different at path "${errSchemaPath}"` + checkStrictMode(it, msg, opts.strictTuples) + } + } +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/items2020.ts b/website/node_modules/ajv/lib/vocabularies/applicator/items2020.ts new file mode 100644 index 00000000..2a99b08d --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/items2020.ts @@ -0,0 +1,36 @@ +import type { + CodeKeywordDefinition, + KeywordErrorDefinition, + ErrorObject, + AnySchema, +} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str} from "../../compile/codegen" +import {alwaysValidSchema} from "../../compile/util" +import {validateArray} from "../code" +import {validateAdditionalItems} from "./additionalItems" + +export type ItemsError = ErrorObject<"items", {limit: number}, AnySchema> + +const error: KeywordErrorDefinition = { + message: ({params: {len}}) => str`must NOT have more than ${len} items`, + params: ({params: {len}}) => _`{limit: ${len}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "items", + type: "array", + schemaType: ["object", "boolean"], + before: "uniqueItems", + error, + code(cxt: KeywordCxt) { + const {schema, parentSchema, it} = cxt + const {prefixItems} = parentSchema + it.items = true + if (alwaysValidSchema(it, schema)) return + if (prefixItems) validateAdditionalItems(cxt, prefixItems) + else cxt.ok(validateArray(cxt)) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/not.ts b/website/node_modules/ajv/lib/vocabularies/applicator/not.ts new file mode 100644 index 00000000..8691db0b --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/not.ts @@ -0,0 +1,38 @@ +import type {CodeKeywordDefinition, ErrorNoParams, AnySchema} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {alwaysValidSchema} from "../../compile/util" + +export type NotKeywordError = ErrorNoParams<"not", AnySchema> + +const def: CodeKeywordDefinition = { + keyword: "not", + schemaType: ["object", "boolean"], + trackErrors: true, + code(cxt: KeywordCxt) { + const {gen, schema, it} = cxt + if (alwaysValidSchema(it, schema)) { + cxt.fail() + return + } + + const valid = gen.name("valid") + cxt.subschema( + { + keyword: "not", + compositeRule: true, + createErrors: false, + allErrors: false, + }, + valid + ) + + cxt.failResult( + valid, + () => cxt.reset(), + () => cxt.error() + ) + }, + error: {message: "must NOT be valid"}, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/oneOf.ts b/website/node_modules/ajv/lib/vocabularies/applicator/oneOf.ts new file mode 100644 index 00000000..c25353ff --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/oneOf.ts @@ -0,0 +1,82 @@ +import type { + CodeKeywordDefinition, + ErrorObject, + KeywordErrorDefinition, + AnySchema, +} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, Name} from "../../compile/codegen" +import {alwaysValidSchema} from "../../compile/util" +import {SchemaCxt} from "../../compile" + +export type OneOfError = ErrorObject< + "oneOf", + {passingSchemas: [number, number] | null}, + AnySchema[] +> + +const error: KeywordErrorDefinition = { + message: "must match exactly one schema in oneOf", + params: ({params}) => _`{passingSchemas: ${params.passing}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "oneOf", + schemaType: "array", + trackErrors: true, + error, + code(cxt: KeywordCxt) { + const {gen, schema, parentSchema, it} = cxt + /* istanbul ignore if */ + if (!Array.isArray(schema)) throw new Error("ajv implementation error") + if (it.opts.discriminator && parentSchema.discriminator) return + const schArr: AnySchema[] = schema + const valid = gen.let("valid", false) + const passing = gen.let("passing", null) + const schValid = gen.name("_valid") + cxt.setParams({passing}) + // TODO possibly fail straight away (with warning or exception) if there are two empty always valid schemas + + gen.block(validateOneOf) + + cxt.result( + valid, + () => cxt.reset(), + () => cxt.error(true) + ) + + function validateOneOf(): void { + schArr.forEach((sch: AnySchema, i: number) => { + let schCxt: SchemaCxt | undefined + if (alwaysValidSchema(it, sch)) { + gen.var(schValid, true) + } else { + schCxt = cxt.subschema( + { + keyword: "oneOf", + schemaProp: i, + compositeRule: true, + }, + schValid + ) + } + + if (i > 0) { + gen + .if(_`${schValid} && ${valid}`) + .assign(valid, false) + .assign(passing, _`[${passing}, ${i}]`) + .else() + } + + gen.if(schValid, () => { + gen.assign(valid, true) + gen.assign(passing, i) + if (schCxt) cxt.mergeEvaluated(schCxt, Name) + }) + }) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/patternProperties.ts b/website/node_modules/ajv/lib/vocabularies/applicator/patternProperties.ts new file mode 100644 index 00000000..ea624e23 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/patternProperties.ts @@ -0,0 +1,91 @@ +import type {CodeKeywordDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {allSchemaProperties, usePattern} from "../code" +import {_, not, Name} from "../../compile/codegen" +import {alwaysValidSchema, checkStrictMode} from "../../compile/util" +import {evaluatedPropsToName, Type} from "../../compile/util" +import {AnySchema} from "../../types" + +const def: CodeKeywordDefinition = { + keyword: "patternProperties", + type: "object", + schemaType: "object", + code(cxt: KeywordCxt) { + const {gen, schema, data, parentSchema, it} = cxt + const {opts} = it + const patterns = allSchemaProperties(schema) + const alwaysValidPatterns = patterns.filter((p) => + alwaysValidSchema(it, schema[p] as AnySchema) + ) + + if ( + patterns.length === 0 || + (alwaysValidPatterns.length === patterns.length && + (!it.opts.unevaluated || it.props === true)) + ) { + return + } + + const checkProperties = + opts.strictSchema && !opts.allowMatchingProperties && parentSchema.properties + const valid = gen.name("valid") + if (it.props !== true && !(it.props instanceof Name)) { + it.props = evaluatedPropsToName(gen, it.props) + } + const {props} = it + validatePatternProperties() + + function validatePatternProperties(): void { + for (const pat of patterns) { + if (checkProperties) checkMatchingProperties(pat) + if (it.allErrors) { + validateProperties(pat) + } else { + gen.var(valid, true) // TODO var + validateProperties(pat) + gen.if(valid) + } + } + } + + function checkMatchingProperties(pat: string): void { + for (const prop in checkProperties) { + if (new RegExp(pat).test(prop)) { + checkStrictMode( + it, + `property ${prop} matches pattern ${pat} (use allowMatchingProperties)` + ) + } + } + } + + function validateProperties(pat: string): void { + gen.forIn("key", data, (key) => { + gen.if(_`${usePattern(cxt, pat)}.test(${key})`, () => { + const alwaysValid = alwaysValidPatterns.includes(pat) + if (!alwaysValid) { + cxt.subschema( + { + keyword: "patternProperties", + schemaProp: pat, + dataProp: key, + dataPropType: Type.Str, + }, + valid + ) + } + + if (it.opts.unevaluated && props !== true) { + gen.assign(_`${props}[${key}]`, true) + } else if (!alwaysValid && !it.allErrors) { + // can short-circuit if `unevaluatedProperties` is not supported (opts.next === false) + // or if all properties were evaluated (props === true) + gen.if(not(valid), () => gen.break()) + } + }) + }) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/prefixItems.ts b/website/node_modules/ajv/lib/vocabularies/applicator/prefixItems.ts new file mode 100644 index 00000000..008fb2db --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/prefixItems.ts @@ -0,0 +1,12 @@ +import type {CodeKeywordDefinition} from "../../types" +import {validateTuple} from "./items" + +const def: CodeKeywordDefinition = { + keyword: "prefixItems", + type: "array", + schemaType: ["array"], + before: "uniqueItems", + code: (cxt) => validateTuple(cxt, "items"), +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/properties.ts b/website/node_modules/ajv/lib/vocabularies/applicator/properties.ts new file mode 100644 index 00000000..a55b19ce --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/properties.ts @@ -0,0 +1,57 @@ +import type {CodeKeywordDefinition} from "../../types" +import {KeywordCxt} from "../../compile/validate" +import {propertyInData, allSchemaProperties} from "../code" +import {alwaysValidSchema, toHash, mergeEvaluated} from "../../compile/util" +import apDef from "./additionalProperties" + +const def: CodeKeywordDefinition = { + keyword: "properties", + type: "object", + schemaType: "object", + code(cxt: KeywordCxt) { + const {gen, schema, parentSchema, data, it} = cxt + if (it.opts.removeAdditional === "all" && parentSchema.additionalProperties === undefined) { + apDef.code(new KeywordCxt(it, apDef, "additionalProperties")) + } + const allProps = allSchemaProperties(schema) + for (const prop of allProps) { + it.definedProperties.add(prop) + } + if (it.opts.unevaluated && allProps.length && it.props !== true) { + it.props = mergeEvaluated.props(gen, toHash(allProps), it.props) + } + const properties = allProps.filter((p) => !alwaysValidSchema(it, schema[p])) + if (properties.length === 0) return + const valid = gen.name("valid") + + for (const prop of properties) { + if (hasDefault(prop)) { + applyPropertySchema(prop) + } else { + gen.if(propertyInData(gen, data, prop, it.opts.ownProperties)) + applyPropertySchema(prop) + if (!it.allErrors) gen.else().var(valid, true) + gen.endIf() + } + cxt.it.definedProperties.add(prop) + cxt.ok(valid) + } + + function hasDefault(prop: string): boolean | undefined { + return it.opts.useDefaults && !it.compositeRule && schema[prop].default !== undefined + } + + function applyPropertySchema(prop: string): void { + cxt.subschema( + { + keyword: "properties", + schemaProp: prop, + dataProp: prop, + }, + valid + ) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/propertyNames.ts b/website/node_modules/ajv/lib/vocabularies/applicator/propertyNames.ts new file mode 100644 index 00000000..1c54d605 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/propertyNames.ts @@ -0,0 +1,50 @@ +import type { + CodeKeywordDefinition, + ErrorObject, + KeywordErrorDefinition, + AnySchema, +} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, not} from "../../compile/codegen" +import {alwaysValidSchema} from "../../compile/util" + +export type PropertyNamesError = ErrorObject<"propertyNames", {propertyName: string}, AnySchema> + +const error: KeywordErrorDefinition = { + message: "property name must be valid", + params: ({params}) => _`{propertyName: ${params.propertyName}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "propertyNames", + type: "object", + schemaType: ["object", "boolean"], + error, + code(cxt: KeywordCxt) { + const {gen, schema, data, it} = cxt + if (alwaysValidSchema(it, schema)) return + const valid = gen.name("valid") + + gen.forIn("key", data, (key) => { + cxt.setParams({propertyName: key}) + cxt.subschema( + { + keyword: "propertyNames", + data: key, + dataTypes: ["string"], + propertyName: key, + compositeRule: true, + }, + valid + ) + gen.if(not(valid), () => { + cxt.error(true) + if (!it.allErrors) gen.break() + }) + }) + + cxt.ok(valid) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/applicator/thenElse.ts b/website/node_modules/ajv/lib/vocabularies/applicator/thenElse.ts new file mode 100644 index 00000000..5055182e --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/applicator/thenElse.ts @@ -0,0 +1,13 @@ +import type {CodeKeywordDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {checkStrictMode} from "../../compile/util" + +const def: CodeKeywordDefinition = { + keyword: ["then", "else"], + schemaType: ["object", "boolean"], + code({keyword, parentSchema, it}: KeywordCxt) { + if (parentSchema.if === undefined) checkStrictMode(it, `"${keyword}" without "if" is ignored`) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/code.ts b/website/node_modules/ajv/lib/vocabularies/code.ts new file mode 100644 index 00000000..92cdd5b0 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/code.ts @@ -0,0 +1,168 @@ +import type {AnySchema, SchemaMap} from "../types" +import type {SchemaCxt} from "../compile" +import type {KeywordCxt} from "../compile/validate" +import {CodeGen, _, and, or, not, nil, strConcat, getProperty, Code, Name} from "../compile/codegen" +import {alwaysValidSchema, Type} from "../compile/util" +import N from "../compile/names" +import {useFunc} from "../compile/util" +export function checkReportMissingProp(cxt: KeywordCxt, prop: string): void { + const {gen, data, it} = cxt + gen.if(noPropertyInData(gen, data, prop, it.opts.ownProperties), () => { + cxt.setParams({missingProperty: _`${prop}`}, true) + cxt.error() + }) +} + +export function checkMissingProp( + {gen, data, it: {opts}}: KeywordCxt, + properties: string[], + missing: Name +): Code { + return or( + ...properties.map((prop) => + and(noPropertyInData(gen, data, prop, opts.ownProperties), _`${missing} = ${prop}`) + ) + ) +} + +export function reportMissingProp(cxt: KeywordCxt, missing: Name): void { + cxt.setParams({missingProperty: missing}, true) + cxt.error() +} + +export function hasPropFunc(gen: CodeGen): Name { + return gen.scopeValue("func", { + // eslint-disable-next-line @typescript-eslint/unbound-method + ref: Object.prototype.hasOwnProperty, + code: _`Object.prototype.hasOwnProperty`, + }) +} + +export function isOwnProperty(gen: CodeGen, data: Name, property: Name | string): Code { + return _`${hasPropFunc(gen)}.call(${data}, ${property})` +} + +export function propertyInData( + gen: CodeGen, + data: Name, + property: Name | string, + ownProperties?: boolean +): Code { + const cond = _`${data}${getProperty(property)} !== undefined` + return ownProperties ? _`${cond} && ${isOwnProperty(gen, data, property)}` : cond +} + +export function noPropertyInData( + gen: CodeGen, + data: Name, + property: Name | string, + ownProperties?: boolean +): Code { + const cond = _`${data}${getProperty(property)} === undefined` + return ownProperties ? or(cond, not(isOwnProperty(gen, data, property))) : cond +} + +export function allSchemaProperties(schemaMap?: SchemaMap): string[] { + return schemaMap ? Object.keys(schemaMap).filter((p) => p !== "__proto__") : [] +} + +export function schemaProperties(it: SchemaCxt, schemaMap: SchemaMap): string[] { + return allSchemaProperties(schemaMap).filter( + (p) => !alwaysValidSchema(it, schemaMap[p] as AnySchema) + ) +} + +export function callValidateCode( + {schemaCode, data, it: {gen, topSchemaRef, schemaPath, errorPath}, it}: KeywordCxt, + func: Code, + context: Code, + passSchema?: boolean +): Code { + const dataAndSchema = passSchema ? _`${schemaCode}, ${data}, ${topSchemaRef}${schemaPath}` : data + const valCxt: [Name, Code | number][] = [ + [N.instancePath, strConcat(N.instancePath, errorPath)], + [N.parentData, it.parentData], + [N.parentDataProperty, it.parentDataProperty], + [N.rootData, N.rootData], + ] + if (it.opts.dynamicRef) valCxt.push([N.dynamicAnchors, N.dynamicAnchors]) + const args = _`${dataAndSchema}, ${gen.object(...valCxt)}` + return context !== nil ? _`${func}.call(${context}, ${args})` : _`${func}(${args})` +} + +const newRegExp = _`new RegExp` + +export function usePattern({gen, it: {opts}}: KeywordCxt, pattern: string): Name { + const u = opts.unicodeRegExp ? "u" : "" + const {regExp} = opts.code + const rx = regExp(pattern, u) + + return gen.scopeValue("pattern", { + key: rx.toString(), + ref: rx, + code: _`${regExp.code === "new RegExp" ? newRegExp : useFunc(gen, regExp)}(${pattern}, ${u})`, + }) +} + +export function validateArray(cxt: KeywordCxt): Name { + const {gen, data, keyword, it} = cxt + const valid = gen.name("valid") + if (it.allErrors) { + const validArr = gen.let("valid", true) + validateItems(() => gen.assign(validArr, false)) + return validArr + } + gen.var(valid, true) + validateItems(() => gen.break()) + return valid + + function validateItems(notValid: () => void): void { + const len = gen.const("len", _`${data}.length`) + gen.forRange("i", 0, len, (i) => { + cxt.subschema( + { + keyword, + dataProp: i, + dataPropType: Type.Num, + }, + valid + ) + gen.if(not(valid), notValid) + }) + } +} + +export function validateUnion(cxt: KeywordCxt): void { + const {gen, schema, keyword, it} = cxt + /* istanbul ignore if */ + if (!Array.isArray(schema)) throw new Error("ajv implementation error") + const alwaysValid = schema.some((sch: AnySchema) => alwaysValidSchema(it, sch)) + if (alwaysValid && !it.opts.unevaluated) return + + const valid = gen.let("valid", false) + const schValid = gen.name("_valid") + + gen.block(() => + schema.forEach((_sch: AnySchema, i: number) => { + const schCxt = cxt.subschema( + { + keyword, + schemaProp: i, + compositeRule: true, + }, + schValid + ) + gen.assign(valid, _`${valid} || ${schValid}`) + const merged = cxt.mergeValidEvaluated(schCxt, schValid) + // can short-circuit if `unevaluatedProperties/Items` not supported (opts.unevaluated !== true) + // or if all properties and items were evaluated (it.props === true && it.items === true) + if (!merged) gen.if(not(valid)) + }) + ) + + cxt.result( + valid, + () => cxt.reset(), + () => cxt.error(true) + ) +} diff --git a/website/node_modules/ajv/lib/vocabularies/core/id.ts b/website/node_modules/ajv/lib/vocabularies/core/id.ts new file mode 100644 index 00000000..aa36c4bb --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/core/id.ts @@ -0,0 +1,10 @@ +import type {CodeKeywordDefinition} from "../../types" + +const def: CodeKeywordDefinition = { + keyword: "id", + code() { + throw new Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID') + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/core/index.ts b/website/node_modules/ajv/lib/vocabularies/core/index.ts new file mode 100644 index 00000000..e63e2895 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/core/index.ts @@ -0,0 +1,16 @@ +import type {Vocabulary} from "../../types" +import idKeyword from "./id" +import refKeyword from "./ref" + +const core: Vocabulary = [ + "$schema", + "$id", + "$defs", + "$vocabulary", + {keyword: "$comment"}, + "definitions", + idKeyword, + refKeyword, +] + +export default core diff --git a/website/node_modules/ajv/lib/vocabularies/core/ref.ts b/website/node_modules/ajv/lib/vocabularies/core/ref.ts new file mode 100644 index 00000000..5d59fbcb --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/core/ref.ts @@ -0,0 +1,129 @@ +import type {CodeKeywordDefinition, AnySchema} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import MissingRefError from "../../compile/ref_error" +import {callValidateCode} from "../code" +import {_, nil, stringify, Code, Name} from "../../compile/codegen" +import N from "../../compile/names" +import {SchemaEnv, resolveRef} from "../../compile" +import {mergeEvaluated} from "../../compile/util" + +const def: CodeKeywordDefinition = { + keyword: "$ref", + schemaType: "string", + code(cxt: KeywordCxt): void { + const {gen, schema: $ref, it} = cxt + const {baseId, schemaEnv: env, validateName, opts, self} = it + const {root} = env + if (($ref === "#" || $ref === "#/") && baseId === root.baseId) return callRootRef() + const schOrEnv = resolveRef.call(self, root, baseId, $ref) + if (schOrEnv === undefined) throw new MissingRefError(it.opts.uriResolver, baseId, $ref) + if (schOrEnv instanceof SchemaEnv) return callValidate(schOrEnv) + return inlineRefSchema(schOrEnv) + + function callRootRef(): void { + if (env === root) return callRef(cxt, validateName, env, env.$async) + const rootName = gen.scopeValue("root", {ref: root}) + return callRef(cxt, _`${rootName}.validate`, root, root.$async) + } + + function callValidate(sch: SchemaEnv): void { + const v = getValidate(cxt, sch) + callRef(cxt, v, sch, sch.$async) + } + + function inlineRefSchema(sch: AnySchema): void { + const schName = gen.scopeValue( + "schema", + opts.code.source === true ? {ref: sch, code: stringify(sch)} : {ref: sch} + ) + const valid = gen.name("valid") + const schCxt = cxt.subschema( + { + schema: sch, + dataTypes: [], + schemaPath: nil, + topSchemaRef: schName, + errSchemaPath: $ref, + }, + valid + ) + cxt.mergeEvaluated(schCxt) + cxt.ok(valid) + } + }, +} + +export function getValidate(cxt: KeywordCxt, sch: SchemaEnv): Code { + const {gen} = cxt + return sch.validate + ? gen.scopeValue("validate", {ref: sch.validate}) + : _`${gen.scopeValue("wrapper", {ref: sch})}.validate` +} + +export function callRef(cxt: KeywordCxt, v: Code, sch?: SchemaEnv, $async?: boolean): void { + const {gen, it} = cxt + const {allErrors, schemaEnv: env, opts} = it + const passCxt = opts.passContext ? N.this : nil + if ($async) callAsyncRef() + else callSyncRef() + + function callAsyncRef(): void { + if (!env.$async) throw new Error("async schema referenced by sync schema") + const valid = gen.let("valid") + gen.try( + () => { + gen.code(_`await ${callValidateCode(cxt, v, passCxt)}`) + addEvaluatedFrom(v) // TODO will not work with async, it has to be returned with the result + if (!allErrors) gen.assign(valid, true) + }, + (e) => { + gen.if(_`!(${e} instanceof ${it.ValidationError as Name})`, () => gen.throw(e)) + addErrorsFrom(e) + if (!allErrors) gen.assign(valid, false) + } + ) + cxt.ok(valid) + } + + function callSyncRef(): void { + cxt.result( + callValidateCode(cxt, v, passCxt), + () => addEvaluatedFrom(v), + () => addErrorsFrom(v) + ) + } + + function addErrorsFrom(source: Code): void { + const errs = _`${source}.errors` + gen.assign(N.vErrors, _`${N.vErrors} === null ? ${errs} : ${N.vErrors}.concat(${errs})`) // TODO tagged + gen.assign(N.errors, _`${N.vErrors}.length`) + } + + function addEvaluatedFrom(source: Code): void { + if (!it.opts.unevaluated) return + const schEvaluated = sch?.validate?.evaluated + // TODO refactor + if (it.props !== true) { + if (schEvaluated && !schEvaluated.dynamicProps) { + if (schEvaluated.props !== undefined) { + it.props = mergeEvaluated.props(gen, schEvaluated.props, it.props) + } + } else { + const props = gen.var("props", _`${source}.evaluated.props`) + it.props = mergeEvaluated.props(gen, props, it.props, Name) + } + } + if (it.items !== true) { + if (schEvaluated && !schEvaluated.dynamicItems) { + if (schEvaluated.items !== undefined) { + it.items = mergeEvaluated.items(gen, schEvaluated.items, it.items) + } + } else { + const items = gen.var("items", _`${source}.evaluated.items`) + it.items = mergeEvaluated.items(gen, items, it.items, Name) + } + } + } +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/discriminator/index.ts b/website/node_modules/ajv/lib/vocabularies/discriminator/index.ts new file mode 100644 index 00000000..98f0f8cf --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/discriminator/index.ts @@ -0,0 +1,110 @@ +import type {CodeKeywordDefinition, AnySchemaObject, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, getProperty, Name} from "../../compile/codegen" +import {DiscrError, DiscrErrorObj} from "../discriminator/types" +import {resolveRef, SchemaEnv} from "../../compile" +import {schemaHasRulesButRef} from "../../compile/util" + +export type DiscriminatorError = DiscrErrorObj | DiscrErrorObj + +const error: KeywordErrorDefinition = { + message: ({params: {discrError, tagName}}) => + discrError === DiscrError.Tag + ? `tag "${tagName}" must be string` + : `value of tag "${tagName}" must be in oneOf`, + params: ({params: {discrError, tag, tagName}}) => + _`{error: ${discrError}, tag: ${tagName}, tagValue: ${tag}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "discriminator", + type: "object", + schemaType: "object", + error, + code(cxt: KeywordCxt) { + const {gen, data, schema, parentSchema, it} = cxt + const {oneOf} = parentSchema + if (!it.opts.discriminator) { + throw new Error("discriminator: requires discriminator option") + } + const tagName = schema.propertyName + if (typeof tagName != "string") throw new Error("discriminator: requires propertyName") + if (schema.mapping) throw new Error("discriminator: mapping is not supported") + if (!oneOf) throw new Error("discriminator: requires oneOf keyword") + const valid = gen.let("valid", false) + const tag = gen.const("tag", _`${data}${getProperty(tagName)}`) + gen.if( + _`typeof ${tag} == "string"`, + () => validateMapping(), + () => cxt.error(false, {discrError: DiscrError.Tag, tag, tagName}) + ) + cxt.ok(valid) + + function validateMapping(): void { + const mapping = getMapping() + gen.if(false) + for (const tagValue in mapping) { + gen.elseIf(_`${tag} === ${tagValue}`) + gen.assign(valid, applyTagSchema(mapping[tagValue])) + } + gen.else() + cxt.error(false, {discrError: DiscrError.Mapping, tag, tagName}) + gen.endIf() + } + + function applyTagSchema(schemaProp?: number): Name { + const _valid = gen.name("valid") + const schCxt = cxt.subschema({keyword: "oneOf", schemaProp}, _valid) + cxt.mergeEvaluated(schCxt, Name) + return _valid + } + + function getMapping(): {[T in string]?: number} { + const oneOfMapping: {[T in string]?: number} = {} + const topRequired = hasRequired(parentSchema) + let tagRequired = true + for (let i = 0; i < oneOf.length; i++) { + let sch = oneOf[i] + if (sch?.$ref && !schemaHasRulesButRef(sch, it.self.RULES)) { + sch = resolveRef.call(it.self, it.schemaEnv.root, it.baseId, sch?.$ref) + if (sch instanceof SchemaEnv) sch = sch.schema + } + const propSch = sch?.properties?.[tagName] + if (typeof propSch != "object") { + throw new Error( + `discriminator: oneOf subschemas (or referenced schemas) must have "properties/${tagName}"` + ) + } + tagRequired = tagRequired && (topRequired || hasRequired(sch)) + addMappings(propSch, i) + } + if (!tagRequired) throw new Error(`discriminator: "${tagName}" must be required`) + return oneOfMapping + + function hasRequired({required}: AnySchemaObject): boolean { + return Array.isArray(required) && required.includes(tagName) + } + + function addMappings(sch: AnySchemaObject, i: number): void { + if (sch.const) { + addMapping(sch.const, i) + } else if (sch.enum) { + for (const tagValue of sch.enum) { + addMapping(tagValue, i) + } + } else { + throw new Error(`discriminator: "properties/${tagName}" must have "const" or "enum"`) + } + } + + function addMapping(tagValue: unknown, i: number): void { + if (typeof tagValue != "string" || tagValue in oneOfMapping) { + throw new Error(`discriminator: "${tagName}" values must be unique strings`) + } + oneOfMapping[tagValue] = i + } + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/discriminator/types.ts b/website/node_modules/ajv/lib/vocabularies/discriminator/types.ts new file mode 100644 index 00000000..bee5a278 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/discriminator/types.ts @@ -0,0 +1,12 @@ +import type {ErrorObject} from "../../types" + +export enum DiscrError { + Tag = "tag", + Mapping = "mapping", +} + +export type DiscrErrorObj = ErrorObject< + "discriminator", + {error: E; tag: string; tagValue: unknown}, + string +> diff --git a/website/node_modules/ajv/lib/vocabularies/draft2020.ts b/website/node_modules/ajv/lib/vocabularies/draft2020.ts new file mode 100644 index 00000000..47fbf0ee --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/draft2020.ts @@ -0,0 +1,23 @@ +import type {Vocabulary} from "../types" +import coreVocabulary from "./core" +import validationVocabulary from "./validation" +import getApplicatorVocabulary from "./applicator" +import dynamicVocabulary from "./dynamic" +import nextVocabulary from "./next" +import unevaluatedVocabulary from "./unevaluated" +import formatVocabulary from "./format" +import {metadataVocabulary, contentVocabulary} from "./metadata" + +const draft2020Vocabularies: Vocabulary[] = [ + dynamicVocabulary, + coreVocabulary, + validationVocabulary, + getApplicatorVocabulary(true), + formatVocabulary, + metadataVocabulary, + contentVocabulary, + nextVocabulary, + unevaluatedVocabulary, +] + +export default draft2020Vocabularies diff --git a/website/node_modules/ajv/lib/vocabularies/draft7.ts b/website/node_modules/ajv/lib/vocabularies/draft7.ts new file mode 100644 index 00000000..226a644a --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/draft7.ts @@ -0,0 +1,17 @@ +import type {Vocabulary} from "../types" +import coreVocabulary from "./core" +import validationVocabulary from "./validation" +import getApplicatorVocabulary from "./applicator" +import formatVocabulary from "./format" +import {metadataVocabulary, contentVocabulary} from "./metadata" + +const draft7Vocabularies: Vocabulary[] = [ + coreVocabulary, + validationVocabulary, + getApplicatorVocabulary(), + formatVocabulary, + metadataVocabulary, + contentVocabulary, +] + +export default draft7Vocabularies diff --git a/website/node_modules/ajv/lib/vocabularies/dynamic/dynamicAnchor.ts b/website/node_modules/ajv/lib/vocabularies/dynamic/dynamicAnchor.ts new file mode 100644 index 00000000..ca1adb91 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/dynamic/dynamicAnchor.ts @@ -0,0 +1,31 @@ +import type {CodeKeywordDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, getProperty, Code} from "../../compile/codegen" +import N from "../../compile/names" +import {SchemaEnv, compileSchema} from "../../compile" +import {getValidate} from "../core/ref" + +const def: CodeKeywordDefinition = { + keyword: "$dynamicAnchor", + schemaType: "string", + code: (cxt) => dynamicAnchor(cxt, cxt.schema), +} + +export function dynamicAnchor(cxt: KeywordCxt, anchor: string): void { + const {gen, it} = cxt + it.schemaEnv.root.dynamicAnchors[anchor] = true + const v = _`${N.dynamicAnchors}${getProperty(anchor)}` + const validate = it.errSchemaPath === "#" ? it.validateName : _getValidate(cxt) + gen.if(_`!${v}`, () => gen.assign(v, validate)) +} + +function _getValidate(cxt: KeywordCxt): Code { + const {schemaEnv, schema, self} = cxt.it + const {root, baseId, localRefs, meta} = schemaEnv.root + const {schemaId} = self.opts + const sch = new SchemaEnv({schema, schemaId, root, baseId, localRefs, meta}) + compileSchema.call(self, sch) + return getValidate(cxt, sch) +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/dynamic/dynamicRef.ts b/website/node_modules/ajv/lib/vocabularies/dynamic/dynamicRef.ts new file mode 100644 index 00000000..6a573f33 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/dynamic/dynamicRef.ts @@ -0,0 +1,51 @@ +import type {CodeKeywordDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, getProperty, Code, Name} from "../../compile/codegen" +import N from "../../compile/names" +import {callRef} from "../core/ref" + +const def: CodeKeywordDefinition = { + keyword: "$dynamicRef", + schemaType: "string", + code: (cxt) => dynamicRef(cxt, cxt.schema), +} + +export function dynamicRef(cxt: KeywordCxt, ref: string): void { + const {gen, keyword, it} = cxt + if (ref[0] !== "#") throw new Error(`"${keyword}" only supports hash fragment reference`) + const anchor = ref.slice(1) + if (it.allErrors) { + _dynamicRef() + } else { + const valid = gen.let("valid", false) + _dynamicRef(valid) + cxt.ok(valid) + } + + function _dynamicRef(valid?: Name): void { + // TODO the assumption here is that `recursiveRef: #` always points to the root + // of the schema object, which is not correct, because there may be $id that + // makes # point to it, and the target schema may not contain dynamic/recursiveAnchor. + // Because of that 2 tests in recursiveRef.json fail. + // This is a similar problem to #815 (`$id` doesn't alter resolution scope for `{ "$ref": "#" }`). + // (This problem is not tested in JSON-Schema-Test-Suite) + if (it.schemaEnv.root.dynamicAnchors[anchor]) { + const v = gen.let("_v", _`${N.dynamicAnchors}${getProperty(anchor)}`) + gen.if(v, _callRef(v, valid), _callRef(it.validateName, valid)) + } else { + _callRef(it.validateName, valid)() + } + } + + function _callRef(validate: Code, valid?: Name): () => void { + return valid + ? () => + gen.block(() => { + callRef(cxt, validate) + gen.let(valid, true) + }) + : () => callRef(cxt, validate) + } +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/dynamic/index.ts b/website/node_modules/ajv/lib/vocabularies/dynamic/index.ts new file mode 100644 index 00000000..6d521db6 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/dynamic/index.ts @@ -0,0 +1,9 @@ +import type {Vocabulary} from "../../types" +import dynamicAnchor from "./dynamicAnchor" +import dynamicRef from "./dynamicRef" +import recursiveAnchor from "./recursiveAnchor" +import recursiveRef from "./recursiveRef" + +const dynamic: Vocabulary = [dynamicAnchor, dynamicRef, recursiveAnchor, recursiveRef] + +export default dynamic diff --git a/website/node_modules/ajv/lib/vocabularies/dynamic/recursiveAnchor.ts b/website/node_modules/ajv/lib/vocabularies/dynamic/recursiveAnchor.ts new file mode 100644 index 00000000..25f3db96 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/dynamic/recursiveAnchor.ts @@ -0,0 +1,14 @@ +import type {CodeKeywordDefinition} from "../../types" +import {dynamicAnchor} from "./dynamicAnchor" +import {checkStrictMode} from "../../compile/util" + +const def: CodeKeywordDefinition = { + keyword: "$recursiveAnchor", + schemaType: "boolean", + code(cxt) { + if (cxt.schema) dynamicAnchor(cxt, "") + else checkStrictMode(cxt.it, "$recursiveAnchor: false is ignored") + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/dynamic/recursiveRef.ts b/website/node_modules/ajv/lib/vocabularies/dynamic/recursiveRef.ts new file mode 100644 index 00000000..c84af0f0 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/dynamic/recursiveRef.ts @@ -0,0 +1,10 @@ +import type {CodeKeywordDefinition} from "../../types" +import {dynamicRef} from "./dynamicRef" + +const def: CodeKeywordDefinition = { + keyword: "$recursiveRef", + schemaType: "string", + code: (cxt) => dynamicRef(cxt, cxt.schema), +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/errors.ts b/website/node_modules/ajv/lib/vocabularies/errors.ts new file mode 100644 index 00000000..c9ca3f02 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/errors.ts @@ -0,0 +1,18 @@ +import type {TypeError} from "../compile/validate/dataType" +import type {ApplicatorKeywordError} from "./applicator" +import type {ValidationKeywordError} from "./validation" +import type {FormatError} from "./format/format" +import type {UnevaluatedPropertiesError} from "./unevaluated/unevaluatedProperties" +import type {UnevaluatedItemsError} from "./unevaluated/unevaluatedItems" +import type {DependentRequiredError} from "./validation/dependentRequired" +import type {DiscriminatorError} from "./discriminator" + +export type DefinedError = + | TypeError + | ApplicatorKeywordError + | ValidationKeywordError + | FormatError + | UnevaluatedPropertiesError + | UnevaluatedItemsError + | DependentRequiredError + | DiscriminatorError diff --git a/website/node_modules/ajv/lib/vocabularies/format/format.ts b/website/node_modules/ajv/lib/vocabularies/format/format.ts new file mode 100644 index 00000000..4b1c13e7 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/format/format.ts @@ -0,0 +1,120 @@ +import type { + AddedFormat, + FormatValidator, + AsyncFormatValidator, + CodeKeywordDefinition, + KeywordErrorDefinition, + ErrorObject, +} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str, nil, or, Code, getProperty, regexpCode} from "../../compile/codegen" + +type FormatValidate = + | FormatValidator + | FormatValidator + | AsyncFormatValidator + | AsyncFormatValidator + | RegExp + | string + | true + +export type FormatError = ErrorObject<"format", {format: string}, string | {$data: string}> + +const error: KeywordErrorDefinition = { + message: ({schemaCode}) => str`must match format "${schemaCode}"`, + params: ({schemaCode}) => _`{format: ${schemaCode}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "format", + type: ["number", "string"], + schemaType: "string", + $data: true, + error, + code(cxt: KeywordCxt, ruleType?: string) { + const {gen, data, $data, schema, schemaCode, it} = cxt + const {opts, errSchemaPath, schemaEnv, self} = it + if (!opts.validateFormats) return + + if ($data) validate$DataFormat() + else validateFormat() + + function validate$DataFormat(): void { + const fmts = gen.scopeValue("formats", { + ref: self.formats, + code: opts.code.formats, + }) + const fDef = gen.const("fDef", _`${fmts}[${schemaCode}]`) + const fType = gen.let("fType") + const format = gen.let("format") + // TODO simplify + gen.if( + _`typeof ${fDef} == "object" && !(${fDef} instanceof RegExp)`, + () => gen.assign(fType, _`${fDef}.type || "string"`).assign(format, _`${fDef}.validate`), + () => gen.assign(fType, _`"string"`).assign(format, fDef) + ) + cxt.fail$data(or(unknownFmt(), invalidFmt())) + + function unknownFmt(): Code { + if (opts.strictSchema === false) return nil + return _`${schemaCode} && !${format}` + } + + function invalidFmt(): Code { + const callFormat = schemaEnv.$async + ? _`(${fDef}.async ? await ${format}(${data}) : ${format}(${data}))` + : _`${format}(${data})` + const validData = _`(typeof ${format} == "function" ? ${callFormat} : ${format}.test(${data}))` + return _`${format} && ${format} !== true && ${fType} === ${ruleType} && !${validData}` + } + } + + function validateFormat(): void { + const formatDef: AddedFormat | undefined = self.formats[schema] + if (!formatDef) { + unknownFormat() + return + } + if (formatDef === true) return + const [fmtType, format, fmtRef] = getFormat(formatDef) + if (fmtType === ruleType) cxt.pass(validCondition()) + + function unknownFormat(): void { + if (opts.strictSchema === false) { + self.logger.warn(unknownMsg()) + return + } + throw new Error(unknownMsg()) + + function unknownMsg(): string { + return `unknown format "${schema as string}" ignored in schema at path "${errSchemaPath}"` + } + } + + function getFormat(fmtDef: AddedFormat): [string, FormatValidate, Code] { + const code = + fmtDef instanceof RegExp + ? regexpCode(fmtDef) + : opts.code.formats + ? _`${opts.code.formats}${getProperty(schema)}` + : undefined + const fmt = gen.scopeValue("formats", {key: schema, ref: fmtDef, code}) + if (typeof fmtDef == "object" && !(fmtDef instanceof RegExp)) { + return [fmtDef.type || "string", fmtDef.validate, _`${fmt}.validate`] + } + + return ["string", fmtDef, fmt] + } + + function validCondition(): Code { + if (typeof formatDef == "object" && !(formatDef instanceof RegExp) && formatDef.async) { + if (!schemaEnv.$async) throw new Error("async format in sync schema") + return _`await ${fmtRef}(${data})` + } + return typeof format == "function" ? _`${fmtRef}(${data})` : _`${fmtRef}.test(${data})` + } + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/format/index.ts b/website/node_modules/ajv/lib/vocabularies/format/index.ts new file mode 100644 index 00000000..bca2f5b3 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/format/index.ts @@ -0,0 +1,6 @@ +import type {Vocabulary} from "../../types" +import formatKeyword from "./format" + +const format: Vocabulary = [formatKeyword] + +export default format diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/discriminator.ts b/website/node_modules/ajv/lib/vocabularies/jtd/discriminator.ts new file mode 100644 index 00000000..f487c97f --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/discriminator.ts @@ -0,0 +1,89 @@ +import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, not, getProperty, Name} from "../../compile/codegen" +import {checkMetadata} from "./metadata" +import {checkNullableObject} from "./nullable" +import {typeErrorMessage, typeErrorParams, _JTDTypeError} from "./error" +import {DiscrError, DiscrErrorObj} from "../discriminator/types" + +export type JTDDiscriminatorError = + | _JTDTypeError<"discriminator", "object", string> + | DiscrErrorObj + | DiscrErrorObj + +const error: KeywordErrorDefinition = { + message: (cxt) => { + const {schema, params} = cxt + return params.discrError + ? params.discrError === DiscrError.Tag + ? `tag "${schema}" must be string` + : `value of tag "${schema}" must be in mapping` + : typeErrorMessage(cxt, "object") + }, + params: (cxt) => { + const {schema, params} = cxt + return params.discrError + ? _`{error: ${params.discrError}, tag: ${schema}, tagValue: ${params.tag}}` + : typeErrorParams(cxt, "object") + }, +} + +const def: CodeKeywordDefinition = { + keyword: "discriminator", + schemaType: "string", + implements: ["mapping"], + error, + code(cxt: KeywordCxt) { + checkMetadata(cxt) + const {gen, data, schema, parentSchema} = cxt + const [valid, cond] = checkNullableObject(cxt, data) + + gen.if(cond) + validateDiscriminator() + gen.elseIf(not(valid)) + cxt.error() + gen.endIf() + cxt.ok(valid) + + function validateDiscriminator(): void { + const tag = gen.const("tag", _`${data}${getProperty(schema)}`) + gen.if(_`${tag} === undefined`) + cxt.error(false, {discrError: DiscrError.Tag, tag}) + gen.elseIf(_`typeof ${tag} == "string"`) + validateMapping(tag) + gen.else() + cxt.error(false, {discrError: DiscrError.Tag, tag}, {instancePath: schema}) + gen.endIf() + } + + function validateMapping(tag: Name): void { + gen.if(false) + for (const tagValue in parentSchema.mapping) { + gen.elseIf(_`${tag} === ${tagValue}`) + gen.assign(valid, applyTagSchema(tagValue)) + } + gen.else() + cxt.error( + false, + {discrError: DiscrError.Mapping, tag}, + {instancePath: schema, schemaPath: "mapping", parentSchema: true} + ) + gen.endIf() + } + + function applyTagSchema(schemaProp: string): Name { + const _valid = gen.name("valid") + cxt.subschema( + { + keyword: "mapping", + schemaProp, + jtdDiscriminator: schema, + }, + _valid + ) + return _valid + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/elements.ts b/website/node_modules/ajv/lib/vocabularies/jtd/elements.ts new file mode 100644 index 00000000..983af7c0 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/elements.ts @@ -0,0 +1,32 @@ +import type {CodeKeywordDefinition, SchemaObject} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {alwaysValidSchema} from "../../compile/util" +import {validateArray} from "../code" +import {_, not} from "../../compile/codegen" +import {checkMetadata} from "./metadata" +import {checkNullable} from "./nullable" +import {typeError, _JTDTypeError} from "./error" + +export type JTDElementsError = _JTDTypeError<"elements", "array", SchemaObject> + +const def: CodeKeywordDefinition = { + keyword: "elements", + schemaType: "object", + error: typeError("array"), + code(cxt: KeywordCxt) { + checkMetadata(cxt) + const {gen, data, schema, it} = cxt + if (alwaysValidSchema(it, schema)) return + const [valid] = checkNullable(cxt) + gen.if(not(valid), () => + gen.if( + _`Array.isArray(${data})`, + () => gen.assign(valid, validateArray(cxt)), + () => cxt.error() + ) + ) + cxt.ok(valid) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/enum.ts b/website/node_modules/ajv/lib/vocabularies/jtd/enum.ts new file mode 100644 index 00000000..75464ff8 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/enum.ts @@ -0,0 +1,45 @@ +import type {CodeKeywordDefinition, KeywordErrorDefinition, ErrorObject} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, or, and, Code} from "../../compile/codegen" +import {checkMetadata} from "./metadata" +import {checkNullable} from "./nullable" + +export type JTDEnumError = ErrorObject<"enum", {allowedValues: string[]}, string[]> + +const error: KeywordErrorDefinition = { + message: "must be equal to one of the allowed values", + params: ({schemaCode}) => _`{allowedValues: ${schemaCode}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "enum", + schemaType: "array", + error, + code(cxt: KeywordCxt) { + checkMetadata(cxt) + const {gen, data, schema, schemaValue, parentSchema, it} = cxt + if (schema.length === 0) throw new Error("enum must have non-empty array") + if (schema.length !== new Set(schema).size) throw new Error("enum items must be unique") + let valid: Code + const isString = _`typeof ${data} == "string"` + if (schema.length >= it.opts.loopEnum) { + let cond: Code + ;[valid, cond] = checkNullable(cxt, isString) + gen.if(cond, loopEnum) + } else { + /* istanbul ignore if */ + if (!Array.isArray(schema)) throw new Error("ajv implementation error") + valid = and(isString, or(...schema.map((value: string) => _`${data} === ${value}`))) + if (parentSchema.nullable) valid = or(_`${data} === null`, valid) + } + cxt.pass(valid) + + function loopEnum(): void { + gen.forOf("v", schemaValue as Code, (v) => + gen.if(_`${valid} = ${data} === ${v}`, () => gen.break()) + ) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/error.ts b/website/node_modules/ajv/lib/vocabularies/jtd/error.ts new file mode 100644 index 00000000..50693225 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/error.ts @@ -0,0 +1,23 @@ +import type {KeywordErrorDefinition, KeywordErrorCxt, ErrorObject} from "../../types" +import {_, Code} from "../../compile/codegen" + +export type _JTDTypeError = ErrorObject< + K, + {type: T; nullable: boolean}, + S +> + +export function typeError(t: string): KeywordErrorDefinition { + return { + message: (cxt) => typeErrorMessage(cxt, t), + params: (cxt) => typeErrorParams(cxt, t), + } +} + +export function typeErrorMessage({parentSchema}: KeywordErrorCxt, t: string): string { + return parentSchema?.nullable ? `must be ${t} or null` : `must be ${t}` +} + +export function typeErrorParams({parentSchema}: KeywordErrorCxt, t: string): Code { + return _`{type: ${t}, nullable: ${!!parentSchema?.nullable}}` +} diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/index.ts b/website/node_modules/ajv/lib/vocabularies/jtd/index.ts new file mode 100644 index 00000000..f7baebc3 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/index.ts @@ -0,0 +1,37 @@ +import type {Vocabulary} from "../../types" +import refKeyword from "./ref" +import typeKeyword, {JTDTypeError} from "./type" +import enumKeyword, {JTDEnumError} from "./enum" +import elements, {JTDElementsError} from "./elements" +import properties, {JTDPropertiesError} from "./properties" +import optionalProperties from "./optionalProperties" +import discriminator, {JTDDiscriminatorError} from "./discriminator" +import values, {JTDValuesError} from "./values" +import union from "./union" +import metadata from "./metadata" + +const jtdVocabulary: Vocabulary = [ + "definitions", + refKeyword, + typeKeyword, + enumKeyword, + elements, + properties, + optionalProperties, + discriminator, + values, + union, + metadata, + {keyword: "additionalProperties", schemaType: "boolean"}, + {keyword: "nullable", schemaType: "boolean"}, +] + +export default jtdVocabulary + +export type JTDErrorObject = + | JTDTypeError + | JTDEnumError + | JTDElementsError + | JTDPropertiesError + | JTDDiscriminatorError + | JTDValuesError diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/metadata.ts b/website/node_modules/ajv/lib/vocabularies/jtd/metadata.ts new file mode 100644 index 00000000..19eeb8c7 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/metadata.ts @@ -0,0 +1,24 @@ +import {KeywordCxt} from "../../ajv" +import type {CodeKeywordDefinition} from "../../types" +import {alwaysValidSchema} from "../../compile/util" + +const def: CodeKeywordDefinition = { + keyword: "metadata", + schemaType: "object", + code(cxt: KeywordCxt) { + checkMetadata(cxt) + const {gen, schema, it} = cxt + if (alwaysValidSchema(it, schema)) return + const valid = gen.name("valid") + cxt.subschema({keyword: "metadata", jtdMetadata: true}, valid) + cxt.ok(valid) + }, +} + +export function checkMetadata({it, keyword}: KeywordCxt, metadata?: boolean): void { + if (it.jtdMetadata !== metadata) { + throw new Error(`JTD: "${keyword}" cannot be used in this schema location`) + } +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/nullable.ts b/website/node_modules/ajv/lib/vocabularies/jtd/nullable.ts new file mode 100644 index 00000000..c74b05da --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/nullable.ts @@ -0,0 +1,21 @@ +import type {KeywordCxt} from "../../compile/validate" +import {_, not, nil, Code, Name} from "../../compile/codegen" + +export function checkNullable( + {gen, data, parentSchema}: KeywordCxt, + cond: Code = nil +): [Name, Code] { + const valid = gen.name("valid") + if (parentSchema.nullable) { + gen.let(valid, _`${data} === null`) + cond = not(valid) + } else { + gen.let(valid, false) + } + return [valid, cond] +} + +export function checkNullableObject(cxt: KeywordCxt, cond: Code): [Name, Code] { + const [valid, cond_] = checkNullable(cxt, cond) + return [valid, _`${cond_} && typeof ${cxt.data} == "object" && !Array.isArray(${cxt.data})`] +} diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/optionalProperties.ts b/website/node_modules/ajv/lib/vocabularies/jtd/optionalProperties.ts new file mode 100644 index 00000000..8e91c8d9 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/optionalProperties.ts @@ -0,0 +1,15 @@ +import type {CodeKeywordDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {validateProperties, error} from "./properties" + +const def: CodeKeywordDefinition = { + keyword: "optionalProperties", + schemaType: "object", + error, + code(cxt: KeywordCxt) { + if (cxt.parentSchema.properties) return + validateProperties(cxt) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/properties.ts b/website/node_modules/ajv/lib/vocabularies/jtd/properties.ts new file mode 100644 index 00000000..9dd24c5c --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/properties.ts @@ -0,0 +1,184 @@ +import type { + CodeKeywordDefinition, + ErrorObject, + KeywordErrorDefinition, + SchemaObject, +} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {propertyInData, allSchemaProperties, isOwnProperty} from "../code" +import {alwaysValidSchema, schemaRefOrVal} from "../../compile/util" +import {_, and, not, Code, Name} from "../../compile/codegen" +import {checkMetadata} from "./metadata" +import {checkNullableObject} from "./nullable" +import {typeErrorMessage, typeErrorParams, _JTDTypeError} from "./error" + +enum PropError { + Additional = "additional", + Missing = "missing", +} + +type PropKeyword = "properties" | "optionalProperties" + +type PropSchema = {[P in string]?: SchemaObject} + +export type JTDPropertiesError = + | _JTDTypeError + | ErrorObject + | ErrorObject + +export const error: KeywordErrorDefinition = { + message: (cxt) => { + const {params} = cxt + return params.propError + ? params.propError === PropError.Additional + ? "must NOT have additional properties" + : `must have property '${params.missingProperty}'` + : typeErrorMessage(cxt, "object") + }, + params: (cxt) => { + const {params} = cxt + return params.propError + ? params.propError === PropError.Additional + ? _`{error: ${params.propError}, additionalProperty: ${params.additionalProperty}}` + : _`{error: ${params.propError}, missingProperty: ${params.missingProperty}}` + : typeErrorParams(cxt, "object") + }, +} + +const def: CodeKeywordDefinition = { + keyword: "properties", + schemaType: "object", + error, + code: validateProperties, +} + +// const error: KeywordErrorDefinition = { +// message: "should NOT have additional properties", +// params: ({params}) => _`{additionalProperty: ${params.additionalProperty}}`, +// } + +export function validateProperties(cxt: KeywordCxt): void { + checkMetadata(cxt) + const {gen, data, parentSchema, it} = cxt + const {additionalProperties, nullable} = parentSchema + if (it.jtdDiscriminator && nullable) throw new Error("JTD: nullable inside discriminator mapping") + if (commonProperties()) { + throw new Error("JTD: properties and optionalProperties have common members") + } + const [allProps, properties] = schemaProperties("properties") + const [allOptProps, optProperties] = schemaProperties("optionalProperties") + if (properties.length === 0 && optProperties.length === 0 && additionalProperties) { + return + } + + const [valid, cond] = + it.jtdDiscriminator === undefined + ? checkNullableObject(cxt, data) + : [gen.let("valid", false), true] + gen.if(cond, () => + gen.assign(valid, true).block(() => { + validateProps(properties, "properties", true) + validateProps(optProperties, "optionalProperties") + if (!additionalProperties) validateAdditional() + }) + ) + cxt.pass(valid) + + function commonProperties(): boolean { + const props = parentSchema.properties as Record | undefined + const optProps = parentSchema.optionalProperties as Record | undefined + if (!(props && optProps)) return false + for (const p in props) { + if (Object.prototype.hasOwnProperty.call(optProps, p)) return true + } + return false + } + + function schemaProperties(keyword: string): [string[], string[]] { + const schema = parentSchema[keyword] + const allPs = schema ? allSchemaProperties(schema) : [] + if (it.jtdDiscriminator && allPs.some((p) => p === it.jtdDiscriminator)) { + throw new Error(`JTD: discriminator tag used in ${keyword}`) + } + const ps = allPs.filter((p) => !alwaysValidSchema(it, schema[p])) + return [allPs, ps] + } + + function validateProps(props: string[], keyword: string, required?: boolean): void { + const _valid = gen.var("valid") + for (const prop of props) { + gen.if( + propertyInData(gen, data, prop, it.opts.ownProperties), + () => applyPropertySchema(prop, keyword, _valid), + () => missingProperty(prop) + ) + cxt.ok(_valid) + } + + function missingProperty(prop: string): void { + if (required) { + gen.assign(_valid, false) + cxt.error(false, {propError: PropError.Missing, missingProperty: prop}, {schemaPath: prop}) + } else { + gen.assign(_valid, true) + } + } + } + + function applyPropertySchema(prop: string, keyword: string, _valid: Name): void { + cxt.subschema( + { + keyword, + schemaProp: prop, + dataProp: prop, + }, + _valid + ) + } + + function validateAdditional(): void { + gen.forIn("key", data, (key: Name) => { + const addProp = isAdditional(key, allProps, "properties", it.jtdDiscriminator) + const addOptProp = isAdditional(key, allOptProps, "optionalProperties") + const extra = + addProp === true ? addOptProp : addOptProp === true ? addProp : and(addProp, addOptProp) + gen.if(extra, () => { + if (it.opts.removeAdditional) { + gen.code(_`delete ${data}[${key}]`) + } else { + cxt.error( + false, + {propError: PropError.Additional, additionalProperty: key}, + {instancePath: key, parentSchema: true} + ) + if (!it.opts.allErrors) gen.break() + } + }) + }) + } + + function isAdditional( + key: Name, + props: string[], + keyword: string, + jtdDiscriminator?: string + ): Code | true { + let additional: Code | boolean + if (props.length > 8) { + // TODO maybe an option instead of hard-coded 8? + const propsSchema = schemaRefOrVal(it, parentSchema[keyword], keyword) + additional = not(isOwnProperty(gen, propsSchema as Code, key)) + if (jtdDiscriminator !== undefined) { + additional = and(additional, _`${key} !== ${jtdDiscriminator}`) + } + } else if (props.length || jtdDiscriminator !== undefined) { + const ps = jtdDiscriminator === undefined ? props : [jtdDiscriminator].concat(props) + additional = and(...ps.map((p) => _`${key} !== ${p}`)) + } else { + additional = true + } + return additional + } +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/ref.ts b/website/node_modules/ajv/lib/vocabularies/jtd/ref.ts new file mode 100644 index 00000000..97646ee1 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/ref.ts @@ -0,0 +1,76 @@ +import type {CodeKeywordDefinition, AnySchemaObject} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {compileSchema, SchemaEnv} from "../../compile" +import {_, not, nil, stringify} from "../../compile/codegen" +import MissingRefError from "../../compile/ref_error" +import N from "../../compile/names" +import {getValidate, callRef} from "../core/ref" +import {checkMetadata} from "./metadata" + +const def: CodeKeywordDefinition = { + keyword: "ref", + schemaType: "string", + code(cxt: KeywordCxt) { + checkMetadata(cxt) + const {gen, data, schema: ref, parentSchema, it} = cxt + const { + schemaEnv: {root}, + } = it + const valid = gen.name("valid") + if (parentSchema.nullable) { + gen.var(valid, _`${data} === null`) + gen.if(not(valid), validateJtdRef) + } else { + gen.var(valid, false) + validateJtdRef() + } + cxt.ok(valid) + + function validateJtdRef(): void { + const refSchema = (root.schema as AnySchemaObject).definitions?.[ref] + if (!refSchema) { + throw new MissingRefError(it.opts.uriResolver, "", ref, `No definition ${ref}`) + } + if (hasRef(refSchema) || !it.opts.inlineRefs) callValidate(refSchema) + else inlineRefSchema(refSchema) + } + + function callValidate(schema: AnySchemaObject): void { + const sch = compileSchema.call( + it.self, + new SchemaEnv({schema, root, schemaPath: `/definitions/${ref}`}) + ) + const v = getValidate(cxt, sch) + const errsCount = gen.const("_errs", N.errors) + callRef(cxt, v, sch, sch.$async) + gen.assign(valid, _`${errsCount} === ${N.errors}`) + } + + function inlineRefSchema(schema: AnySchemaObject): void { + const schName = gen.scopeValue( + "schema", + it.opts.code.source === true ? {ref: schema, code: stringify(schema)} : {ref: schema} + ) + cxt.subschema( + { + schema, + dataTypes: [], + schemaPath: nil, + topSchemaRef: schName, + errSchemaPath: `/definitions/${ref}`, + }, + valid + ) + } + }, +} + +export function hasRef(schema: AnySchemaObject): boolean { + for (const key in schema) { + let sch: AnySchemaObject + if (key === "ref" || (typeof (sch = schema[key]) == "object" && hasRef(sch))) return true + } + return false +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/type.ts b/website/node_modules/ajv/lib/vocabularies/jtd/type.ts new file mode 100644 index 00000000..17274300 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/type.ts @@ -0,0 +1,75 @@ +import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, nil, or, Code} from "../../compile/codegen" +import validTimestamp from "../../runtime/timestamp" +import {useFunc} from "../../compile/util" +import {checkMetadata} from "./metadata" +import {typeErrorMessage, typeErrorParams, _JTDTypeError} from "./error" + +export type JTDTypeError = _JTDTypeError<"type", JTDType, JTDType> + +export type IntType = "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" + +export const intRange: {[T in IntType]: [number, number, number]} = { + int8: [-128, 127, 3], + uint8: [0, 255, 3], + int16: [-32768, 32767, 5], + uint16: [0, 65535, 5], + int32: [-2147483648, 2147483647, 10], + uint32: [0, 4294967295, 10], +} + +export type JTDType = "boolean" | "string" | "timestamp" | "float32" | "float64" | IntType + +const error: KeywordErrorDefinition = { + message: (cxt) => typeErrorMessage(cxt, cxt.schema), + params: (cxt) => typeErrorParams(cxt, cxt.schema), +} + +function timestampCode(cxt: KeywordCxt): Code { + const {gen, data, it} = cxt + const {timestamp, allowDate} = it.opts + if (timestamp === "date") return _`${data} instanceof Date ` + const vts = useFunc(gen, validTimestamp) + const allowDateArg = allowDate ? _`, true` : nil + const validString = _`typeof ${data} == "string" && ${vts}(${data}${allowDateArg})` + return timestamp === "string" ? validString : or(_`${data} instanceof Date`, validString) +} + +const def: CodeKeywordDefinition = { + keyword: "type", + schemaType: "string", + error, + code(cxt: KeywordCxt) { + checkMetadata(cxt) + const {data, schema, parentSchema, it} = cxt + let cond: Code + switch (schema) { + case "boolean": + case "string": + cond = _`typeof ${data} == ${schema}` + break + case "timestamp": { + cond = timestampCode(cxt) + break + } + case "float32": + case "float64": + cond = _`typeof ${data} == "number"` + break + default: { + const sch = schema as IntType + cond = _`typeof ${data} == "number" && isFinite(${data}) && !(${data} % 1)` + if (!it.opts.int32range && (sch === "int32" || sch === "uint32")) { + if (sch === "uint32") cond = _`${cond} && ${data} >= 0` + } else { + const [min, max] = intRange[sch] + cond = _`${cond} && ${data} >= ${min} && ${data} <= ${max}` + } + } + } + cxt.pass(parentSchema.nullable ? or(_`${data} === null`, cond) : cond) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/union.ts b/website/node_modules/ajv/lib/vocabularies/jtd/union.ts new file mode 100644 index 00000000..588f07ab --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/union.ts @@ -0,0 +1,12 @@ +import type {CodeKeywordDefinition} from "../../types" +import {validateUnion} from "../code" + +const def: CodeKeywordDefinition = { + keyword: "union", + schemaType: "array", + trackErrors: true, + code: validateUnion, + error: {message: "must match a schema in union"}, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/jtd/values.ts b/website/node_modules/ajv/lib/vocabularies/jtd/values.ts new file mode 100644 index 00000000..e6494507 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/jtd/values.ts @@ -0,0 +1,58 @@ +import type {CodeKeywordDefinition, SchemaObject} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {alwaysValidSchema, Type} from "../../compile/util" +import {not, or, Name} from "../../compile/codegen" +import {checkMetadata} from "./metadata" +import {checkNullableObject} from "./nullable" +import {typeError, _JTDTypeError} from "./error" + +export type JTDValuesError = _JTDTypeError<"values", "object", SchemaObject> + +const def: CodeKeywordDefinition = { + keyword: "values", + schemaType: "object", + error: typeError("object"), + code(cxt: KeywordCxt) { + checkMetadata(cxt) + const {gen, data, schema, it} = cxt + const [valid, cond] = checkNullableObject(cxt, data) + if (alwaysValidSchema(it, schema)) { + gen.if(not(or(cond, valid)), () => cxt.error()) + } else { + gen.if(cond) + gen.assign(valid, validateMap()) + gen.elseIf(not(valid)) + cxt.error() + gen.endIf() + } + cxt.ok(valid) + + function validateMap(): Name | boolean { + const _valid = gen.name("valid") + if (it.allErrors) { + const validMap = gen.let("valid", true) + validateValues(() => gen.assign(validMap, false)) + return validMap + } + gen.var(_valid, true) + validateValues(() => gen.break()) + return _valid + + function validateValues(notValid: () => void): void { + gen.forIn("key", data, (key) => { + cxt.subschema( + { + keyword: "values", + dataProp: key, + dataPropType: Type.Str, + }, + _valid + ) + gen.if(not(_valid), notValid) + }) + } + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/metadata.ts b/website/node_modules/ajv/lib/vocabularies/metadata.ts new file mode 100644 index 00000000..b9d5af85 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/metadata.ts @@ -0,0 +1,17 @@ +import type {Vocabulary} from "../types" + +export const metadataVocabulary: Vocabulary = [ + "title", + "description", + "default", + "deprecated", + "readOnly", + "writeOnly", + "examples", +] + +export const contentVocabulary: Vocabulary = [ + "contentMediaType", + "contentEncoding", + "contentSchema", +] diff --git a/website/node_modules/ajv/lib/vocabularies/next.ts b/website/node_modules/ajv/lib/vocabularies/next.ts new file mode 100644 index 00000000..1e987ad2 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/next.ts @@ -0,0 +1,8 @@ +import type {Vocabulary} from "../types" +import dependentRequired from "./validation/dependentRequired" +import dependentSchemas from "./applicator/dependentSchemas" +import limitContains from "./validation/limitContains" + +const next: Vocabulary = [dependentRequired, dependentSchemas, limitContains] + +export default next diff --git a/website/node_modules/ajv/lib/vocabularies/unevaluated/index.ts b/website/node_modules/ajv/lib/vocabularies/unevaluated/index.ts new file mode 100644 index 00000000..f7f0815d --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/unevaluated/index.ts @@ -0,0 +1,7 @@ +import type {Vocabulary} from "../../types" +import unevaluatedProperties from "./unevaluatedProperties" +import unevaluatedItems from "./unevaluatedItems" + +const unevaluated: Vocabulary = [unevaluatedProperties, unevaluatedItems] + +export default unevaluated diff --git a/website/node_modules/ajv/lib/vocabularies/unevaluated/unevaluatedItems.ts b/website/node_modules/ajv/lib/vocabularies/unevaluated/unevaluatedItems.ts new file mode 100644 index 00000000..50bf0e7c --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/unevaluated/unevaluatedItems.ts @@ -0,0 +1,47 @@ +import type { + CodeKeywordDefinition, + ErrorObject, + KeywordErrorDefinition, + AnySchema, +} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str, not, Name} from "../../compile/codegen" +import {alwaysValidSchema, Type} from "../../compile/util" + +export type UnevaluatedItemsError = ErrorObject<"unevaluatedItems", {limit: number}, AnySchema> + +const error: KeywordErrorDefinition = { + message: ({params: {len}}) => str`must NOT have more than ${len} items`, + params: ({params: {len}}) => _`{limit: ${len}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "unevaluatedItems", + type: "array", + schemaType: ["boolean", "object"], + error, + code(cxt: KeywordCxt) { + const {gen, schema, data, it} = cxt + const items = it.items || 0 + if (items === true) return + const len = gen.const("len", _`${data}.length`) + if (schema === false) { + cxt.setParams({len: items}) + cxt.fail(_`${len} > ${items}`) + } else if (typeof schema == "object" && !alwaysValidSchema(it, schema)) { + const valid = gen.var("valid", _`${len} <= ${items}`) + gen.if(not(valid), () => validateItems(valid, items)) + cxt.ok(valid) + } + it.items = true + + function validateItems(valid: Name, from: Name | number): void { + gen.forRange("i", from, len, (i) => { + cxt.subschema({keyword: "unevaluatedItems", dataProp: i, dataPropType: Type.Num}, valid) + if (!it.allErrors) gen.if(not(valid), () => gen.break()) + }) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/unevaluated/unevaluatedProperties.ts b/website/node_modules/ajv/lib/vocabularies/unevaluated/unevaluatedProperties.ts new file mode 100644 index 00000000..0e6868fa --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/unevaluated/unevaluatedProperties.ts @@ -0,0 +1,85 @@ +import type { + CodeKeywordDefinition, + KeywordErrorDefinition, + ErrorObject, + AnySchema, +} from "../../types" +import {_, not, and, Name, Code} from "../../compile/codegen" +import {alwaysValidSchema, Type} from "../../compile/util" +import N from "../../compile/names" + +export type UnevaluatedPropertiesError = ErrorObject< + "unevaluatedProperties", + {unevaluatedProperty: string}, + AnySchema +> + +const error: KeywordErrorDefinition = { + message: "must NOT have unevaluated properties", + params: ({params}) => _`{unevaluatedProperty: ${params.unevaluatedProperty}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "unevaluatedProperties", + type: "object", + schemaType: ["boolean", "object"], + trackErrors: true, + error, + code(cxt) { + const {gen, schema, data, errsCount, it} = cxt + /* istanbul ignore if */ + if (!errsCount) throw new Error("ajv implementation error") + const {allErrors, props} = it + if (props instanceof Name) { + gen.if(_`${props} !== true`, () => + gen.forIn("key", data, (key: Name) => + gen.if(unevaluatedDynamic(props, key), () => unevaluatedPropCode(key)) + ) + ) + } else if (props !== true) { + gen.forIn("key", data, (key: Name) => + props === undefined + ? unevaluatedPropCode(key) + : gen.if(unevaluatedStatic(props, key), () => unevaluatedPropCode(key)) + ) + } + it.props = true + cxt.ok(_`${errsCount} === ${N.errors}`) + + function unevaluatedPropCode(key: Name): void { + if (schema === false) { + cxt.setParams({unevaluatedProperty: key}) + cxt.error() + if (!allErrors) gen.break() + return + } + + if (!alwaysValidSchema(it, schema)) { + const valid = gen.name("valid") + cxt.subschema( + { + keyword: "unevaluatedProperties", + dataProp: key, + dataPropType: Type.Str, + }, + valid + ) + if (!allErrors) gen.if(not(valid), () => gen.break()) + } + } + + function unevaluatedDynamic(evaluatedProps: Name, key: Name): Code { + return _`!${evaluatedProps} || !${evaluatedProps}[${key}]` + } + + function unevaluatedStatic(evaluatedProps: {[K in string]?: true}, key: Name): Code { + const ps: Code[] = [] + for (const p in evaluatedProps) { + if (evaluatedProps[p] === true) ps.push(_`${key} !== ${p}`) + } + return and(...ps) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/const.ts b/website/node_modules/ajv/lib/vocabularies/validation/const.ts new file mode 100644 index 00000000..a3b94a5d --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/const.ts @@ -0,0 +1,28 @@ +import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_} from "../../compile/codegen" +import {useFunc} from "../../compile/util" +import equal from "../../runtime/equal" + +export type ConstError = ErrorObject<"const", {allowedValue: any}> + +const error: KeywordErrorDefinition = { + message: "must be equal to constant", + params: ({schemaCode}) => _`{allowedValue: ${schemaCode}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "const", + $data: true, + error, + code(cxt: KeywordCxt) { + const {gen, data, $data, schemaCode, schema} = cxt + if ($data || (schema && typeof schema == "object")) { + cxt.fail$data(_`!${useFunc(gen, equal)}(${data}, ${schemaCode})`) + } else { + cxt.fail(_`${schema} !== ${data}`) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/dependentRequired.ts b/website/node_modules/ajv/lib/vocabularies/validation/dependentRequired.ts new file mode 100644 index 00000000..4c616cfa --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/dependentRequired.ts @@ -0,0 +1,23 @@ +import type {CodeKeywordDefinition, ErrorObject} from "../../types" +import { + validatePropertyDeps, + error, + DependenciesErrorParams, + PropertyDependencies, +} from "../applicator/dependencies" + +export type DependentRequiredError = ErrorObject< + "dependentRequired", + DependenciesErrorParams, + PropertyDependencies +> + +const def: CodeKeywordDefinition = { + keyword: "dependentRequired", + type: "object", + schemaType: "object", + error, + code: (cxt) => validatePropertyDeps(cxt), +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/enum.ts b/website/node_modules/ajv/lib/vocabularies/validation/enum.ts new file mode 100644 index 00000000..76377fb0 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/enum.ts @@ -0,0 +1,54 @@ +import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, or, Name, Code} from "../../compile/codegen" +import {useFunc} from "../../compile/util" +import equal from "../../runtime/equal" + +export type EnumError = ErrorObject<"enum", {allowedValues: any[]}, any[] | {$data: string}> + +const error: KeywordErrorDefinition = { + message: "must be equal to one of the allowed values", + params: ({schemaCode}) => _`{allowedValues: ${schemaCode}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "enum", + schemaType: "array", + $data: true, + error, + code(cxt: KeywordCxt) { + const {gen, data, $data, schema, schemaCode, it} = cxt + if (!$data && schema.length === 0) throw new Error("enum must have non-empty array") + const useLoop = schema.length >= it.opts.loopEnum + let eql: Name | undefined + const getEql = (): Name => (eql ??= useFunc(gen, equal)) + + let valid: Code + if (useLoop || $data) { + valid = gen.let("valid") + cxt.block$data(valid, loopEnum) + } else { + /* istanbul ignore if */ + if (!Array.isArray(schema)) throw new Error("ajv implementation error") + const vSchema = gen.const("vSchema", schemaCode) + valid = or(...schema.map((_x: unknown, i: number) => equalCode(vSchema, i))) + } + cxt.pass(valid) + + function loopEnum(): void { + gen.assign(valid, false) + gen.forOf("v", schemaCode as Code, (v) => + gen.if(_`${getEql()}(${data}, ${v})`, () => gen.assign(valid, true).break()) + ) + } + + function equalCode(vSchema: Name, i: number): Code { + const sch = schema[i] + return typeof sch === "object" && sch !== null + ? _`${getEql()}(${data}, ${vSchema}[${i}])` + : _`${data} === ${sch}` + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/index.ts b/website/node_modules/ajv/lib/vocabularies/validation/index.ts new file mode 100644 index 00000000..3531b196 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/index.ts @@ -0,0 +1,49 @@ +import type {ErrorObject, Vocabulary} from "../../types" +import limitNumber, {LimitNumberError} from "./limitNumber" +import multipleOf, {MultipleOfError} from "./multipleOf" +import limitLength from "./limitLength" +import pattern, {PatternError} from "./pattern" +import limitProperties from "./limitProperties" +import required, {RequiredError} from "./required" +import limitItems from "./limitItems" +import uniqueItems, {UniqueItemsError} from "./uniqueItems" +import constKeyword, {ConstError} from "./const" +import enumKeyword, {EnumError} from "./enum" + +const validation: Vocabulary = [ + // number + limitNumber, + multipleOf, + // string + limitLength, + pattern, + // object + limitProperties, + required, + // array + limitItems, + uniqueItems, + // any + {keyword: "type", schemaType: ["string", "array"]}, + {keyword: "nullable", schemaType: "boolean"}, + constKeyword, + enumKeyword, +] + +export default validation + +type LimitError = ErrorObject< + "maxItems" | "minItems" | "minProperties" | "maxProperties" | "minLength" | "maxLength", + {limit: number}, + number | {$data: string} +> + +export type ValidationKeywordError = + | LimitError + | LimitNumberError + | MultipleOfError + | PatternError + | RequiredError + | UniqueItemsError + | ConstError + | EnumError diff --git a/website/node_modules/ajv/lib/vocabularies/validation/limitContains.ts b/website/node_modules/ajv/lib/vocabularies/validation/limitContains.ts new file mode 100644 index 00000000..8bb43c1a --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/limitContains.ts @@ -0,0 +1,16 @@ +import type {CodeKeywordDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {checkStrictMode} from "../../compile/util" + +const def: CodeKeywordDefinition = { + keyword: ["maxContains", "minContains"], + type: "array", + schemaType: "number", + code({keyword, parentSchema, it}: KeywordCxt) { + if (parentSchema.contains === undefined) { + checkStrictMode(it, `"${keyword}" without "contains" is ignored`) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/limitItems.ts b/website/node_modules/ajv/lib/vocabularies/validation/limitItems.ts new file mode 100644 index 00000000..566de858 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/limitItems.ts @@ -0,0 +1,26 @@ +import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str, operators} from "../../compile/codegen" + +const error: KeywordErrorDefinition = { + message({keyword, schemaCode}) { + const comp = keyword === "maxItems" ? "more" : "fewer" + return str`must NOT have ${comp} than ${schemaCode} items` + }, + params: ({schemaCode}) => _`{limit: ${schemaCode}}`, +} + +const def: CodeKeywordDefinition = { + keyword: ["maxItems", "minItems"], + type: "array", + schemaType: "number", + $data: true, + error, + code(cxt: KeywordCxt) { + const {keyword, data, schemaCode} = cxt + const op = keyword === "maxItems" ? operators.GT : operators.LT + cxt.fail$data(_`${data}.length ${op} ${schemaCode}`) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/limitLength.ts b/website/node_modules/ajv/lib/vocabularies/validation/limitLength.ts new file mode 100644 index 00000000..f4f94725 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/limitLength.ts @@ -0,0 +1,30 @@ +import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str, operators} from "../../compile/codegen" +import {useFunc} from "../../compile/util" +import ucs2length from "../../runtime/ucs2length" + +const error: KeywordErrorDefinition = { + message({keyword, schemaCode}) { + const comp = keyword === "maxLength" ? "more" : "fewer" + return str`must NOT have ${comp} than ${schemaCode} characters` + }, + params: ({schemaCode}) => _`{limit: ${schemaCode}}`, +} + +const def: CodeKeywordDefinition = { + keyword: ["maxLength", "minLength"], + type: "string", + schemaType: "number", + $data: true, + error, + code(cxt: KeywordCxt) { + const {keyword, data, schemaCode, it} = cxt + const op = keyword === "maxLength" ? operators.GT : operators.LT + const len = + it.opts.unicode === false ? _`${data}.length` : _`${useFunc(cxt.gen, ucs2length)}(${data})` + cxt.fail$data(_`${len} ${op} ${schemaCode}`) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/limitNumber.ts b/website/node_modules/ajv/lib/vocabularies/validation/limitNumber.ts new file mode 100644 index 00000000..5499202e --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/limitNumber.ts @@ -0,0 +1,42 @@ +import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str, operators, Code} from "../../compile/codegen" + +const ops = operators + +type Kwd = "maximum" | "minimum" | "exclusiveMaximum" | "exclusiveMinimum" + +type Comparison = "<=" | ">=" | "<" | ">" + +const KWDs: {[K in Kwd]: {okStr: Comparison; ok: Code; fail: Code}} = { + maximum: {okStr: "<=", ok: ops.LTE, fail: ops.GT}, + minimum: {okStr: ">=", ok: ops.GTE, fail: ops.LT}, + exclusiveMaximum: {okStr: "<", ok: ops.LT, fail: ops.GTE}, + exclusiveMinimum: {okStr: ">", ok: ops.GT, fail: ops.LTE}, +} + +export type LimitNumberError = ErrorObject< + Kwd, + {limit: number; comparison: Comparison}, + number | {$data: string} +> + +const error: KeywordErrorDefinition = { + message: ({keyword, schemaCode}) => str`must be ${KWDs[keyword as Kwd].okStr} ${schemaCode}`, + params: ({keyword, schemaCode}) => + _`{comparison: ${KWDs[keyword as Kwd].okStr}, limit: ${schemaCode}}`, +} + +const def: CodeKeywordDefinition = { + keyword: Object.keys(KWDs), + type: "number", + schemaType: "number", + $data: true, + error, + code(cxt: KeywordCxt) { + const {keyword, data, schemaCode} = cxt + cxt.fail$data(_`${data} ${KWDs[keyword as Kwd].fail} ${schemaCode} || isNaN(${data})`) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/limitProperties.ts b/website/node_modules/ajv/lib/vocabularies/validation/limitProperties.ts new file mode 100644 index 00000000..07fffa8b --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/limitProperties.ts @@ -0,0 +1,26 @@ +import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str, operators} from "../../compile/codegen" + +const error: KeywordErrorDefinition = { + message({keyword, schemaCode}) { + const comp = keyword === "maxProperties" ? "more" : "fewer" + return str`must NOT have ${comp} than ${schemaCode} properties` + }, + params: ({schemaCode}) => _`{limit: ${schemaCode}}`, +} + +const def: CodeKeywordDefinition = { + keyword: ["maxProperties", "minProperties"], + type: "object", + schemaType: "number", + $data: true, + error, + code(cxt: KeywordCxt) { + const {keyword, data, schemaCode} = cxt + const op = keyword === "maxProperties" ? operators.GT : operators.LT + cxt.fail$data(_`Object.keys(${data}).length ${op} ${schemaCode}`) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/multipleOf.ts b/website/node_modules/ajv/lib/vocabularies/validation/multipleOf.ts new file mode 100644 index 00000000..1fd79abb --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/multipleOf.ts @@ -0,0 +1,34 @@ +import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {_, str} from "../../compile/codegen" + +export type MultipleOfError = ErrorObject< + "multipleOf", + {multipleOf: number}, + number | {$data: string} +> + +const error: KeywordErrorDefinition = { + message: ({schemaCode}) => str`must be multiple of ${schemaCode}`, + params: ({schemaCode}) => _`{multipleOf: ${schemaCode}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "multipleOf", + type: "number", + schemaType: "number", + $data: true, + error, + code(cxt: KeywordCxt) { + const {gen, data, schemaCode, it} = cxt + // const bdt = bad$DataType(schemaCode, def.schemaType, $data) + const prec = it.opts.multipleOfPrecision + const res = gen.let("res") + const invalid = prec + ? _`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` + : _`${res} !== parseInt(${res})` + cxt.fail$data(_`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/pattern.ts b/website/node_modules/ajv/lib/vocabularies/validation/pattern.ts new file mode 100644 index 00000000..7b27b7d3 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/pattern.ts @@ -0,0 +1,28 @@ +import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {usePattern} from "../code" +import {_, str} from "../../compile/codegen" + +export type PatternError = ErrorObject<"pattern", {pattern: string}, string | {$data: string}> + +const error: KeywordErrorDefinition = { + message: ({schemaCode}) => str`must match pattern "${schemaCode}"`, + params: ({schemaCode}) => _`{pattern: ${schemaCode}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "pattern", + type: "string", + schemaType: "string", + $data: true, + error, + code(cxt: KeywordCxt) { + const {data, $data, schema, schemaCode, it} = cxt + // TODO regexp should be wrapped in try/catchs + const u = it.opts.unicodeRegExp ? "u" : "" + const regExp = $data ? _`(new RegExp(${schemaCode}, ${u}))` : usePattern(cxt, schema) + cxt.fail$data(_`!${regExp}.test(${data})`) + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/required.ts b/website/node_modules/ajv/lib/vocabularies/validation/required.ts new file mode 100644 index 00000000..fea7367e --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/required.ts @@ -0,0 +1,98 @@ +import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import { + checkReportMissingProp, + checkMissingProp, + reportMissingProp, + propertyInData, + noPropertyInData, +} from "../code" +import {_, str, nil, not, Name, Code} from "../../compile/codegen" +import {checkStrictMode} from "../../compile/util" + +export type RequiredError = ErrorObject< + "required", + {missingProperty: string}, + string[] | {$data: string} +> + +const error: KeywordErrorDefinition = { + message: ({params: {missingProperty}}) => str`must have required property '${missingProperty}'`, + params: ({params: {missingProperty}}) => _`{missingProperty: ${missingProperty}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "required", + type: "object", + schemaType: "array", + $data: true, + error, + code(cxt: KeywordCxt) { + const {gen, schema, schemaCode, data, $data, it} = cxt + const {opts} = it + if (!$data && schema.length === 0) return + const useLoop = schema.length >= opts.loopRequired + if (it.allErrors) allErrorsMode() + else exitOnErrorMode() + + if (opts.strictRequired) { + const props = cxt.parentSchema.properties + const {definedProperties} = cxt.it + for (const requiredKey of schema) { + if (props?.[requiredKey] === undefined && !definedProperties.has(requiredKey)) { + const schemaPath = it.schemaEnv.baseId + it.errSchemaPath + const msg = `required property "${requiredKey}" is not defined at "${schemaPath}" (strictRequired)` + checkStrictMode(it, msg, it.opts.strictRequired) + } + } + } + + function allErrorsMode(): void { + if (useLoop || $data) { + cxt.block$data(nil, loopAllRequired) + } else { + for (const prop of schema) { + checkReportMissingProp(cxt, prop) + } + } + } + + function exitOnErrorMode(): void { + const missing = gen.let("missing") + if (useLoop || $data) { + const valid = gen.let("valid", true) + cxt.block$data(valid, () => loopUntilMissing(missing, valid)) + cxt.ok(valid) + } else { + gen.if(checkMissingProp(cxt, schema, missing)) + reportMissingProp(cxt, missing) + gen.else() + } + } + + function loopAllRequired(): void { + gen.forOf("prop", schemaCode as Code, (prop) => { + cxt.setParams({missingProperty: prop}) + gen.if(noPropertyInData(gen, data, prop, opts.ownProperties), () => cxt.error()) + }) + } + + function loopUntilMissing(missing: Name, valid: Name): void { + cxt.setParams({missingProperty: missing}) + gen.forOf( + missing, + schemaCode as Code, + () => { + gen.assign(valid, propertyInData(gen, data, missing, opts.ownProperties)) + gen.if(not(valid), () => { + cxt.error() + gen.break() + }) + }, + nil + ) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/lib/vocabularies/validation/uniqueItems.ts b/website/node_modules/ajv/lib/vocabularies/validation/uniqueItems.ts new file mode 100644 index 00000000..765c4d04 --- /dev/null +++ b/website/node_modules/ajv/lib/vocabularies/validation/uniqueItems.ts @@ -0,0 +1,79 @@ +import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" +import type {KeywordCxt} from "../../compile/validate" +import {checkDataTypes, getSchemaTypes, DataType} from "../../compile/validate/dataType" +import {_, str, Name} from "../../compile/codegen" +import {useFunc} from "../../compile/util" +import equal from "../../runtime/equal" + +export type UniqueItemsError = ErrorObject< + "uniqueItems", + {i: number; j: number}, + boolean | {$data: string} +> + +const error: KeywordErrorDefinition = { + message: ({params: {i, j}}) => + str`must NOT have duplicate items (items ## ${j} and ${i} are identical)`, + params: ({params: {i, j}}) => _`{i: ${i}, j: ${j}}`, +} + +const def: CodeKeywordDefinition = { + keyword: "uniqueItems", + type: "array", + schemaType: "boolean", + $data: true, + error, + code(cxt: KeywordCxt) { + const {gen, data, $data, schema, parentSchema, schemaCode, it} = cxt + if (!$data && !schema) return + const valid = gen.let("valid") + const itemTypes = parentSchema.items ? getSchemaTypes(parentSchema.items) : [] + cxt.block$data(valid, validateUniqueItems, _`${schemaCode} === false`) + cxt.ok(valid) + + function validateUniqueItems(): void { + const i = gen.let("i", _`${data}.length`) + const j = gen.let("j") + cxt.setParams({i, j}) + gen.assign(valid, true) + gen.if(_`${i} > 1`, () => (canOptimize() ? loopN : loopN2)(i, j)) + } + + function canOptimize(): boolean { + return itemTypes.length > 0 && !itemTypes.some((t) => t === "object" || t === "array") + } + + function loopN(i: Name, j: Name): void { + const item = gen.name("item") + const wrongType = checkDataTypes(itemTypes, item, it.opts.strictNumbers, DataType.Wrong) + const indices = gen.const("indices", _`{}`) + gen.for(_`;${i}--;`, () => { + gen.let(item, _`${data}[${i}]`) + gen.if(wrongType, _`continue`) + if (itemTypes.length > 1) gen.if(_`typeof ${item} == "string"`, _`${item} += "_"`) + gen + .if(_`typeof ${indices}[${item}] == "number"`, () => { + gen.assign(j, _`${indices}[${item}]`) + cxt.error() + gen.assign(valid, false).break() + }) + .code(_`${indices}[${item}] = ${i}`) + }) + } + + function loopN2(i: Name, j: Name): void { + const eql = useFunc(gen, equal) + const outer = gen.name("outer") + gen.label(outer).for(_`;${i}--;`, () => + gen.for(_`${j} = ${i}; ${j}--;`, () => + gen.if(_`${eql}(${data}[${i}], ${data}[${j}])`, () => { + cxt.error() + gen.assign(valid, false).break(outer) + }) + ) + ) + } + }, +} + +export default def diff --git a/website/node_modules/ajv/package.json b/website/node_modules/ajv/package.json new file mode 100644 index 00000000..61ae2b99 --- /dev/null +++ b/website/node_modules/ajv/package.json @@ -0,0 +1,126 @@ +{ + "name": "ajv", + "version": "8.12.0", + "description": "Another JSON Schema Validator", + "main": "dist/ajv.js", + "types": "dist/ajv.d.ts", + "files": [ + "lib/", + "dist/", + ".runkit_example.js" + ], + "scripts": { + "eslint": "eslint \"lib/**/*.ts\" \"spec/**/*.*s\" --ignore-pattern spec/JSON-Schema-Test-Suite", + "prettier:write": "prettier --write \"./**/*.{json,yaml,js,ts}\"", + "prettier:check": "prettier --list-different \"./**/*.{json,yaml,js,ts}\"", + "test-spec": "cross-env TS_NODE_PROJECT=spec/tsconfig.json mocha -r ts-node/register \"spec/**/*.spec.{ts,js}\" -R dot", + "test-codegen": "nyc cross-env TS_NODE_PROJECT=spec/tsconfig.json mocha -r ts-node/register 'spec/codegen.spec.ts' -R spec", + "test-debug": "npm run test-spec -- --inspect-brk", + "test-cov": "nyc npm run test-spec", + "rollup": "rm -rf bundle && rollup -c", + "bundle": "rm -rf bundle && node ./scripts/bundle.js ajv ajv7 ajv7 && node ./scripts/bundle.js 2019 ajv2019 ajv2019 && node ./scripts/bundle.js 2020 ajv2020 ajv2020 && node ./scripts/bundle.js jtd ajvJTD ajvJTD", + "build": "rm -rf dist && tsc && cp -r lib/refs dist && rm dist/refs/json-schema-2019-09/index.ts && rm dist/refs/json-schema-2020-12/index.ts && rm dist/refs/jtd-schema.ts", + "json-tests": "rm -rf spec/_json/*.js && node scripts/jsontests", + "test-karma": "karma start", + "test-browser": "rm -rf .browser && npm run bundle && scripts/prepare-tests && karma start", + "test-all": "npm run test-cov && if-node-version 12 npm run test-browser", + "test": "npm run json-tests && npm run prettier:check && npm run eslint && npm link && npm link --legacy-peer-deps ajv && npm run test-cov", + "test-ci": "AJV_FULL_TEST=true npm test", + "prepublish": "npm run build", + "benchmark": "npm i && npm run build && npm link && cd ./benchmark && npm link --legacy-peer-deps ajv && npm i && node ./jtd", + "docs:dev": "./scripts/prepare-site && vuepress dev docs", + "docs:build": "./scripts/prepare-site && vuepress build docs" + }, + "nyc": { + "exclude": [ + "**/spec/**", + "node_modules" + ], + "reporter": [ + "lcov", + "text-summary" + ] + }, + "repository": "ajv-validator/ajv", + "keywords": [ + "JSON", + "schema", + "validator", + "validation", + "jsonschema", + "json-schema", + "json-schema-validator", + "json-schema-validation" + ], + "author": "Evgeny Poberezkin", + "license": "MIT", + "bugs": "https://github.com/ajv-validator/ajv/issues", + "homepage": "https://ajv.js.org", + "runkitExampleFilename": ".runkit_example.js", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "devDependencies": { + "@ajv-validator/config": "^0.3.0", + "@rollup/plugin-commonjs": "^24.0.0", + "@rollup/plugin-json": "^6.0.0", + "@rollup/plugin-node-resolve": "^15.0.1", + "@rollup/plugin-typescript": "^10.0.1", + "@types/chai": "^4.2.12", + "@types/mocha": "^10.0.0", + "@types/node": "^18.11.9", + "@types/require-from-string": "^1.2.0", + "@typescript-eslint/eslint-plugin": "^3.8.0", + "@typescript-eslint/parser": "^3.8.0", + "ajv-formats": "^3.0.0-rc.0", + "browserify": "^17.0.0", + "chai": "^4.0.1", + "cross-env": "^7.0.2", + "dayjs": "^1.10.4", + "dayjs-plugin-utc": "^0.1.2", + "eslint": "^7.8.1", + "eslint-config-prettier": "^7.0.0", + "fast-uri": "^2.1.0", + "glob": "^8.0.2", + "husky": "^8.0.2", + "if-node-version": "^1.0.0", + "jimp": "^0.16.1", + "js-beautify": "^1.7.3", + "json-schema-test": "^2.0.0", + "karma": "^6.0.0", + "karma-chrome-launcher": "^3.0.0", + "karma-mocha": "^2.0.0", + "lint-staged": "^13.0.3", + "mocha": "^10.0.0", + "module-from-string": "^3.1.3", + "node-fetch": "^3.0.0", + "nyc": "^15.0.0", + "prettier": "^2.3.1", + "re2": "^1.16.0", + "rollup": "^2.44.0", + "rollup-plugin-terser": "^7.0.2", + "ts-node": "^10.0.0", + "tsify": "^5.0.2", + "typescript": "^4.8.0" + }, + "collective": { + "type": "opencollective", + "url": "https://opencollective.com/ajv" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + }, + "prettier": "@ajv-validator/config/prettierrc.json", + "husky": { + "hooks": { + "pre-commit": "lint-staged && npm test" + } + }, + "lint-staged": { + "*.{json,yaml,js,ts}": "prettier --write" + } +} diff --git a/website/node_modules/ansi-align/CHANGELOG.md b/website/node_modules/ansi-align/CHANGELOG.md new file mode 100644 index 00000000..36eeb52c --- /dev/null +++ b/website/node_modules/ansi-align/CHANGELOG.md @@ -0,0 +1,58 @@ +# Changelog + +All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +### [3.0.1](https://github.com/nexdrew/ansi-align/compare/v3.0.0...v3.0.1) (2021-09-27) + + +### Bug Fixes + +* **package:** update string-width to version 4.1.0 ([#52](https://github.com/nexdrew/ansi-align/issues/52)) ([ab5b733](https://github.com/nexdrew/ansi-align/commit/ab5b733b1c30eef87b75e15459f2216db28d7ed3)) + + +# [3.0.0](https://github.com/nexdrew/ansi-align/compare/v2.0.0...v3.0.0) (2018-12-17) + + +### Bug Fixes + +* **package:** update string-width to version 3.0.0 ([#50](https://github.com/nexdrew/ansi-align/issues/50)) ([67f0d8f](https://github.com/nexdrew/ansi-align/commit/67f0d8f)) + + +### BREAKING CHANGES + +* **package:** Node 4 no longer supported, please update to Node 6+ or use ansi-align@2.0.0 + + + + +# [2.0.0](https://github.com/nexdrew/ansi-align/compare/v1.1.0...v2.0.0) (2017-05-01) + + +### Features + +* ES2015ify, dropping support for Node <4 ([#30](https://github.com/nexdrew/ansi-align/issues/30)) ([7b43f48](https://github.com/nexdrew/ansi-align/commit/7b43f48)) + + +### BREAKING CHANGES + +* Node 0.10 or 0.12 no longer supported, please update to Node 4+ or use ansi-align@1.1.0 + + + + +# [1.1.0](https://github.com/nexdrew/ansi-align/compare/v1.0.0...v1.1.0) (2016-06-06) + + +### Features + +* support left-alignment as no-op ([#3](https://github.com/nexdrew/ansi-align/issues/3)) ([e581db6](https://github.com/nexdrew/ansi-align/commit/e581db6)) + + + + +# 1.0.0 (2016-04-30) + + +### Features + +* initial commit ([1914d90](https://github.com/nexdrew/ansi-align/commit/1914d90)) diff --git a/website/node_modules/ansi-align/LICENSE b/website/node_modules/ansi-align/LICENSE new file mode 100644 index 00000000..ab601b65 --- /dev/null +++ b/website/node_modules/ansi-align/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2016, Contributors + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/website/node_modules/ansi-align/README.md b/website/node_modules/ansi-align/README.md new file mode 100644 index 00000000..5e516825 --- /dev/null +++ b/website/node_modules/ansi-align/README.md @@ -0,0 +1,80 @@ +# ansi-align + +> align-text with ANSI support for CLIs + +[![Build Status](https://travis-ci.org/nexdrew/ansi-align.svg?branch=master)](https://travis-ci.org/nexdrew/ansi-align) +[![Coverage Status](https://coveralls.io/repos/github/nexdrew/ansi-align/badge.svg?branch=master)](https://coveralls.io/github/nexdrew/ansi-align?branch=master) +[![Standard Version](https://img.shields.io/badge/release-standard%20version-brightgreen.svg)](https://github.com/conventional-changelog/standard-version) +[![Greenkeeper badge](https://badges.greenkeeper.io/nexdrew/ansi-align.svg)](https://greenkeeper.io/) + +Easily center- or right- align a block of text, carefully ignoring ANSI escape codes. + +E.g. turn this: + +ansi text block no alignment :( + +Into this: + +ansi text block center aligned! + +## Install + +```sh +npm install --save ansi-align +``` + +```js +var ansiAlign = require('ansi-align') +``` + +## API + +### `ansiAlign(text, [opts])` + +Align the given text per the line with the greatest [`string-width`](https://github.com/sindresorhus/string-width), returning a new string (or array). + +#### Arguments + +- `text`: required, string or array + + The text to align. If a string is given, it will be split using either the `opts.split` value or `'\n'` by default. If an array is given, a different array of modified strings will be returned. + +- `opts`: optional, object + + Options to change behavior, see below. + +#### Options + +- `opts.align`: string, default `'center'` + + The alignment mode. Use `'center'` for center-alignment, `'right'` for right-alignment, or `'left'` for left-alignment. Note that the given `text` is assumed to be left-aligned already, so specifying `align: 'left'` just returns the `text` as is (no-op). + +- `opts.split`: string or RegExp, default `'\n'` + + The separator to use when splitting the text. Only used if text is given as a string. + +- `opts.pad`: string, default `' '` + + The value used to left-pad (prepend to) lines of lesser width. Will be repeated as necessary to adjust alignment to the line with the greatest width. + +### `ansiAlign.center(text)` + +Alias for `ansiAlign(text, { align: 'center' })`. + +### `ansiAlign.right(text)` + +Alias for `ansiAlign(text, { align: 'right' })`. + +### `ansiAlign.left(text)` + +Alias for `ansiAlign(text, { align: 'left' })`, which is a no-op. + +## Similar Packages + +- [`center-align`](https://github.com/jonschlinkert/center-align): Very close to this package, except it doesn't support ANSI codes. +- [`left-pad`](https://github.com/camwest/left-pad): Great for left-padding but does not support center alignment or ANSI codes. +- Pretty much anything by the [chalk](https://github.com/chalk) team + +## License + +ISC © Contributors diff --git a/website/node_modules/ansi-align/index.js b/website/node_modules/ansi-align/index.js new file mode 100644 index 00000000..67fa826d --- /dev/null +++ b/website/node_modules/ansi-align/index.js @@ -0,0 +1,61 @@ +'use strict' + +const stringWidth = require('string-width') + +function ansiAlign (text, opts) { + if (!text) return text + + opts = opts || {} + const align = opts.align || 'center' + + // short-circuit `align: 'left'` as no-op + if (align === 'left') return text + + const split = opts.split || '\n' + const pad = opts.pad || ' ' + const widthDiffFn = align !== 'right' ? halfDiff : fullDiff + + let returnString = false + if (!Array.isArray(text)) { + returnString = true + text = String(text).split(split) + } + + let width + let maxWidth = 0 + text = text.map(function (str) { + str = String(str) + width = stringWidth(str) + maxWidth = Math.max(width, maxWidth) + return { + str, + width + } + }).map(function (obj) { + return new Array(widthDiffFn(maxWidth, obj.width) + 1).join(pad) + obj.str + }) + + return returnString ? text.join(split) : text +} + +ansiAlign.left = function left (text) { + return ansiAlign(text, { align: 'left' }) +} + +ansiAlign.center = function center (text) { + return ansiAlign(text, { align: 'center' }) +} + +ansiAlign.right = function right (text) { + return ansiAlign(text, { align: 'right' }) +} + +module.exports = ansiAlign + +function halfDiff (maxWidth, curWidth) { + return Math.floor((maxWidth - curWidth) / 2) +} + +function fullDiff (maxWidth, curWidth) { + return maxWidth - curWidth +} diff --git a/website/node_modules/ansi-align/node_modules/ansi-regex/index.d.ts b/website/node_modules/ansi-align/node_modules/ansi-regex/index.d.ts new file mode 100644 index 00000000..2dbf6af2 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/ansi-regex/index.d.ts @@ -0,0 +1,37 @@ +declare namespace ansiRegex { + interface Options { + /** + Match only the first ANSI escape. + + @default false + */ + onlyFirst: boolean; + } +} + +/** +Regular expression for matching ANSI escape codes. + +@example +``` +import ansiRegex = require('ansi-regex'); + +ansiRegex().test('\u001B[4mcake\u001B[0m'); +//=> true + +ansiRegex().test('cake'); +//=> false + +'\u001B[4mcake\u001B[0m'.match(ansiRegex()); +//=> ['\u001B[4m', '\u001B[0m'] + +'\u001B[4mcake\u001B[0m'.match(ansiRegex({onlyFirst: true})); +//=> ['\u001B[4m'] + +'\u001B]8;;https://github.com\u0007click\u001B]8;;\u0007'.match(ansiRegex()); +//=> ['\u001B]8;;https://github.com\u0007', '\u001B]8;;\u0007'] +``` +*/ +declare function ansiRegex(options?: ansiRegex.Options): RegExp; + +export = ansiRegex; diff --git a/website/node_modules/ansi-align/node_modules/ansi-regex/index.js b/website/node_modules/ansi-align/node_modules/ansi-regex/index.js new file mode 100644 index 00000000..616ff837 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/ansi-regex/index.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = ({onlyFirst = false} = {}) => { + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' + ].join('|'); + + return new RegExp(pattern, onlyFirst ? undefined : 'g'); +}; diff --git a/website/node_modules/ansi-align/node_modules/ansi-regex/license b/website/node_modules/ansi-align/node_modules/ansi-regex/license new file mode 100644 index 00000000..e7af2f77 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/ansi-regex/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/ansi-align/node_modules/ansi-regex/package.json b/website/node_modules/ansi-align/node_modules/ansi-regex/package.json new file mode 100644 index 00000000..017f5311 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/ansi-regex/package.json @@ -0,0 +1,55 @@ +{ + "name": "ansi-regex", + "version": "5.0.1", + "description": "Regular expression for matching ANSI escape codes", + "license": "MIT", + "repository": "chalk/ansi-regex", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=8" + }, + "scripts": { + "test": "xo && ava && tsd", + "view-supported": "node fixtures/view-codes.js" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "devDependencies": { + "ava": "^2.4.0", + "tsd": "^0.9.0", + "xo": "^0.25.3" + } +} diff --git a/website/node_modules/ansi-align/node_modules/ansi-regex/readme.md b/website/node_modules/ansi-align/node_modules/ansi-regex/readme.md new file mode 100644 index 00000000..4d848bc3 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/ansi-regex/readme.md @@ -0,0 +1,78 @@ +# ansi-regex + +> Regular expression for matching [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) + + +## Install + +``` +$ npm install ansi-regex +``` + + +## Usage + +```js +const ansiRegex = require('ansi-regex'); + +ansiRegex().test('\u001B[4mcake\u001B[0m'); +//=> true + +ansiRegex().test('cake'); +//=> false + +'\u001B[4mcake\u001B[0m'.match(ansiRegex()); +//=> ['\u001B[4m', '\u001B[0m'] + +'\u001B[4mcake\u001B[0m'.match(ansiRegex({onlyFirst: true})); +//=> ['\u001B[4m'] + +'\u001B]8;;https://github.com\u0007click\u001B]8;;\u0007'.match(ansiRegex()); +//=> ['\u001B]8;;https://github.com\u0007', '\u001B]8;;\u0007'] +``` + + +## API + +### ansiRegex(options?) + +Returns a regex for matching ANSI escape codes. + +#### options + +Type: `object` + +##### onlyFirst + +Type: `boolean`
+Default: `false` *(Matches any ANSI escape codes in a string)* + +Match only the first ANSI escape. + + +## FAQ + +### Why do you test for codes not in the ECMA 48 standard? + +Some of the codes we run as a test are codes that we acquired finding various lists of non-standard or manufacturer specific codes. We test for both standard and non-standard codes, as most of them follow the same or similar format and can be safely matched in strings without the risk of removing actual string content. There are a few non-standard control codes that do not follow the traditional format (i.e. they end in numbers) thus forcing us to exclude them from the test because we cannot reliably match them. + +On the historical side, those ECMA standards were established in the early 90's whereas the VT100, for example, was designed in the mid/late 70's. At that point in time, control codes were still pretty ungoverned and engineers used them for a multitude of things, namely to activate hardware ports that may have been proprietary. Somewhere else you see a similar 'anarchy' of codes is in the x86 architecture for processors; there are a ton of "interrupts" that can mean different things on certain brands of processors, most of which have been phased out. + + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + + +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/website/node_modules/ansi-align/node_modules/emoji-regex/LICENSE-MIT.txt b/website/node_modules/ansi-align/node_modules/emoji-regex/LICENSE-MIT.txt new file mode 100644 index 00000000..a41e0a7e --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/emoji-regex/LICENSE-MIT.txt @@ -0,0 +1,20 @@ +Copyright Mathias Bynens + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/ansi-align/node_modules/emoji-regex/README.md b/website/node_modules/ansi-align/node_modules/emoji-regex/README.md new file mode 100644 index 00000000..f10e1733 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/emoji-regex/README.md @@ -0,0 +1,73 @@ +# emoji-regex [![Build status](https://travis-ci.org/mathiasbynens/emoji-regex.svg?branch=master)](https://travis-ci.org/mathiasbynens/emoji-regex) + +_emoji-regex_ offers a regular expression to match all emoji symbols (including textual representations of emoji) as per the Unicode Standard. + +This repository contains a script that generates this regular expression based on [the data from Unicode v12](https://github.com/mathiasbynens/unicode-12.0.0). Because of this, the regular expression can easily be updated whenever new emoji are added to the Unicode standard. + +## Installation + +Via [npm](https://www.npmjs.com/): + +```bash +npm install emoji-regex +``` + +In [Node.js](https://nodejs.org/): + +```js +const emojiRegex = require('emoji-regex'); +// Note: because the regular expression has the global flag set, this module +// exports a function that returns the regex rather than exporting the regular +// expression itself, to make it impossible to (accidentally) mutate the +// original regular expression. + +const text = ` +\u{231A}: ⌚ default emoji presentation character (Emoji_Presentation) +\u{2194}\u{FE0F}: ↔️ default text presentation character rendered as emoji +\u{1F469}: 👩 emoji modifier base (Emoji_Modifier_Base) +\u{1F469}\u{1F3FF}: 👩🏿 emoji modifier base followed by a modifier +`; + +const regex = emojiRegex(); +let match; +while (match = regex.exec(text)) { + const emoji = match[0]; + console.log(`Matched sequence ${ emoji } — code points: ${ [...emoji].length }`); +} +``` + +Console output: + +``` +Matched sequence ⌚ — code points: 1 +Matched sequence ⌚ — code points: 1 +Matched sequence ↔️ — code points: 2 +Matched sequence ↔️ — code points: 2 +Matched sequence 👩 — code points: 1 +Matched sequence 👩 — code points: 1 +Matched sequence 👩🏿 — code points: 2 +Matched sequence 👩🏿 — code points: 2 +``` + +To match emoji in their textual representation as well (i.e. emoji that are not `Emoji_Presentation` symbols and that aren’t forced to render as emoji by a variation selector), `require` the other regex: + +```js +const emojiRegex = require('emoji-regex/text.js'); +``` + +Additionally, in environments which support ES2015 Unicode escapes, you may `require` ES2015-style versions of the regexes: + +```js +const emojiRegex = require('emoji-regex/es2015/index.js'); +const emojiRegexText = require('emoji-regex/es2015/text.js'); +``` + +## Author + +| [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter") | +|---| +| [Mathias Bynens](https://mathiasbynens.be/) | + +## License + +_emoji-regex_ is available under the [MIT](https://mths.be/mit) license. diff --git a/website/node_modules/ansi-align/node_modules/emoji-regex/es2015/index.js b/website/node_modules/ansi-align/node_modules/emoji-regex/es2015/index.js new file mode 100644 index 00000000..b4cf3dcd --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/emoji-regex/es2015/index.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = () => { + // https://mths.be/emoji + return /\u{1F3F4}\u{E0067}\u{E0062}(?:\u{E0065}\u{E006E}\u{E0067}|\u{E0073}\u{E0063}\u{E0074}|\u{E0077}\u{E006C}\u{E0073})\u{E007F}|\u{1F468}(?:\u{1F3FC}\u200D(?:\u{1F91D}\u200D\u{1F468}\u{1F3FB}|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FF}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}-\u{1F3FE}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FE}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}-\u{1F3FD}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FD}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}\u{1F3FC}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D)?\u{1F468}|[\u{1F468}\u{1F469}]\u200D(?:\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}])|\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}]|[\u{1F468}\u{1F469}]\u200D[\u{1F466}\u{1F467}]|[\u2695\u2696\u2708]\uFE0F|[\u{1F466}\u{1F467}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|(?:\u{1F3FB}\u200D[\u2695\u2696\u2708]|\u{1F3FF}\u200D[\u2695\u2696\u2708]|\u{1F3FE}\u200D[\u2695\u2696\u2708]|\u{1F3FD}\u200D[\u2695\u2696\u2708]|\u{1F3FC}\u200D[\u2695\u2696\u2708])\uFE0F|\u{1F3FB}\u200D[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]|[\u{1F3FB}-\u{1F3FF}])|(?:\u{1F9D1}\u{1F3FB}\u200D\u{1F91D}\u200D\u{1F9D1}|\u{1F469}\u{1F3FC}\u200D\u{1F91D}\u200D\u{1F469})\u{1F3FB}|\u{1F9D1}(?:\u{1F3FF}\u200D\u{1F91D}\u200D\u{1F9D1}[\u{1F3FB}-\u{1F3FF}]|\u200D\u{1F91D}\u200D\u{1F9D1})|(?:\u{1F9D1}\u{1F3FE}\u200D\u{1F91D}\u200D\u{1F9D1}|\u{1F469}\u{1F3FF}\u200D\u{1F91D}\u200D[\u{1F468}\u{1F469}])[\u{1F3FB}-\u{1F3FE}]|(?:\u{1F9D1}\u{1F3FC}\u200D\u{1F91D}\u200D\u{1F9D1}|\u{1F469}\u{1F3FD}\u200D\u{1F91D}\u200D\u{1F469})[\u{1F3FB}\u{1F3FC}]|\u{1F469}(?:\u{1F3FE}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}-\u{1F3FD}\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FC}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}\u{1F3FD}-\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FB}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FC}-\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FD}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}\u{1F3FC}\u{1F3FE}\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D[\u{1F468}\u{1F469}]|[\u{1F468}\u{1F469}])|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FF}\u200D[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F469}\u200D\u{1F469}\u200D(?:\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}])|(?:\u{1F9D1}\u{1F3FD}\u200D\u{1F91D}\u200D\u{1F9D1}|\u{1F469}\u{1F3FE}\u200D\u{1F91D}\u200D\u{1F469})[\u{1F3FB}-\u{1F3FD}]|\u{1F469}\u200D\u{1F466}\u200D\u{1F466}|\u{1F469}\u200D\u{1F469}\u200D[\u{1F466}\u{1F467}]|(?:\u{1F441}\uFE0F\u200D\u{1F5E8}|\u{1F469}(?:\u{1F3FF}\u200D[\u2695\u2696\u2708]|\u{1F3FE}\u200D[\u2695\u2696\u2708]|\u{1F3FC}\u200D[\u2695\u2696\u2708]|\u{1F3FB}\u200D[\u2695\u2696\u2708]|\u{1F3FD}\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}]\uFE0F|[\u{1F46F}\u{1F93C}\u{1F9DE}\u{1F9DF}])\u200D[\u2640\u2642]|[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}][\u{1F3FB}-\u{1F3FF}]\u200D[\u2640\u2642]|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9CD}-\u{1F9CF}\u{1F9D6}-\u{1F9DD}](?:[\u{1F3FB}-\u{1F3FF}]\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\u{1F3F4}\u200D\u2620)\uFE0F|\u{1F469}\u200D\u{1F467}\u200D[\u{1F466}\u{1F467}]|\u{1F3F3}\uFE0F\u200D\u{1F308}|\u{1F415}\u200D\u{1F9BA}|\u{1F469}\u200D\u{1F466}|\u{1F469}\u200D\u{1F467}|\u{1F1FD}\u{1F1F0}|\u{1F1F4}\u{1F1F2}|\u{1F1F6}\u{1F1E6}|[#\*0-9]\uFE0F\u20E3|\u{1F1E7}[\u{1F1E6}\u{1F1E7}\u{1F1E9}-\u{1F1EF}\u{1F1F1}-\u{1F1F4}\u{1F1F6}-\u{1F1F9}\u{1F1FB}\u{1F1FC}\u{1F1FE}\u{1F1FF}]|\u{1F1F9}[\u{1F1E6}\u{1F1E8}\u{1F1E9}\u{1F1EB}-\u{1F1ED}\u{1F1EF}-\u{1F1F4}\u{1F1F7}\u{1F1F9}\u{1F1FB}\u{1F1FC}\u{1F1FF}]|\u{1F1EA}[\u{1F1E6}\u{1F1E8}\u{1F1EA}\u{1F1EC}\u{1F1ED}\u{1F1F7}-\u{1F1FA}]|\u{1F9D1}[\u{1F3FB}-\u{1F3FF}]|\u{1F1F7}[\u{1F1EA}\u{1F1F4}\u{1F1F8}\u{1F1FA}\u{1F1FC}]|\u{1F469}[\u{1F3FB}-\u{1F3FF}]|\u{1F1F2}[\u{1F1E6}\u{1F1E8}-\u{1F1ED}\u{1F1F0}-\u{1F1FF}]|\u{1F1E6}[\u{1F1E8}-\u{1F1EC}\u{1F1EE}\u{1F1F1}\u{1F1F2}\u{1F1F4}\u{1F1F6}-\u{1F1FA}\u{1F1FC}\u{1F1FD}\u{1F1FF}]|\u{1F1F0}[\u{1F1EA}\u{1F1EC}-\u{1F1EE}\u{1F1F2}\u{1F1F3}\u{1F1F5}\u{1F1F7}\u{1F1FC}\u{1F1FE}\u{1F1FF}]|\u{1F1ED}[\u{1F1F0}\u{1F1F2}\u{1F1F3}\u{1F1F7}\u{1F1F9}\u{1F1FA}]|\u{1F1E9}[\u{1F1EA}\u{1F1EC}\u{1F1EF}\u{1F1F0}\u{1F1F2}\u{1F1F4}\u{1F1FF}]|\u{1F1FE}[\u{1F1EA}\u{1F1F9}]|\u{1F1EC}[\u{1F1E6}\u{1F1E7}\u{1F1E9}-\u{1F1EE}\u{1F1F1}-\u{1F1F3}\u{1F1F5}-\u{1F1FA}\u{1F1FC}\u{1F1FE}]|\u{1F1F8}[\u{1F1E6}-\u{1F1EA}\u{1F1EC}-\u{1F1F4}\u{1F1F7}-\u{1F1F9}\u{1F1FB}\u{1F1FD}-\u{1F1FF}]|\u{1F1EB}[\u{1F1EE}-\u{1F1F0}\u{1F1F2}\u{1F1F4}\u{1F1F7}]|\u{1F1F5}[\u{1F1E6}\u{1F1EA}-\u{1F1ED}\u{1F1F0}-\u{1F1F3}\u{1F1F7}-\u{1F1F9}\u{1F1FC}\u{1F1FE}]|\u{1F1FB}[\u{1F1E6}\u{1F1E8}\u{1F1EA}\u{1F1EC}\u{1F1EE}\u{1F1F3}\u{1F1FA}]|\u{1F1F3}[\u{1F1E6}\u{1F1E8}\u{1F1EA}-\u{1F1EC}\u{1F1EE}\u{1F1F1}\u{1F1F4}\u{1F1F5}\u{1F1F7}\u{1F1FA}\u{1F1FF}]|\u{1F1E8}[\u{1F1E6}\u{1F1E8}\u{1F1E9}\u{1F1EB}-\u{1F1EE}\u{1F1F0}-\u{1F1F5}\u{1F1F7}\u{1F1FA}-\u{1F1FF}]|\u{1F1F1}[\u{1F1E6}-\u{1F1E8}\u{1F1EE}\u{1F1F0}\u{1F1F7}-\u{1F1FB}\u{1F1FE}]|\u{1F1FF}[\u{1F1E6}\u{1F1F2}\u{1F1FC}]|\u{1F1FC}[\u{1F1EB}\u{1F1F8}]|\u{1F1FA}[\u{1F1E6}\u{1F1EC}\u{1F1F2}\u{1F1F3}\u{1F1F8}\u{1F1FE}\u{1F1FF}]|\u{1F1EE}[\u{1F1E8}-\u{1F1EA}\u{1F1F1}-\u{1F1F4}\u{1F1F6}-\u{1F1F9}]|\u{1F1EF}[\u{1F1EA}\u{1F1F2}\u{1F1F4}\u{1F1F5}]|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9CD}-\u{1F9CF}\u{1F9D6}-\u{1F9DD}][\u{1F3FB}-\u{1F3FF}]|[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}][\u{1F3FB}-\u{1F3FF}]|[\u261D\u270A-\u270D\u{1F385}\u{1F3C2}\u{1F3C7}\u{1F442}\u{1F443}\u{1F446}-\u{1F450}\u{1F466}\u{1F467}\u{1F46B}-\u{1F46D}\u{1F470}\u{1F472}\u{1F474}-\u{1F476}\u{1F478}\u{1F47C}\u{1F483}\u{1F485}\u{1F4AA}\u{1F574}\u{1F57A}\u{1F590}\u{1F595}\u{1F596}\u{1F64C}\u{1F64F}\u{1F6C0}\u{1F6CC}\u{1F90F}\u{1F918}-\u{1F91C}\u{1F91E}\u{1F91F}\u{1F930}-\u{1F936}\u{1F9B5}\u{1F9B6}\u{1F9BB}\u{1F9D2}-\u{1F9D5}][\u{1F3FB}-\u{1F3FF}]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55\u{1F004}\u{1F0CF}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E6}-\u{1F1FF}\u{1F201}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F236}\u{1F238}-\u{1F23A}\u{1F250}\u{1F251}\u{1F300}-\u{1F320}\u{1F32D}-\u{1F335}\u{1F337}-\u{1F37C}\u{1F37E}-\u{1F393}\u{1F3A0}-\u{1F3CA}\u{1F3CF}-\u{1F3D3}\u{1F3E0}-\u{1F3F0}\u{1F3F4}\u{1F3F8}-\u{1F43E}\u{1F440}\u{1F442}-\u{1F4FC}\u{1F4FF}-\u{1F53D}\u{1F54B}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F57A}\u{1F595}\u{1F596}\u{1F5A4}\u{1F5FB}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CC}\u{1F6D0}-\u{1F6D2}\u{1F6D5}\u{1F6EB}\u{1F6EC}\u{1F6F4}-\u{1F6FA}\u{1F7E0}-\u{1F7EB}\u{1F90D}-\u{1F93A}\u{1F93C}-\u{1F945}\u{1F947}-\u{1F971}\u{1F973}-\u{1F976}\u{1F97A}-\u{1F9A2}\u{1F9A5}-\u{1F9AA}\u{1F9AE}-\u{1F9CA}\u{1F9CD}-\u{1F9FF}\u{1FA70}-\u{1FA73}\u{1FA78}-\u{1FA7A}\u{1FA80}-\u{1FA82}\u{1FA90}-\u{1FA95}]|[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299\u{1F004}\u{1F0CF}\u{1F170}\u{1F171}\u{1F17E}\u{1F17F}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E6}-\u{1F1FF}\u{1F201}\u{1F202}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F23A}\u{1F250}\u{1F251}\u{1F300}-\u{1F321}\u{1F324}-\u{1F393}\u{1F396}\u{1F397}\u{1F399}-\u{1F39B}\u{1F39E}-\u{1F3F0}\u{1F3F3}-\u{1F3F5}\u{1F3F7}-\u{1F4FD}\u{1F4FF}-\u{1F53D}\u{1F549}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F56F}\u{1F570}\u{1F573}-\u{1F57A}\u{1F587}\u{1F58A}-\u{1F58D}\u{1F590}\u{1F595}\u{1F596}\u{1F5A4}\u{1F5A5}\u{1F5A8}\u{1F5B1}\u{1F5B2}\u{1F5BC}\u{1F5C2}-\u{1F5C4}\u{1F5D1}-\u{1F5D3}\u{1F5DC}-\u{1F5DE}\u{1F5E1}\u{1F5E3}\u{1F5E8}\u{1F5EF}\u{1F5F3}\u{1F5FA}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CB}-\u{1F6D2}\u{1F6D5}\u{1F6E0}-\u{1F6E5}\u{1F6E9}\u{1F6EB}\u{1F6EC}\u{1F6F0}\u{1F6F3}-\u{1F6FA}\u{1F7E0}-\u{1F7EB}\u{1F90D}-\u{1F93A}\u{1F93C}-\u{1F945}\u{1F947}-\u{1F971}\u{1F973}-\u{1F976}\u{1F97A}-\u{1F9A2}\u{1F9A5}-\u{1F9AA}\u{1F9AE}-\u{1F9CA}\u{1F9CD}-\u{1F9FF}\u{1FA70}-\u{1FA73}\u{1FA78}-\u{1FA7A}\u{1FA80}-\u{1FA82}\u{1FA90}-\u{1FA95}]\uFE0F|[\u261D\u26F9\u270A-\u270D\u{1F385}\u{1F3C2}-\u{1F3C4}\u{1F3C7}\u{1F3CA}-\u{1F3CC}\u{1F442}\u{1F443}\u{1F446}-\u{1F450}\u{1F466}-\u{1F478}\u{1F47C}\u{1F481}-\u{1F483}\u{1F485}-\u{1F487}\u{1F48F}\u{1F491}\u{1F4AA}\u{1F574}\u{1F575}\u{1F57A}\u{1F590}\u{1F595}\u{1F596}\u{1F645}-\u{1F647}\u{1F64B}-\u{1F64F}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F6C0}\u{1F6CC}\u{1F90F}\u{1F918}-\u{1F91F}\u{1F926}\u{1F930}-\u{1F939}\u{1F93C}-\u{1F93E}\u{1F9B5}\u{1F9B6}\u{1F9B8}\u{1F9B9}\u{1F9BB}\u{1F9CD}-\u{1F9CF}\u{1F9D1}-\u{1F9DD}]/gu; +}; diff --git a/website/node_modules/ansi-align/node_modules/emoji-regex/es2015/text.js b/website/node_modules/ansi-align/node_modules/emoji-regex/es2015/text.js new file mode 100644 index 00000000..780309df --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/emoji-regex/es2015/text.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = () => { + // https://mths.be/emoji + return /\u{1F3F4}\u{E0067}\u{E0062}(?:\u{E0065}\u{E006E}\u{E0067}|\u{E0073}\u{E0063}\u{E0074}|\u{E0077}\u{E006C}\u{E0073})\u{E007F}|\u{1F468}(?:\u{1F3FC}\u200D(?:\u{1F91D}\u200D\u{1F468}\u{1F3FB}|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FF}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}-\u{1F3FE}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FE}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}-\u{1F3FD}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FD}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}\u{1F3FC}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D)?\u{1F468}|[\u{1F468}\u{1F469}]\u200D(?:\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}])|\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}]|[\u{1F468}\u{1F469}]\u200D[\u{1F466}\u{1F467}]|[\u2695\u2696\u2708]\uFE0F|[\u{1F466}\u{1F467}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|(?:\u{1F3FB}\u200D[\u2695\u2696\u2708]|\u{1F3FF}\u200D[\u2695\u2696\u2708]|\u{1F3FE}\u200D[\u2695\u2696\u2708]|\u{1F3FD}\u200D[\u2695\u2696\u2708]|\u{1F3FC}\u200D[\u2695\u2696\u2708])\uFE0F|\u{1F3FB}\u200D[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]|[\u{1F3FB}-\u{1F3FF}])|(?:\u{1F9D1}\u{1F3FB}\u200D\u{1F91D}\u200D\u{1F9D1}|\u{1F469}\u{1F3FC}\u200D\u{1F91D}\u200D\u{1F469})\u{1F3FB}|\u{1F9D1}(?:\u{1F3FF}\u200D\u{1F91D}\u200D\u{1F9D1}[\u{1F3FB}-\u{1F3FF}]|\u200D\u{1F91D}\u200D\u{1F9D1})|(?:\u{1F9D1}\u{1F3FE}\u200D\u{1F91D}\u200D\u{1F9D1}|\u{1F469}\u{1F3FF}\u200D\u{1F91D}\u200D[\u{1F468}\u{1F469}])[\u{1F3FB}-\u{1F3FE}]|(?:\u{1F9D1}\u{1F3FC}\u200D\u{1F91D}\u200D\u{1F9D1}|\u{1F469}\u{1F3FD}\u200D\u{1F91D}\u200D\u{1F469})[\u{1F3FB}\u{1F3FC}]|\u{1F469}(?:\u{1F3FE}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}-\u{1F3FD}\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FC}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}\u{1F3FD}-\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FB}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FC}-\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FD}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}\u{1F3FC}\u{1F3FE}\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D[\u{1F468}\u{1F469}]|[\u{1F468}\u{1F469}])|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FF}\u200D[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F469}\u200D\u{1F469}\u200D(?:\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}])|(?:\u{1F9D1}\u{1F3FD}\u200D\u{1F91D}\u200D\u{1F9D1}|\u{1F469}\u{1F3FE}\u200D\u{1F91D}\u200D\u{1F469})[\u{1F3FB}-\u{1F3FD}]|\u{1F469}\u200D\u{1F466}\u200D\u{1F466}|\u{1F469}\u200D\u{1F469}\u200D[\u{1F466}\u{1F467}]|(?:\u{1F441}\uFE0F\u200D\u{1F5E8}|\u{1F469}(?:\u{1F3FF}\u200D[\u2695\u2696\u2708]|\u{1F3FE}\u200D[\u2695\u2696\u2708]|\u{1F3FC}\u200D[\u2695\u2696\u2708]|\u{1F3FB}\u200D[\u2695\u2696\u2708]|\u{1F3FD}\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}]\uFE0F|[\u{1F46F}\u{1F93C}\u{1F9DE}\u{1F9DF}])\u200D[\u2640\u2642]|[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}][\u{1F3FB}-\u{1F3FF}]\u200D[\u2640\u2642]|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9CD}-\u{1F9CF}\u{1F9D6}-\u{1F9DD}](?:[\u{1F3FB}-\u{1F3FF}]\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\u{1F3F4}\u200D\u2620)\uFE0F|\u{1F469}\u200D\u{1F467}\u200D[\u{1F466}\u{1F467}]|\u{1F3F3}\uFE0F\u200D\u{1F308}|\u{1F415}\u200D\u{1F9BA}|\u{1F469}\u200D\u{1F466}|\u{1F469}\u200D\u{1F467}|\u{1F1FD}\u{1F1F0}|\u{1F1F4}\u{1F1F2}|\u{1F1F6}\u{1F1E6}|[#\*0-9]\uFE0F\u20E3|\u{1F1E7}[\u{1F1E6}\u{1F1E7}\u{1F1E9}-\u{1F1EF}\u{1F1F1}-\u{1F1F4}\u{1F1F6}-\u{1F1F9}\u{1F1FB}\u{1F1FC}\u{1F1FE}\u{1F1FF}]|\u{1F1F9}[\u{1F1E6}\u{1F1E8}\u{1F1E9}\u{1F1EB}-\u{1F1ED}\u{1F1EF}-\u{1F1F4}\u{1F1F7}\u{1F1F9}\u{1F1FB}\u{1F1FC}\u{1F1FF}]|\u{1F1EA}[\u{1F1E6}\u{1F1E8}\u{1F1EA}\u{1F1EC}\u{1F1ED}\u{1F1F7}-\u{1F1FA}]|\u{1F9D1}[\u{1F3FB}-\u{1F3FF}]|\u{1F1F7}[\u{1F1EA}\u{1F1F4}\u{1F1F8}\u{1F1FA}\u{1F1FC}]|\u{1F469}[\u{1F3FB}-\u{1F3FF}]|\u{1F1F2}[\u{1F1E6}\u{1F1E8}-\u{1F1ED}\u{1F1F0}-\u{1F1FF}]|\u{1F1E6}[\u{1F1E8}-\u{1F1EC}\u{1F1EE}\u{1F1F1}\u{1F1F2}\u{1F1F4}\u{1F1F6}-\u{1F1FA}\u{1F1FC}\u{1F1FD}\u{1F1FF}]|\u{1F1F0}[\u{1F1EA}\u{1F1EC}-\u{1F1EE}\u{1F1F2}\u{1F1F3}\u{1F1F5}\u{1F1F7}\u{1F1FC}\u{1F1FE}\u{1F1FF}]|\u{1F1ED}[\u{1F1F0}\u{1F1F2}\u{1F1F3}\u{1F1F7}\u{1F1F9}\u{1F1FA}]|\u{1F1E9}[\u{1F1EA}\u{1F1EC}\u{1F1EF}\u{1F1F0}\u{1F1F2}\u{1F1F4}\u{1F1FF}]|\u{1F1FE}[\u{1F1EA}\u{1F1F9}]|\u{1F1EC}[\u{1F1E6}\u{1F1E7}\u{1F1E9}-\u{1F1EE}\u{1F1F1}-\u{1F1F3}\u{1F1F5}-\u{1F1FA}\u{1F1FC}\u{1F1FE}]|\u{1F1F8}[\u{1F1E6}-\u{1F1EA}\u{1F1EC}-\u{1F1F4}\u{1F1F7}-\u{1F1F9}\u{1F1FB}\u{1F1FD}-\u{1F1FF}]|\u{1F1EB}[\u{1F1EE}-\u{1F1F0}\u{1F1F2}\u{1F1F4}\u{1F1F7}]|\u{1F1F5}[\u{1F1E6}\u{1F1EA}-\u{1F1ED}\u{1F1F0}-\u{1F1F3}\u{1F1F7}-\u{1F1F9}\u{1F1FC}\u{1F1FE}]|\u{1F1FB}[\u{1F1E6}\u{1F1E8}\u{1F1EA}\u{1F1EC}\u{1F1EE}\u{1F1F3}\u{1F1FA}]|\u{1F1F3}[\u{1F1E6}\u{1F1E8}\u{1F1EA}-\u{1F1EC}\u{1F1EE}\u{1F1F1}\u{1F1F4}\u{1F1F5}\u{1F1F7}\u{1F1FA}\u{1F1FF}]|\u{1F1E8}[\u{1F1E6}\u{1F1E8}\u{1F1E9}\u{1F1EB}-\u{1F1EE}\u{1F1F0}-\u{1F1F5}\u{1F1F7}\u{1F1FA}-\u{1F1FF}]|\u{1F1F1}[\u{1F1E6}-\u{1F1E8}\u{1F1EE}\u{1F1F0}\u{1F1F7}-\u{1F1FB}\u{1F1FE}]|\u{1F1FF}[\u{1F1E6}\u{1F1F2}\u{1F1FC}]|\u{1F1FC}[\u{1F1EB}\u{1F1F8}]|\u{1F1FA}[\u{1F1E6}\u{1F1EC}\u{1F1F2}\u{1F1F3}\u{1F1F8}\u{1F1FE}\u{1F1FF}]|\u{1F1EE}[\u{1F1E8}-\u{1F1EA}\u{1F1F1}-\u{1F1F4}\u{1F1F6}-\u{1F1F9}]|\u{1F1EF}[\u{1F1EA}\u{1F1F2}\u{1F1F4}\u{1F1F5}]|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9CD}-\u{1F9CF}\u{1F9D6}-\u{1F9DD}][\u{1F3FB}-\u{1F3FF}]|[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}][\u{1F3FB}-\u{1F3FF}]|[\u261D\u270A-\u270D\u{1F385}\u{1F3C2}\u{1F3C7}\u{1F442}\u{1F443}\u{1F446}-\u{1F450}\u{1F466}\u{1F467}\u{1F46B}-\u{1F46D}\u{1F470}\u{1F472}\u{1F474}-\u{1F476}\u{1F478}\u{1F47C}\u{1F483}\u{1F485}\u{1F4AA}\u{1F574}\u{1F57A}\u{1F590}\u{1F595}\u{1F596}\u{1F64C}\u{1F64F}\u{1F6C0}\u{1F6CC}\u{1F90F}\u{1F918}-\u{1F91C}\u{1F91E}\u{1F91F}\u{1F930}-\u{1F936}\u{1F9B5}\u{1F9B6}\u{1F9BB}\u{1F9D2}-\u{1F9D5}][\u{1F3FB}-\u{1F3FF}]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55\u{1F004}\u{1F0CF}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E6}-\u{1F1FF}\u{1F201}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F236}\u{1F238}-\u{1F23A}\u{1F250}\u{1F251}\u{1F300}-\u{1F320}\u{1F32D}-\u{1F335}\u{1F337}-\u{1F37C}\u{1F37E}-\u{1F393}\u{1F3A0}-\u{1F3CA}\u{1F3CF}-\u{1F3D3}\u{1F3E0}-\u{1F3F0}\u{1F3F4}\u{1F3F8}-\u{1F43E}\u{1F440}\u{1F442}-\u{1F4FC}\u{1F4FF}-\u{1F53D}\u{1F54B}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F57A}\u{1F595}\u{1F596}\u{1F5A4}\u{1F5FB}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CC}\u{1F6D0}-\u{1F6D2}\u{1F6D5}\u{1F6EB}\u{1F6EC}\u{1F6F4}-\u{1F6FA}\u{1F7E0}-\u{1F7EB}\u{1F90D}-\u{1F93A}\u{1F93C}-\u{1F945}\u{1F947}-\u{1F971}\u{1F973}-\u{1F976}\u{1F97A}-\u{1F9A2}\u{1F9A5}-\u{1F9AA}\u{1F9AE}-\u{1F9CA}\u{1F9CD}-\u{1F9FF}\u{1FA70}-\u{1FA73}\u{1FA78}-\u{1FA7A}\u{1FA80}-\u{1FA82}\u{1FA90}-\u{1FA95}]|[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299\u{1F004}\u{1F0CF}\u{1F170}\u{1F171}\u{1F17E}\u{1F17F}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E6}-\u{1F1FF}\u{1F201}\u{1F202}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F23A}\u{1F250}\u{1F251}\u{1F300}-\u{1F321}\u{1F324}-\u{1F393}\u{1F396}\u{1F397}\u{1F399}-\u{1F39B}\u{1F39E}-\u{1F3F0}\u{1F3F3}-\u{1F3F5}\u{1F3F7}-\u{1F4FD}\u{1F4FF}-\u{1F53D}\u{1F549}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F56F}\u{1F570}\u{1F573}-\u{1F57A}\u{1F587}\u{1F58A}-\u{1F58D}\u{1F590}\u{1F595}\u{1F596}\u{1F5A4}\u{1F5A5}\u{1F5A8}\u{1F5B1}\u{1F5B2}\u{1F5BC}\u{1F5C2}-\u{1F5C4}\u{1F5D1}-\u{1F5D3}\u{1F5DC}-\u{1F5DE}\u{1F5E1}\u{1F5E3}\u{1F5E8}\u{1F5EF}\u{1F5F3}\u{1F5FA}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CB}-\u{1F6D2}\u{1F6D5}\u{1F6E0}-\u{1F6E5}\u{1F6E9}\u{1F6EB}\u{1F6EC}\u{1F6F0}\u{1F6F3}-\u{1F6FA}\u{1F7E0}-\u{1F7EB}\u{1F90D}-\u{1F93A}\u{1F93C}-\u{1F945}\u{1F947}-\u{1F971}\u{1F973}-\u{1F976}\u{1F97A}-\u{1F9A2}\u{1F9A5}-\u{1F9AA}\u{1F9AE}-\u{1F9CA}\u{1F9CD}-\u{1F9FF}\u{1FA70}-\u{1FA73}\u{1FA78}-\u{1FA7A}\u{1FA80}-\u{1FA82}\u{1FA90}-\u{1FA95}]\uFE0F?|[\u261D\u26F9\u270A-\u270D\u{1F385}\u{1F3C2}-\u{1F3C4}\u{1F3C7}\u{1F3CA}-\u{1F3CC}\u{1F442}\u{1F443}\u{1F446}-\u{1F450}\u{1F466}-\u{1F478}\u{1F47C}\u{1F481}-\u{1F483}\u{1F485}-\u{1F487}\u{1F48F}\u{1F491}\u{1F4AA}\u{1F574}\u{1F575}\u{1F57A}\u{1F590}\u{1F595}\u{1F596}\u{1F645}-\u{1F647}\u{1F64B}-\u{1F64F}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F6C0}\u{1F6CC}\u{1F90F}\u{1F918}-\u{1F91F}\u{1F926}\u{1F930}-\u{1F939}\u{1F93C}-\u{1F93E}\u{1F9B5}\u{1F9B6}\u{1F9B8}\u{1F9B9}\u{1F9BB}\u{1F9CD}-\u{1F9CF}\u{1F9D1}-\u{1F9DD}]/gu; +}; diff --git a/website/node_modules/ansi-align/node_modules/emoji-regex/index.d.ts b/website/node_modules/ansi-align/node_modules/emoji-regex/index.d.ts new file mode 100644 index 00000000..1955b470 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/emoji-regex/index.d.ts @@ -0,0 +1,23 @@ +declare module 'emoji-regex' { + function emojiRegex(): RegExp; + + export default emojiRegex; +} + +declare module 'emoji-regex/text' { + function emojiRegex(): RegExp; + + export default emojiRegex; +} + +declare module 'emoji-regex/es2015' { + function emojiRegex(): RegExp; + + export default emojiRegex; +} + +declare module 'emoji-regex/es2015/text' { + function emojiRegex(): RegExp; + + export default emojiRegex; +} diff --git a/website/node_modules/ansi-align/node_modules/emoji-regex/index.js b/website/node_modules/ansi-align/node_modules/emoji-regex/index.js new file mode 100644 index 00000000..d993a3a9 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/emoji-regex/index.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = function () { + // https://mths.be/emoji + return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\uD83D\uDC68(?:\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|[\u2695\u2696\u2708]\uFE0F|\uD83D[\uDC66\uDC67]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708])\uFE0F|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C[\uDFFB-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)\uD83C\uDFFB|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB\uDFFC])|\uD83D\uDC69(?:\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB-\uDFFD])|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)\uFE0F|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83C\uDFF4\u200D\u2620)\uFE0F|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF4\uD83C\uDDF2|\uD83C\uDDF6\uD83C\uDDE6|[#\*0-9]\uFE0F\u20E3|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270A-\u270D]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC70\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDCAA\uDD74\uDD7A\uDD90\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD36\uDDB5\uDDB6\uDDBB\uDDD2-\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5\uDEEB\uDEEC\uDEF4-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g; +}; diff --git a/website/node_modules/ansi-align/node_modules/emoji-regex/package.json b/website/node_modules/ansi-align/node_modules/emoji-regex/package.json new file mode 100644 index 00000000..6d323528 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/emoji-regex/package.json @@ -0,0 +1,50 @@ +{ + "name": "emoji-regex", + "version": "8.0.0", + "description": "A regular expression to match all Emoji-only symbols as per the Unicode Standard.", + "homepage": "https://mths.be/emoji-regex", + "main": "index.js", + "types": "index.d.ts", + "keywords": [ + "unicode", + "regex", + "regexp", + "regular expressions", + "code points", + "symbols", + "characters", + "emoji" + ], + "license": "MIT", + "author": { + "name": "Mathias Bynens", + "url": "https://mathiasbynens.be/" + }, + "repository": { + "type": "git", + "url": "https://github.com/mathiasbynens/emoji-regex.git" + }, + "bugs": "https://github.com/mathiasbynens/emoji-regex/issues", + "files": [ + "LICENSE-MIT.txt", + "index.js", + "index.d.ts", + "text.js", + "es2015/index.js", + "es2015/text.js" + ], + "scripts": { + "build": "rm -rf -- es2015; babel src -d .; NODE_ENV=es2015 babel src -d ./es2015; node script/inject-sequences.js", + "test": "mocha", + "test:watch": "npm run test -- --watch" + }, + "devDependencies": { + "@babel/cli": "^7.2.3", + "@babel/core": "^7.3.4", + "@babel/plugin-proposal-unicode-property-regex": "^7.2.0", + "@babel/preset-env": "^7.3.4", + "mocha": "^6.0.2", + "regexgen": "^1.3.0", + "unicode-12.0.0": "^0.7.9" + } +} diff --git a/website/node_modules/ansi-align/node_modules/emoji-regex/text.js b/website/node_modules/ansi-align/node_modules/emoji-regex/text.js new file mode 100644 index 00000000..0a55ce2f --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/emoji-regex/text.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = function () { + // https://mths.be/emoji + return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\uD83D\uDC68(?:\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|[\u2695\u2696\u2708]\uFE0F|\uD83D[\uDC66\uDC67]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708])\uFE0F|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C[\uDFFB-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)\uD83C\uDFFB|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB\uDFFC])|\uD83D\uDC69(?:\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB-\uDFFD])|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)\uFE0F|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83C\uDFF4\u200D\u2620)\uFE0F|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF4\uD83C\uDDF2|\uD83C\uDDF6\uD83C\uDDE6|[#\*0-9]\uFE0F\u20E3|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270A-\u270D]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC70\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDCAA\uDD74\uDD7A\uDD90\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD36\uDDB5\uDDB6\uDDBB\uDDD2-\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5\uDEEB\uDEEC\uDEF4-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])\uFE0F?|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g; +}; diff --git a/website/node_modules/ansi-align/node_modules/string-width/index.d.ts b/website/node_modules/ansi-align/node_modules/string-width/index.d.ts new file mode 100644 index 00000000..12b53097 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/string-width/index.d.ts @@ -0,0 +1,29 @@ +declare const stringWidth: { + /** + Get the visual width of a string - the number of columns required to display it. + + Some Unicode characters are [fullwidth](https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms) and use double the normal width. [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) are stripped and doesn't affect the width. + + @example + ``` + import stringWidth = require('string-width'); + + stringWidth('a'); + //=> 1 + + stringWidth('古'); + //=> 2 + + stringWidth('\u001B[1m古\u001B[22m'); + //=> 2 + ``` + */ + (string: string): number; + + // TODO: remove this in the next major version, refactor the whole definition to: + // declare function stringWidth(string: string): number; + // export = stringWidth; + default: typeof stringWidth; +} + +export = stringWidth; diff --git a/website/node_modules/ansi-align/node_modules/string-width/index.js b/website/node_modules/ansi-align/node_modules/string-width/index.js new file mode 100644 index 00000000..f4d261a9 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/string-width/index.js @@ -0,0 +1,47 @@ +'use strict'; +const stripAnsi = require('strip-ansi'); +const isFullwidthCodePoint = require('is-fullwidth-code-point'); +const emojiRegex = require('emoji-regex'); + +const stringWidth = string => { + if (typeof string !== 'string' || string.length === 0) { + return 0; + } + + string = stripAnsi(string); + + if (string.length === 0) { + return 0; + } + + string = string.replace(emojiRegex(), ' '); + + let width = 0; + + for (let i = 0; i < string.length; i++) { + const code = string.codePointAt(i); + + // Ignore control characters + if (code <= 0x1F || (code >= 0x7F && code <= 0x9F)) { + continue; + } + + // Ignore combining characters + if (code >= 0x300 && code <= 0x36F) { + continue; + } + + // Surrogates + if (code > 0xFFFF) { + i++; + } + + width += isFullwidthCodePoint(code) ? 2 : 1; + } + + return width; +}; + +module.exports = stringWidth; +// TODO: remove this in the next major version +module.exports.default = stringWidth; diff --git a/website/node_modules/ansi-align/node_modules/string-width/license b/website/node_modules/ansi-align/node_modules/string-width/license new file mode 100644 index 00000000..e7af2f77 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/string-width/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/ansi-align/node_modules/string-width/package.json b/website/node_modules/ansi-align/node_modules/string-width/package.json new file mode 100644 index 00000000..28ba7b4c --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/string-width/package.json @@ -0,0 +1,56 @@ +{ + "name": "string-width", + "version": "4.2.3", + "description": "Get the visual width of a string - the number of columns required to display it", + "license": "MIT", + "repository": "sindresorhus/string-width", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=8" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "string", + "character", + "unicode", + "width", + "visual", + "column", + "columns", + "fullwidth", + "full-width", + "full", + "ansi", + "escape", + "codes", + "cli", + "command-line", + "terminal", + "console", + "cjk", + "chinese", + "japanese", + "korean", + "fixed-width" + ], + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "devDependencies": { + "ava": "^1.4.1", + "tsd": "^0.7.1", + "xo": "^0.24.0" + } +} diff --git a/website/node_modules/ansi-align/node_modules/string-width/readme.md b/website/node_modules/ansi-align/node_modules/string-width/readme.md new file mode 100644 index 00000000..bdd31412 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/string-width/readme.md @@ -0,0 +1,50 @@ +# string-width + +> Get the visual width of a string - the number of columns required to display it + +Some Unicode characters are [fullwidth](https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms) and use double the normal width. [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) are stripped and doesn't affect the width. + +Useful to be able to measure the actual width of command-line output. + + +## Install + +``` +$ npm install string-width +``` + + +## Usage + +```js +const stringWidth = require('string-width'); + +stringWidth('a'); +//=> 1 + +stringWidth('古'); +//=> 2 + +stringWidth('\u001B[1m古\u001B[22m'); +//=> 2 +``` + + +## Related + +- [string-width-cli](https://github.com/sindresorhus/string-width-cli) - CLI for this module +- [string-length](https://github.com/sindresorhus/string-length) - Get the real length of a string +- [widest-line](https://github.com/sindresorhus/widest-line) - Get the visual width of the widest line in a string + + +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/website/node_modules/ansi-align/node_modules/strip-ansi/index.d.ts b/website/node_modules/ansi-align/node_modules/strip-ansi/index.d.ts new file mode 100644 index 00000000..907fccc2 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/strip-ansi/index.d.ts @@ -0,0 +1,17 @@ +/** +Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string. + +@example +``` +import stripAnsi = require('strip-ansi'); + +stripAnsi('\u001B[4mUnicorn\u001B[0m'); +//=> 'Unicorn' + +stripAnsi('\u001B]8;;https://github.com\u0007Click\u001B]8;;\u0007'); +//=> 'Click' +``` +*/ +declare function stripAnsi(string: string): string; + +export = stripAnsi; diff --git a/website/node_modules/ansi-align/node_modules/strip-ansi/index.js b/website/node_modules/ansi-align/node_modules/strip-ansi/index.js new file mode 100644 index 00000000..9a593dfc --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/strip-ansi/index.js @@ -0,0 +1,4 @@ +'use strict'; +const ansiRegex = require('ansi-regex'); + +module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; diff --git a/website/node_modules/ansi-align/node_modules/strip-ansi/license b/website/node_modules/ansi-align/node_modules/strip-ansi/license new file mode 100644 index 00000000..e7af2f77 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/strip-ansi/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/ansi-align/node_modules/strip-ansi/package.json b/website/node_modules/ansi-align/node_modules/strip-ansi/package.json new file mode 100644 index 00000000..1a41108d --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/strip-ansi/package.json @@ -0,0 +1,54 @@ +{ + "name": "strip-ansi", + "version": "6.0.1", + "description": "Strip ANSI escape codes from a string", + "license": "MIT", + "repository": "chalk/strip-ansi", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=8" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "strip", + "trim", + "remove", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "devDependencies": { + "ava": "^2.4.0", + "tsd": "^0.10.0", + "xo": "^0.25.3" + } +} diff --git a/website/node_modules/ansi-align/node_modules/strip-ansi/readme.md b/website/node_modules/ansi-align/node_modules/strip-ansi/readme.md new file mode 100644 index 00000000..7c4b56d4 --- /dev/null +++ b/website/node_modules/ansi-align/node_modules/strip-ansi/readme.md @@ -0,0 +1,46 @@ +# strip-ansi [![Build Status](https://travis-ci.org/chalk/strip-ansi.svg?branch=master)](https://travis-ci.org/chalk/strip-ansi) + +> Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string + + +## Install + +``` +$ npm install strip-ansi +``` + + +## Usage + +```js +const stripAnsi = require('strip-ansi'); + +stripAnsi('\u001B[4mUnicorn\u001B[0m'); +//=> 'Unicorn' + +stripAnsi('\u001B]8;;https://github.com\u0007Click\u001B]8;;\u0007'); +//=> 'Click' +``` + + +## strip-ansi for enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of strip-ansi and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-strip-ansi?utm_source=npm-strip-ansi&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + + +## Related + +- [strip-ansi-cli](https://github.com/chalk/strip-ansi-cli) - CLI for this module +- [strip-ansi-stream](https://github.com/chalk/strip-ansi-stream) - Streaming version of this module +- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes +- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes +- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right + + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + diff --git a/website/node_modules/ansi-align/package.json b/website/node_modules/ansi-align/package.json new file mode 100644 index 00000000..96a9471c --- /dev/null +++ b/website/node_modules/ansi-align/package.json @@ -0,0 +1,43 @@ +{ + "name": "ansi-align", + "version": "3.0.1", + "description": "align-text with ANSI support for CLIs", + "main": "index.js", + "scripts": { + "pretest": "standard", + "test": "nyc ava", + "coverage": "nyc report --reporter=text-lcov | coveralls", + "release": "standard-version" + }, + "files": [ + "index.js" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/nexdrew/ansi-align.git" + }, + "keywords": [ + "ansi", + "align", + "cli", + "center", + "pad" + ], + "author": "nexdrew", + "license": "ISC", + "bugs": { + "url": "https://github.com/nexdrew/ansi-align/issues" + }, + "homepage": "https://github.com/nexdrew/ansi-align#readme", + "dependencies": { + "string-width": "^4.1.0" + }, + "devDependencies": { + "ava": "^2.0.0", + "chalk": "^2.4.2", + "coveralls": "^3.0.3", + "nyc": "^14.0.0", + "standard": "^14.0.0", + "standard-version": "^7.0.0" + } +} diff --git a/website/node_modules/ansi-regex/index.d.ts b/website/node_modules/ansi-regex/index.d.ts new file mode 100644 index 00000000..7d562e9c --- /dev/null +++ b/website/node_modules/ansi-regex/index.d.ts @@ -0,0 +1,33 @@ +export type Options = { + /** + Match only the first ANSI escape. + + @default false + */ + readonly onlyFirst: boolean; +}; + +/** +Regular expression for matching ANSI escape codes. + +@example +``` +import ansiRegex from 'ansi-regex'; + +ansiRegex().test('\u001B[4mcake\u001B[0m'); +//=> true + +ansiRegex().test('cake'); +//=> false + +'\u001B[4mcake\u001B[0m'.match(ansiRegex()); +//=> ['\u001B[4m', '\u001B[0m'] + +'\u001B[4mcake\u001B[0m'.match(ansiRegex({onlyFirst: true})); +//=> ['\u001B[4m'] + +'\u001B]8;;https://github.com\u0007click\u001B]8;;\u0007'.match(ansiRegex()); +//=> ['\u001B]8;;https://github.com\u0007', '\u001B]8;;\u0007'] +``` +*/ +export default function ansiRegex(options?: Options): RegExp; diff --git a/website/node_modules/ansi-regex/index.js b/website/node_modules/ansi-regex/index.js new file mode 100644 index 00000000..2cc5ca24 --- /dev/null +++ b/website/node_modules/ansi-regex/index.js @@ -0,0 +1,14 @@ +export default function ansiRegex({onlyFirst = false} = {}) { + // Valid string terminator sequences are BEL, ESC\, and 0x9c + const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)'; + + // OSC sequences only: ESC ] ... ST (non-greedy until the first ST) + const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`; + + // CSI and related: ESC/C1, optional intermediates, optional params (supports ; and :) then final byte + const csi = '[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]'; + + const pattern = `${osc}|${csi}`; + + return new RegExp(pattern, onlyFirst ? undefined : 'g'); +} diff --git a/website/node_modules/ansi-regex/license b/website/node_modules/ansi-regex/license new file mode 100644 index 00000000..fa7ceba3 --- /dev/null +++ b/website/node_modules/ansi-regex/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/ansi-regex/package.json b/website/node_modules/ansi-regex/package.json new file mode 100644 index 00000000..2efe9ebb --- /dev/null +++ b/website/node_modules/ansi-regex/package.json @@ -0,0 +1,61 @@ +{ + "name": "ansi-regex", + "version": "6.2.2", + "description": "Regular expression for matching ANSI escape codes", + "license": "MIT", + "repository": "chalk/ansi-regex", + "funding": "https://github.com/chalk/ansi-regex?sponsor=1", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "type": "module", + "exports": "./index.js", + "types": "./index.d.ts", + "sideEffects": false, + "engines": { + "node": ">=12" + }, + "scripts": { + "test": "xo && ava && tsd", + "view-supported": "node fixtures/view-codes.js" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "devDependencies": { + "ansi-escapes": "^5.0.0", + "ava": "^3.15.0", + "tsd": "^0.21.0", + "xo": "^0.54.2" + } +} diff --git a/website/node_modules/ansi-regex/readme.md b/website/node_modules/ansi-regex/readme.md new file mode 100644 index 00000000..4d3c415a --- /dev/null +++ b/website/node_modules/ansi-regex/readme.md @@ -0,0 +1,66 @@ +# ansi-regex + +> Regular expression for matching [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) + +## Install + +```sh +npm install ansi-regex +``` + +## Usage + +```js +import ansiRegex from 'ansi-regex'; + +ansiRegex().test('\u001B[4mcake\u001B[0m'); +//=> true + +ansiRegex().test('cake'); +//=> false + +'\u001B[4mcake\u001B[0m'.match(ansiRegex()); +//=> ['\u001B[4m', '\u001B[0m'] + +'\u001B[4mcake\u001B[0m'.match(ansiRegex({onlyFirst: true})); +//=> ['\u001B[4m'] + +'\u001B]8;;https://github.com\u0007click\u001B]8;;\u0007'.match(ansiRegex()); +//=> ['\u001B]8;;https://github.com\u0007', '\u001B]8;;\u0007'] +``` + +## API + +### ansiRegex(options?) + +Returns a regex for matching ANSI escape codes. + +#### options + +Type: `object` + +##### onlyFirst + +Type: `boolean`\ +Default: `false` *(Matches any ANSI escape codes in a string)* + +Match only the first ANSI escape. + +## Important + +If you run the regex against untrusted user input in a server context, you should [give it a timeout](https://github.com/sindresorhus/super-regex). + +**I do not consider [ReDoS](https://blog.yossarian.net/2022/12/28/ReDoS-vulnerabilities-and-misaligned-incentives) a valid vulnerability for this package.** + +## FAQ + +### Why do you test for codes not in the ECMA 48 standard? + +Some of the codes we run as a test are codes that we acquired finding various lists of non-standard or manufacturer specific codes. We test for both standard and non-standard codes, as most of them follow the same or similar format and can be safely matched in strings without the risk of removing actual string content. There are a few non-standard control codes that do not follow the traditional format (i.e. they end in numbers) thus forcing us to exclude them from the test because we cannot reliably match them. + +On the historical side, those ECMA standards were established in the early 90's whereas the VT100, for example, was designed in the mid/late 70's. At that point in time, control codes were still pretty ungoverned and engineers used them for a multitude of things, namely to activate hardware ports that may have been proprietary. Somewhere else you see a similar 'anarchy' of codes is in the x86 architecture for processors; there are a ton of "interrupts" that can mean different things on certain brands of processors, most of which have been phased out. + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) diff --git a/website/node_modules/ansi-styles/index.d.ts b/website/node_modules/ansi-styles/index.d.ts new file mode 100644 index 00000000..ee8bc27c --- /dev/null +++ b/website/node_modules/ansi-styles/index.d.ts @@ -0,0 +1,236 @@ +export type CSPair = { // eslint-disable-line @typescript-eslint/naming-convention + /** + The ANSI terminal control sequence for starting this style. + */ + readonly open: string; + + /** + The ANSI terminal control sequence for ending this style. + */ + readonly close: string; +}; + +export type ColorBase = { + /** + The ANSI terminal control sequence for ending this color. + */ + readonly close: string; + + ansi(code: number): string; + + ansi256(code: number): string; + + ansi16m(red: number, green: number, blue: number): string; +}; + +export type Modifier = { + /** + Resets the current color chain. + */ + readonly reset: CSPair; + + /** + Make text bold. + */ + readonly bold: CSPair; + + /** + Emitting only a small amount of light. + */ + readonly dim: CSPair; + + /** + Make text italic. (Not widely supported) + */ + readonly italic: CSPair; + + /** + Make text underline. (Not widely supported) + */ + readonly underline: CSPair; + + /** + Make text overline. + + Supported on VTE-based terminals, the GNOME terminal, mintty, and Git Bash. + */ + readonly overline: CSPair; + + /** + Inverse background and foreground colors. + */ + readonly inverse: CSPair; + + /** + Prints the text, but makes it invisible. + */ + readonly hidden: CSPair; + + /** + Puts a horizontal line through the center of the text. (Not widely supported) + */ + readonly strikethrough: CSPair; +}; + +export type ForegroundColor = { + readonly black: CSPair; + readonly red: CSPair; + readonly green: CSPair; + readonly yellow: CSPair; + readonly blue: CSPair; + readonly cyan: CSPair; + readonly magenta: CSPair; + readonly white: CSPair; + + /** + Alias for `blackBright`. + */ + readonly gray: CSPair; + + /** + Alias for `blackBright`. + */ + readonly grey: CSPair; + + readonly blackBright: CSPair; + readonly redBright: CSPair; + readonly greenBright: CSPair; + readonly yellowBright: CSPair; + readonly blueBright: CSPair; + readonly cyanBright: CSPair; + readonly magentaBright: CSPair; + readonly whiteBright: CSPair; +}; + +export type BackgroundColor = { + readonly bgBlack: CSPair; + readonly bgRed: CSPair; + readonly bgGreen: CSPair; + readonly bgYellow: CSPair; + readonly bgBlue: CSPair; + readonly bgCyan: CSPair; + readonly bgMagenta: CSPair; + readonly bgWhite: CSPair; + + /** + Alias for `bgBlackBright`. + */ + readonly bgGray: CSPair; + + /** + Alias for `bgBlackBright`. + */ + readonly bgGrey: CSPair; + + readonly bgBlackBright: CSPair; + readonly bgRedBright: CSPair; + readonly bgGreenBright: CSPair; + readonly bgYellowBright: CSPair; + readonly bgBlueBright: CSPair; + readonly bgCyanBright: CSPair; + readonly bgMagentaBright: CSPair; + readonly bgWhiteBright: CSPair; +}; + +export type ConvertColor = { + /** + Convert from the RGB color space to the ANSI 256 color space. + + @param red - (`0...255`) + @param green - (`0...255`) + @param blue - (`0...255`) + */ + rgbToAnsi256(red: number, green: number, blue: number): number; + + /** + Convert from the RGB HEX color space to the RGB color space. + + @param hex - A hexadecimal string containing RGB data. + */ + hexToRgb(hex: string): [red: number, green: number, blue: number]; + + /** + Convert from the RGB HEX color space to the ANSI 256 color space. + + @param hex - A hexadecimal string containing RGB data. + */ + hexToAnsi256(hex: string): number; + + /** + Convert from the ANSI 256 color space to the ANSI 16 color space. + + @param code - A number representing the ANSI 256 color. + */ + ansi256ToAnsi(code: number): number; + + /** + Convert from the RGB color space to the ANSI 16 color space. + + @param red - (`0...255`) + @param green - (`0...255`) + @param blue - (`0...255`) + */ + rgbToAnsi(red: number, green: number, blue: number): number; + + /** + Convert from the RGB HEX color space to the ANSI 16 color space. + + @param hex - A hexadecimal string containing RGB data. + */ + hexToAnsi(hex: string): number; +}; + +/** +Basic modifier names. +*/ +export type ModifierName = keyof Modifier; + +/** +Basic foreground color names. + +[More colors here.](https://github.com/chalk/chalk/blob/main/readme.md#256-and-truecolor-color-support) +*/ +export type ForegroundColorName = keyof ForegroundColor; + +/** +Basic background color names. + +[More colors here.](https://github.com/chalk/chalk/blob/main/readme.md#256-and-truecolor-color-support) +*/ +export type BackgroundColorName = keyof BackgroundColor; + +/** +Basic color names. The combination of foreground and background color names. + +[More colors here.](https://github.com/chalk/chalk/blob/main/readme.md#256-and-truecolor-color-support) +*/ +export type ColorName = ForegroundColorName | BackgroundColorName; + +/** +Basic modifier names. +*/ +export const modifierNames: readonly ModifierName[]; + +/** +Basic foreground color names. +*/ +export const foregroundColorNames: readonly ForegroundColorName[]; + +/** +Basic background color names. +*/ +export const backgroundColorNames: readonly BackgroundColorName[]; + +/* +Basic color names. The combination of foreground and background color names. +*/ +export const colorNames: readonly ColorName[]; + +declare const ansiStyles: { + readonly modifier: Modifier; + readonly color: ColorBase & ForegroundColor; + readonly bgColor: ColorBase & BackgroundColor; + readonly codes: ReadonlyMap; +} & ForegroundColor & BackgroundColor & Modifier & ConvertColor; + +export default ansiStyles; diff --git a/website/node_modules/ansi-styles/index.js b/website/node_modules/ansi-styles/index.js new file mode 100644 index 00000000..eaa7bed6 --- /dev/null +++ b/website/node_modules/ansi-styles/index.js @@ -0,0 +1,223 @@ +const ANSI_BACKGROUND_OFFSET = 10; + +const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`; + +const wrapAnsi256 = (offset = 0) => code => `\u001B[${38 + offset};5;${code}m`; + +const wrapAnsi16m = (offset = 0) => (red, green, blue) => `\u001B[${38 + offset};2;${red};${green};${blue}m`; + +const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + overline: [53, 55], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29], + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + + // Bright color + blackBright: [90, 39], + gray: [90, 39], // Alias of `blackBright` + grey: [90, 39], // Alias of `blackBright` + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39], + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], + + // Bright color + bgBlackBright: [100, 49], + bgGray: [100, 49], // Alias of `bgBlackBright` + bgGrey: [100, 49], // Alias of `bgBlackBright` + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49], + }, +}; + +export const modifierNames = Object.keys(styles.modifier); +export const foregroundColorNames = Object.keys(styles.color); +export const backgroundColorNames = Object.keys(styles.bgColor); +export const colorNames = [...foregroundColorNames, ...backgroundColorNames]; + +function assembleStyles() { + const codes = new Map(); + + for (const [groupName, group] of Object.entries(styles)) { + for (const [styleName, style] of Object.entries(group)) { + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m`, + }; + + group[styleName] = styles[styleName]; + + codes.set(style[0], style[1]); + } + + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false, + }); + } + + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false, + }); + + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; + + styles.color.ansi = wrapAnsi16(); + styles.color.ansi256 = wrapAnsi256(); + styles.color.ansi16m = wrapAnsi16m(); + styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET); + styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET); + styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET); + + // From https://github.com/Qix-/color-convert/blob/3f0e0d4e92e235796ccb17f6e85c72094a651f49/conversions.js + Object.defineProperties(styles, { + rgbToAnsi256: { + value(red, green, blue) { + // We use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (red === green && green === blue) { + if (red < 8) { + return 16; + } + + if (red > 248) { + return 231; + } + + return Math.round(((red - 8) / 247) * 24) + 232; + } + + return 16 + + (36 * Math.round(red / 255 * 5)) + + (6 * Math.round(green / 255 * 5)) + + Math.round(blue / 255 * 5); + }, + enumerable: false, + }, + hexToRgb: { + value(hex) { + const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16)); + if (!matches) { + return [0, 0, 0]; + } + + let [colorString] = matches; + + if (colorString.length === 3) { + colorString = [...colorString].map(character => character + character).join(''); + } + + const integer = Number.parseInt(colorString, 16); + + return [ + /* eslint-disable no-bitwise */ + (integer >> 16) & 0xFF, + (integer >> 8) & 0xFF, + integer & 0xFF, + /* eslint-enable no-bitwise */ + ]; + }, + enumerable: false, + }, + hexToAnsi256: { + value: hex => styles.rgbToAnsi256(...styles.hexToRgb(hex)), + enumerable: false, + }, + ansi256ToAnsi: { + value(code) { + if (code < 8) { + return 30 + code; + } + + if (code < 16) { + return 90 + (code - 8); + } + + let red; + let green; + let blue; + + if (code >= 232) { + red = (((code - 232) * 10) + 8) / 255; + green = red; + blue = red; + } else { + code -= 16; + + const remainder = code % 36; + + red = Math.floor(code / 36) / 5; + green = Math.floor(remainder / 6) / 5; + blue = (remainder % 6) / 5; + } + + const value = Math.max(red, green, blue) * 2; + + if (value === 0) { + return 30; + } + + // eslint-disable-next-line no-bitwise + let result = 30 + ((Math.round(blue) << 2) | (Math.round(green) << 1) | Math.round(red)); + + if (value === 2) { + result += 60; + } + + return result; + }, + enumerable: false, + }, + rgbToAnsi: { + value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)), + enumerable: false, + }, + hexToAnsi: { + value: hex => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)), + enumerable: false, + }, + }); + + return styles; +} + +const ansiStyles = assembleStyles(); + +export default ansiStyles; diff --git a/website/node_modules/ansi-styles/license b/website/node_modules/ansi-styles/license new file mode 100644 index 00000000..fa7ceba3 --- /dev/null +++ b/website/node_modules/ansi-styles/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/ansi-styles/package.json b/website/node_modules/ansi-styles/package.json new file mode 100644 index 00000000..16b508f0 --- /dev/null +++ b/website/node_modules/ansi-styles/package.json @@ -0,0 +1,54 @@ +{ + "name": "ansi-styles", + "version": "6.2.3", + "description": "ANSI escape codes for styling strings in the terminal", + "license": "MIT", + "repository": "chalk/ansi-styles", + "funding": "https://github.com/chalk/ansi-styles?sponsor=1", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "type": "module", + "exports": "./index.js", + "engines": { + "node": ">=12" + }, + "scripts": { + "test": "xo && ava && tsd", + "screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "devDependencies": { + "ava": "^6.1.3", + "svg-term-cli": "^2.1.1", + "tsd": "^0.31.1", + "xo": "^0.58.0" + } +} diff --git a/website/node_modules/ansi-styles/readme.md b/website/node_modules/ansi-styles/readme.md new file mode 100644 index 00000000..6d04183f --- /dev/null +++ b/website/node_modules/ansi-styles/readme.md @@ -0,0 +1,173 @@ +# ansi-styles + +> [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal + +You probably want the higher-level [chalk](https://github.com/chalk/chalk) module for styling your strings. + +![](screenshot.png) + +## Install + +```sh +npm install ansi-styles +``` + +## Usage + +```js +import styles from 'ansi-styles'; + +console.log(`${styles.green.open}Hello world!${styles.green.close}`); + + +// Color conversion between 256/truecolor +// NOTE: When converting from truecolor to 256 colors, the original color +// may be degraded to fit the new color palette. This means terminals +// that do not support 16 million colors will best-match the +// original color. +console.log(`${styles.color.ansi(styles.rgbToAnsi(199, 20, 250))}Hello World${styles.color.close}`) +console.log(`${styles.color.ansi256(styles.rgbToAnsi256(199, 20, 250))}Hello World${styles.color.close}`) +console.log(`${styles.color.ansi16m(...styles.hexToRgb('#abcdef'))}Hello World${styles.color.close}`) +``` + +## API + +### `open` and `close` + +Each style has an `open` and `close` property. + +### `modifierNames`, `foregroundColorNames`, `backgroundColorNames`, and `colorNames` + +All supported style strings are exposed as an array of strings for convenience. `colorNames` is the combination of `foregroundColorNames` and `backgroundColorNames`. + +This can be useful if you need to validate input: + +```js +import {modifierNames, foregroundColorNames} from 'ansi-styles'; + +console.log(modifierNames.includes('bold')); +//=> true + +console.log(foregroundColorNames.includes('pink')); +//=> false +``` + +## Styles + +### Modifiers + +- `reset` +- `bold` +- `dim` +- `italic` *(Not widely supported)* +- `underline` +- `overline` *Supported on VTE-based terminals, the GNOME terminal, mintty, and Git Bash.* +- `inverse` +- `hidden` +- `strikethrough` *(Not widely supported)* + +### Colors + +- `black` +- `red` +- `green` +- `yellow` +- `blue` +- `magenta` +- `cyan` +- `white` +- `blackBright` (alias: `gray`, `grey`) +- `redBright` +- `greenBright` +- `yellowBright` +- `blueBright` +- `magentaBright` +- `cyanBright` +- `whiteBright` + +### Background colors + +- `bgBlack` +- `bgRed` +- `bgGreen` +- `bgYellow` +- `bgBlue` +- `bgMagenta` +- `bgCyan` +- `bgWhite` +- `bgBlackBright` (alias: `bgGray`, `bgGrey`) +- `bgRedBright` +- `bgGreenBright` +- `bgYellowBright` +- `bgBlueBright` +- `bgMagentaBright` +- `bgCyanBright` +- `bgWhiteBright` + +## Advanced usage + +By default, you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module. + +- `styles.modifier` +- `styles.color` +- `styles.bgColor` + +###### Example + +```js +import styles from 'ansi-styles'; + +console.log(styles.color.green.open); +``` + +Raw escape codes (i.e. without the CSI escape prefix `\u001B[` and render mode postfix `m`) are available under `styles.codes`, which returns a `Map` with the open codes as keys and close codes as values. + +###### Example + +```js +import styles from 'ansi-styles'; + +console.log(styles.codes.get(36)); +//=> 39 +``` + +## 16 / 256 / 16 million (TrueColor) support + +`ansi-styles` allows converting between various color formats and ANSI escapes, with support for 16, 256 and [16 million colors](https://gist.github.com/XVilka/8346728). + +The following color spaces are supported: + +- `rgb` +- `hex` +- `ansi256` +- `ansi` + +To use these, call the associated conversion function with the intended output, for example: + +```js +import styles from 'ansi-styles'; + +styles.color.ansi(styles.rgbToAnsi(100, 200, 15)); // RGB to 16 color ansi foreground code +styles.bgColor.ansi(styles.hexToAnsi('#C0FFEE')); // HEX to 16 color ansi foreground code + +styles.color.ansi256(styles.rgbToAnsi256(100, 200, 15)); // RGB to 256 color ansi foreground code +styles.bgColor.ansi256(styles.hexToAnsi256('#C0FFEE')); // HEX to 256 color ansi foreground code + +styles.color.ansi16m(100, 200, 15); // RGB to 16 million color foreground code +styles.bgColor.ansi16m(...styles.hexToRgb('#C0FFEE')); // Hex (RGB) to 16 million color foreground code +``` + +## Related + +- [ansi-escapes](https://github.com/sindresorhus/ansi-escapes) - ANSI escape codes for manipulating the terminal + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + +## For enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of `ansi-styles` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-ansi-styles?utm_source=npm-ansi-styles&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) diff --git a/website/node_modules/arch/LICENSE b/website/node_modules/arch/LICENSE new file mode 100644 index 00000000..c7e68527 --- /dev/null +++ b/website/node_modules/arch/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/arch/README.md b/website/node_modules/arch/README.md new file mode 100644 index 00000000..8ab14a62 --- /dev/null +++ b/website/node_modules/arch/README.md @@ -0,0 +1,71 @@ +# arch [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url] + +[travis-image]: https://img.shields.io/travis/feross/arch/master.svg +[travis-url]: https://travis-ci.org/feross/arch +[npm-image]: https://img.shields.io/npm/v/arch.svg +[npm-url]: https://npmjs.org/package/arch +[downloads-image]: https://img.shields.io/npm/dm/arch.svg +[downloads-url]: https://npmjs.org/package/arch +[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg +[standard-url]: https://standardjs.com + +### Better `os.arch()` for node and the browser -- detect OS architecture + +[![Sauce Test Status](https://saucelabs.com/browser-matrix/arch2.svg)](https://saucelabs.com/u/arch2) + +This module is used by [WebTorrent Desktop](http://webtorrent.io/desktop) to +determine if the user is on a 32-bit vs. 64-bit operating system to offer the +right app installer. + +In Node.js, the `os.arch()` method (and `process.arch` property) returns a string +identifying the operating system CPU architecture **for which the Node.js binary +was compiled**. + +This is not the same as the **operating system CPU architecture**. For example, +you can run Node.js 32-bit on a 64-bit OS. In that situation, `os.arch()` will +return a misleading 'x86' (32-bit) value, instead of 'x64' (64-bit). + +Use this package to get the actual operating system CPU architecture. + +**BONUS: This package works in the browser too.** + +## install + +``` +npm install arch +``` + +## usage + +```js +var arch = require('arch') +console.log(arch()) // always returns 'x64' or 'x86' +``` + +In the browser, there is no spec that defines where this information lives, so we +check all known locations including `navigator.userAgent`, `navigator.platform`, +and `navigator.cpuClass` to make a best guess. + +If there is no *affirmative indication* that the architecture is 64-bit, then +32-bit will be assumed. This makes this package perfect for determining what +installer executable to offer to desktop app users. If there is ambiguity, then +the user will get the 32-bit installer, which will work fine even for a user with +a 64-bit OS. + +For reference, `x64` means 64-bit and `x86` means 32-bit. + +Here is some history behind these naming conventions: + +- https://en.wikipedia.org/wiki/X86 +- https://en.wikipedia.org/wiki/IA-32 +- https://en.wikipedia.org/wiki/X86-64 + +## Node.js proposal - `os.sysarch()` + +Note: There is +[a proposal](https://github.com/nodejs/node-v0.x-archive/issues/2862#issuecomment-103942051) +to add this functionality to Node.js as `os.sysarch()`. + +## license + +MIT. Copyright (c) [Feross Aboukhadijeh](http://feross.org). diff --git a/website/node_modules/arch/browser.js b/website/node_modules/arch/browser.js new file mode 100644 index 00000000..eca04d05 --- /dev/null +++ b/website/node_modules/arch/browser.js @@ -0,0 +1,43 @@ +module.exports = function arch () { + /** + * User agent strings that indicate a 64-bit OS. + * See: http://stackoverflow.com/a/13709431/292185 + */ + var userAgent = navigator.userAgent + if ([ + 'x86_64', + 'x86-64', + 'Win64', + 'x64;', + 'amd64', + 'AMD64', + 'WOW64', + 'x64_64' + ].some(function (str) { + return userAgent.indexOf(str) > -1 + })) { + return 'x64' + } + + /** + * Platform strings that indicate a 64-bit OS. + * See: http://stackoverflow.com/a/19883965/292185 + */ + var platform = navigator.platform + if (platform === 'MacIntel' || platform === 'Linux x86_64') { + return 'x64' + } + + /** + * CPU class strings that indicate a 64-bit OS. + * See: http://stackoverflow.com/a/6267019/292185 + */ + if (navigator.cpuClass === 'x64') { + return 'x64' + } + + /** + * If none of the above, assume the architecture is 32-bit. + */ + return 'x86' +} diff --git a/website/node_modules/arch/index.d.ts b/website/node_modules/arch/index.d.ts new file mode 100644 index 00000000..acbce662 --- /dev/null +++ b/website/node_modules/arch/index.d.ts @@ -0,0 +1,4 @@ + +declare function arch(): 'x64' | 'x86'; + +export = arch; diff --git a/website/node_modules/arch/index.js b/website/node_modules/arch/index.js new file mode 100644 index 00000000..7d90b8ad --- /dev/null +++ b/website/node_modules/arch/index.js @@ -0,0 +1,60 @@ +/*! arch. MIT License. Feross Aboukhadijeh */ +var cp = require('child_process') +var fs = require('fs') +var path = require('path') + +/** + * Returns the operating system's CPU architecture. This is different than + * `process.arch` or `os.arch()` which returns the architecture the Node.js (or + * Electron) binary was compiled for. + */ +module.exports = function arch () { + /** + * The running binary is 64-bit, so the OS is clearly 64-bit. + */ + if (process.arch === 'x64') { + return 'x64' + } + + /** + * All recent versions of Mac OS are 64-bit. + */ + if (process.platform === 'darwin') { + return 'x64' + } + + /** + * On Windows, the most reliable way to detect a 64-bit OS from within a 32-bit + * app is based on the presence of a WOW64 file: %SystemRoot%\SysNative. + * See: https://twitter.com/feross/status/776949077208510464 + */ + if (process.platform === 'win32') { + var useEnv = false + try { + useEnv = !!(process.env.SYSTEMROOT && fs.statSync(process.env.SYSTEMROOT)) + } catch (err) {} + + var sysRoot = useEnv ? process.env.SYSTEMROOT : 'C:\\Windows' + + // If %SystemRoot%\SysNative exists, we are in a WOW64 FS Redirected application. + var isWOW64 = false + try { + isWOW64 = !!fs.statSync(path.join(sysRoot, 'sysnative')) + } catch (err) {} + + return isWOW64 ? 'x64' : 'x86' + } + + /** + * On Linux, use the `getconf` command to get the architecture. + */ + if (process.platform === 'linux') { + var output = cp.execSync('getconf LONG_BIT', { encoding: 'utf8' }) + return output === '64\n' ? 'x64' : 'x86' + } + + /** + * If none of the above, assume the architecture is 32-bit. + */ + return 'x86' +} diff --git a/website/node_modules/arch/package.json b/website/node_modules/arch/package.json new file mode 100644 index 00000000..70a0446e --- /dev/null +++ b/website/node_modules/arch/package.json @@ -0,0 +1,60 @@ +{ + "name": "arch", + "description": "Better `os.arch()` for node and the browser -- detect OS architecture", + "version": "2.2.0", + "author": { + "name": "Feross Aboukhadijeh", + "email": "feross@feross.org", + "url": "https://feross.org" + }, + "browser": "browser.js", + "types": "./index.d.ts", + "bugs": { + "url": "https://github.com/feross/arch/issues" + }, + "devDependencies": { + "airtap": "^3.0.0", + "standard": "*", + "tape": "^5.0.0" + }, + "homepage": "https://github.com/feross/arch", + "keywords": [ + "browser", + "browserify", + "arch", + "cpu info", + "cpus", + "architecture", + "navigator.platform", + "x64", + "x86", + "64 bit", + "32 bit" + ], + "license": "MIT", + "main": "index.js", + "repository": { + "type": "git", + "url": "git://github.com/feross/arch.git" + }, + "scripts": { + "test": "standard && npm run test-node && npm run test-browser", + "test-browser": "airtap -- test/*.js", + "test-browser-local": "airtap --local -- test/*.js", + "test-node": "tape test/*.js" + }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] +} diff --git a/website/node_modules/arg/LICENSE.md b/website/node_modules/arg/LICENSE.md new file mode 100644 index 00000000..b708f872 --- /dev/null +++ b/website/node_modules/arg/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 Vercel, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/website/node_modules/arg/README.md b/website/node_modules/arg/README.md new file mode 100644 index 00000000..6501df59 --- /dev/null +++ b/website/node_modules/arg/README.md @@ -0,0 +1,317 @@ +# Arg + +`arg` is an unopinionated, no-frills CLI argument parser. + +## Installation + +```bash +npm install arg +``` + +## Usage + +`arg()` takes either 1 or 2 arguments: + +1. Command line specification object (see below) +2. Parse options (_Optional_, defaults to `{permissive: false, argv: process.argv.slice(2), stopAtPositional: false}`) + +It returns an object with any values present on the command-line (missing options are thus +missing from the resulting object). Arg performs no validation/requirement checking - we +leave that up to the application. + +All parameters that aren't consumed by options (commonly referred to as "extra" parameters) +are added to `result._`, which is _always_ an array (even if no extra parameters are passed, +in which case an empty array is returned). + +```javascript +const arg = require('arg'); + +// `options` is an optional parameter +const args = arg( + spec, + (options = { permissive: false, argv: process.argv.slice(2) }) +); +``` + +For example: + +```console +$ node ./hello.js --verbose -vvv --port=1234 -n 'My name' foo bar --tag qux --tag=qix -- --foobar +``` + +```javascript +// hello.js +const arg = require('arg'); + +const args = arg({ + // Types + '--help': Boolean, + '--version': Boolean, + '--verbose': arg.COUNT, // Counts the number of times --verbose is passed + '--port': Number, // --port or --port= + '--name': String, // --name or --name= + '--tag': [String], // --tag or --tag= + + // Aliases + '-v': '--verbose', + '-n': '--name', // -n ; result is stored in --name + '--label': '--name' // --label or --label=; + // result is stored in --name +}); + +console.log(args); +/* +{ + _: ["foo", "bar", "--foobar"], + '--port': 1234, + '--verbose': 4, + '--name': "My name", + '--tag': ["qux", "qix"] +} +*/ +``` + +The values for each key=>value pair is either a type (function or [function]) or a string (indicating an alias). + +- In the case of a function, the string value of the argument's value is passed to it, + and the return value is used as the ultimate value. + +- In the case of an array, the only element _must_ be a type function. Array types indicate + that the argument may be passed multiple times, and as such the resulting value in the returned + object is an array with all of the values that were passed using the specified flag. + +- In the case of a string, an alias is established. If a flag is passed that matches the _key_, + then the _value_ is substituted in its place. + +Type functions are passed three arguments: + +1. The parameter value (always a string) +2. The parameter name (e.g. `--label`) +3. The previous value for the destination (useful for reduce-like operations or for supporting `-v` multiple times, etc.) + +This means the built-in `String`, `Number`, and `Boolean` type constructors "just work" as type functions. + +Note that `Boolean` and `[Boolean]` have special treatment - an option argument is _not_ consumed or passed, but instead `true` is +returned. These options are called "flags". + +For custom handlers that wish to behave as flags, you may pass the function through `arg.flag()`: + +```javascript +const arg = require('arg'); + +const argv = [ + '--foo', + 'bar', + '-ff', + 'baz', + '--foo', + '--foo', + 'qux', + '-fff', + 'qix' +]; + +function myHandler(value, argName, previousValue) { + /* `value` is always `true` */ + return 'na ' + (previousValue || 'batman!'); +} + +const args = arg( + { + '--foo': arg.flag(myHandler), + '-f': '--foo' + }, + { + argv + } +); + +console.log(args); +/* +{ + _: ['bar', 'baz', 'qux', 'qix'], + '--foo': 'na na na na na na na na batman!' +} +*/ +``` + +As well, `arg` supplies a helper argument handler called `arg.COUNT`, which equivalent to a `[Boolean]` argument's `.length` +property - effectively counting the number of times the boolean flag, denoted by the key, is passed on the command line.. +For example, this is how you could implement `ssh`'s multiple levels of verbosity (`-vvvv` being the most verbose). + +```javascript +const arg = require('arg'); + +const argv = ['-AAAA', '-BBBB']; + +const args = arg( + { + '-A': arg.COUNT, + '-B': [Boolean] + }, + { + argv + } +); + +console.log(args); +/* +{ + _: [], + '-A': 4, + '-B': [true, true, true, true] +} +*/ +``` + +### Options + +If a second parameter is specified and is an object, it specifies parsing options to modify the behavior of `arg()`. + +#### `argv` + +If you have already sliced or generated a number of raw arguments to be parsed (as opposed to letting `arg` +slice them from `process.argv`) you may specify them in the `argv` option. + +For example: + +```javascript +const args = arg( + { + '--foo': String + }, + { + argv: ['hello', '--foo', 'world'] + } +); +``` + +results in: + +```javascript +const args = { + _: ['hello'], + '--foo': 'world' +}; +``` + +#### `permissive` + +When `permissive` set to `true`, `arg` will push any unknown arguments +onto the "extra" argument array (`result._`) instead of throwing an error about +an unknown flag. + +For example: + +```javascript +const arg = require('arg'); + +const argv = [ + '--foo', + 'hello', + '--qux', + 'qix', + '--bar', + '12345', + 'hello again' +]; + +const args = arg( + { + '--foo': String, + '--bar': Number + }, + { + argv, + permissive: true + } +); +``` + +results in: + +```javascript +const args = { + _: ['--qux', 'qix', 'hello again'], + '--foo': 'hello', + '--bar': 12345 +}; +``` + +#### `stopAtPositional` + +When `stopAtPositional` is set to `true`, `arg` will halt parsing at the first +positional argument. + +For example: + +```javascript +const arg = require('arg'); + +const argv = ['--foo', 'hello', '--bar']; + +const args = arg( + { + '--foo': Boolean, + '--bar': Boolean + }, + { + argv, + stopAtPositional: true + } +); +``` + +results in: + +```javascript +const args = { + _: ['hello', '--bar'], + '--foo': true +}; +``` + +### Errors + +Some errors that `arg` throws provide a `.code` property in order to aid in recovering from user error, or to +differentiate between user error and developer error (bug). + +##### ARG_UNKNOWN_OPTION + +If an unknown option (not defined in the spec object) is passed, an error with code `ARG_UNKNOWN_OPTION` will be thrown: + +```js +// cli.js +try { + require('arg')({ '--hi': String }); +} catch (err) { + if (err.code === 'ARG_UNKNOWN_OPTION') { + console.log(err.message); + } else { + throw err; + } +} +``` + +```shell +node cli.js --extraneous true +Unknown or unexpected option: --extraneous +``` + +# FAQ + +A few questions and answers that have been asked before: + +### How do I require an argument with `arg`? + +Do the assertion yourself, such as: + +```javascript +const args = arg({ '--name': String }); + +if (!args['--name']) throw new Error('missing required argument: --name'); +``` + +# License + +Released under the [MIT License](LICENSE.md). diff --git a/website/node_modules/arg/index.d.ts b/website/node_modules/arg/index.d.ts new file mode 100644 index 00000000..44f9f354 --- /dev/null +++ b/website/node_modules/arg/index.d.ts @@ -0,0 +1,44 @@ +declare function arg( + spec: T, + options?: arg.Options +): arg.Result; + +declare namespace arg { + export const flagSymbol: unique symbol; + + export function flag(fn: T): T & { [arg.flagSymbol]: true }; + + export const COUNT: Handler & { [arg.flagSymbol]: true }; + + export type Handler = ( + value: string, + name: string, + previousValue?: T + ) => T; + + export class ArgError extends Error { + constructor(message: string, code: string); + + code: string; + } + + export interface Spec { + [key: string]: string | Handler | [Handler]; + } + + export type Result = { _: string[] } & { + [K in keyof T]?: T[K] extends Handler + ? ReturnType + : T[K] extends [Handler] + ? Array> + : never; + }; + + export interface Options { + argv?: string[]; + permissive?: boolean; + stopAtPositional?: boolean; + } +} + +export = arg; diff --git a/website/node_modules/arg/index.js b/website/node_modules/arg/index.js new file mode 100644 index 00000000..3f60f4ca --- /dev/null +++ b/website/node_modules/arg/index.js @@ -0,0 +1,195 @@ +const flagSymbol = Symbol('arg flag'); + +class ArgError extends Error { + constructor(msg, code) { + super(msg); + this.name = 'ArgError'; + this.code = code; + + Object.setPrototypeOf(this, ArgError.prototype); + } +} + +function arg( + opts, + { + argv = process.argv.slice(2), + permissive = false, + stopAtPositional = false + } = {} +) { + if (!opts) { + throw new ArgError( + 'argument specification object is required', + 'ARG_CONFIG_NO_SPEC' + ); + } + + const result = { _: [] }; + + const aliases = {}; + const handlers = {}; + + for (const key of Object.keys(opts)) { + if (!key) { + throw new ArgError( + 'argument key cannot be an empty string', + 'ARG_CONFIG_EMPTY_KEY' + ); + } + + if (key[0] !== '-') { + throw new ArgError( + `argument key must start with '-' but found: '${key}'`, + 'ARG_CONFIG_NONOPT_KEY' + ); + } + + if (key.length === 1) { + throw new ArgError( + `argument key must have a name; singular '-' keys are not allowed: ${key}`, + 'ARG_CONFIG_NONAME_KEY' + ); + } + + if (typeof opts[key] === 'string') { + aliases[key] = opts[key]; + continue; + } + + let type = opts[key]; + let isFlag = false; + + if ( + Array.isArray(type) && + type.length === 1 && + typeof type[0] === 'function' + ) { + const [fn] = type; + type = (value, name, prev = []) => { + prev.push(fn(value, name, prev[prev.length - 1])); + return prev; + }; + isFlag = fn === Boolean || fn[flagSymbol] === true; + } else if (typeof type === 'function') { + isFlag = type === Boolean || type[flagSymbol] === true; + } else { + throw new ArgError( + `type missing or not a function or valid array type: ${key}`, + 'ARG_CONFIG_VAD_TYPE' + ); + } + + if (key[1] !== '-' && key.length > 2) { + throw new ArgError( + `short argument keys (with a single hyphen) must have only one character: ${key}`, + 'ARG_CONFIG_SHORTOPT_TOOLONG' + ); + } + + handlers[key] = [type, isFlag]; + } + + for (let i = 0, len = argv.length; i < len; i++) { + const wholeArg = argv[i]; + + if (stopAtPositional && result._.length > 0) { + result._ = result._.concat(argv.slice(i)); + break; + } + + if (wholeArg === '--') { + result._ = result._.concat(argv.slice(i + 1)); + break; + } + + if (wholeArg.length > 1 && wholeArg[0] === '-') { + /* eslint-disable operator-linebreak */ + const separatedArguments = + wholeArg[1] === '-' || wholeArg.length === 2 + ? [wholeArg] + : wholeArg + .slice(1) + .split('') + .map((a) => `-${a}`); + /* eslint-enable operator-linebreak */ + + for (let j = 0; j < separatedArguments.length; j++) { + const arg = separatedArguments[j]; + const [originalArgName, argStr] = + arg[1] === '-' ? arg.split(/=(.*)/, 2) : [arg, undefined]; + + let argName = originalArgName; + while (argName in aliases) { + argName = aliases[argName]; + } + + if (!(argName in handlers)) { + if (permissive) { + result._.push(arg); + continue; + } else { + throw new ArgError( + `unknown or unexpected option: ${originalArgName}`, + 'ARG_UNKNOWN_OPTION' + ); + } + } + + const [type, isFlag] = handlers[argName]; + + if (!isFlag && j + 1 < separatedArguments.length) { + throw new ArgError( + `option requires argument (but was followed by another short argument): ${originalArgName}`, + 'ARG_MISSING_REQUIRED_SHORTARG' + ); + } + + if (isFlag) { + result[argName] = type(true, argName, result[argName]); + } else if (argStr === undefined) { + if ( + argv.length < i + 2 || + (argv[i + 1].length > 1 && + argv[i + 1][0] === '-' && + !( + argv[i + 1].match(/^-?\d*(\.(?=\d))?\d*$/) && + (type === Number || + // eslint-disable-next-line no-undef + (typeof BigInt !== 'undefined' && type === BigInt)) + )) + ) { + const extended = + originalArgName === argName ? '' : ` (alias for ${argName})`; + throw new ArgError( + `option requires argument: ${originalArgName}${extended}`, + 'ARG_MISSING_REQUIRED_LONGARG' + ); + } + + result[argName] = type(argv[i + 1], argName, result[argName]); + ++i; + } else { + result[argName] = type(argStr, argName, result[argName]); + } + } + } else { + result._.push(wholeArg); + } + } + + return result; +} + +arg.flag = (fn) => { + fn[flagSymbol] = true; + return fn; +}; + +// Utility types +arg.COUNT = arg.flag((v, name, existingCount) => (existingCount || 0) + 1); + +// Expose error class +arg.ArgError = ArgError; + +module.exports = arg; diff --git a/website/node_modules/arg/package.json b/website/node_modules/arg/package.json new file mode 100644 index 00000000..47368d76 --- /dev/null +++ b/website/node_modules/arg/package.json @@ -0,0 +1,28 @@ +{ + "name": "arg", + "version": "5.0.2", + "description": "Unopinionated, no-frills CLI argument parser", + "main": "index.js", + "types": "index.d.ts", + "repository": "vercel/arg", + "author": "Josh Junon ", + "license": "MIT", + "files": [ + "index.js", + "index.d.ts" + ], + "scripts": { + "test": "WARN_EXIT=1 jest --coverage -w 2" + }, + "devDependencies": { + "chai": "^4.1.1", + "jest": "^27.0.6", + "prettier": "^2.3.2" + }, + "prettier": { + "arrowParens": "always", + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "none" + } +} diff --git a/website/node_modules/balanced-match/.github/FUNDING.yml b/website/node_modules/balanced-match/.github/FUNDING.yml new file mode 100644 index 00000000..cea8b16e --- /dev/null +++ b/website/node_modules/balanced-match/.github/FUNDING.yml @@ -0,0 +1,2 @@ +tidelift: "npm/balanced-match" +patreon: juliangruber diff --git a/website/node_modules/balanced-match/LICENSE.md b/website/node_modules/balanced-match/LICENSE.md new file mode 100644 index 00000000..2cdc8e41 --- /dev/null +++ b/website/node_modules/balanced-match/LICENSE.md @@ -0,0 +1,21 @@ +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/website/node_modules/balanced-match/README.md b/website/node_modules/balanced-match/README.md new file mode 100644 index 00000000..d2a48b6b --- /dev/null +++ b/website/node_modules/balanced-match/README.md @@ -0,0 +1,97 @@ +# balanced-match + +Match balanced string pairs, like `{` and `}` or `` and ``. Supports regular expressions as well! + +[![build status](https://secure.travis-ci.org/juliangruber/balanced-match.svg)](http://travis-ci.org/juliangruber/balanced-match) +[![downloads](https://img.shields.io/npm/dm/balanced-match.svg)](https://www.npmjs.org/package/balanced-match) + +[![testling badge](https://ci.testling.com/juliangruber/balanced-match.png)](https://ci.testling.com/juliangruber/balanced-match) + +## Example + +Get the first matching pair of braces: + +```js +var balanced = require('balanced-match'); + +console.log(balanced('{', '}', 'pre{in{nested}}post')); +console.log(balanced('{', '}', 'pre{first}between{second}post')); +console.log(balanced(/\s+\{\s+/, /\s+\}\s+/, 'pre { in{nest} } post')); +``` + +The matches are: + +```bash +$ node example.js +{ start: 3, end: 14, pre: 'pre', body: 'in{nested}', post: 'post' } +{ start: 3, + end: 9, + pre: 'pre', + body: 'first', + post: 'between{second}post' } +{ start: 3, end: 17, pre: 'pre', body: 'in{nest}', post: 'post' } +``` + +## API + +### var m = balanced(a, b, str) + +For the first non-nested matching pair of `a` and `b` in `str`, return an +object with those keys: + +* **start** the index of the first match of `a` +* **end** the index of the matching `b` +* **pre** the preamble, `a` and `b` not included +* **body** the match, `a` and `b` not included +* **post** the postscript, `a` and `b` not included + +If there's no match, `undefined` will be returned. + +If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `['{', 'a', '']` and `{a}}` will match `['', 'a', '}']`. + +### var r = balanced.range(a, b, str) + +For the first non-nested matching pair of `a` and `b` in `str`, return an +array with indexes: `[ , ]`. + +If there's no match, `undefined` will be returned. + +If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `[ 1, 3 ]` and `{a}}` will match `[0, 2]`. + +## Installation + +With [npm](https://npmjs.org) do: + +```bash +npm install balanced-match +``` + +## Security contact information + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. + +## License + +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/website/node_modules/balanced-match/index.js b/website/node_modules/balanced-match/index.js new file mode 100644 index 00000000..c67a6460 --- /dev/null +++ b/website/node_modules/balanced-match/index.js @@ -0,0 +1,62 @@ +'use strict'; +module.exports = balanced; +function balanced(a, b, str) { + if (a instanceof RegExp) a = maybeMatch(a, str); + if (b instanceof RegExp) b = maybeMatch(b, str); + + var r = range(a, b, str); + + return r && { + start: r[0], + end: r[1], + pre: str.slice(0, r[0]), + body: str.slice(r[0] + a.length, r[1]), + post: str.slice(r[1] + b.length) + }; +} + +function maybeMatch(reg, str) { + var m = str.match(reg); + return m ? m[0] : null; +} + +balanced.range = range; +function range(a, b, str) { + var begs, beg, left, right, result; + var ai = str.indexOf(a); + var bi = str.indexOf(b, ai + 1); + var i = ai; + + if (ai >= 0 && bi > 0) { + if(a===b) { + return [ai, bi]; + } + begs = []; + left = str.length; + + while (i >= 0 && !result) { + if (i == ai) { + begs.push(i); + ai = str.indexOf(a, i + 1); + } else if (begs.length == 1) { + result = [ begs.pop(), bi ]; + } else { + beg = begs.pop(); + if (beg < left) { + left = beg; + right = bi; + } + + bi = str.indexOf(b, i + 1); + } + + i = ai < bi && ai >= 0 ? ai : bi; + } + + if (begs.length) { + result = [ left, right ]; + } + } + + return result; +} diff --git a/website/node_modules/balanced-match/package.json b/website/node_modules/balanced-match/package.json new file mode 100644 index 00000000..ce6073e0 --- /dev/null +++ b/website/node_modules/balanced-match/package.json @@ -0,0 +1,48 @@ +{ + "name": "balanced-match", + "description": "Match balanced character pairs, like \"{\" and \"}\"", + "version": "1.0.2", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/balanced-match.git" + }, + "homepage": "https://github.com/juliangruber/balanced-match", + "main": "index.js", + "scripts": { + "test": "tape test/test.js", + "bench": "matcha test/bench.js" + }, + "devDependencies": { + "matcha": "^0.7.0", + "tape": "^4.6.0" + }, + "keywords": [ + "match", + "regexp", + "test", + "balanced", + "parse" + ], + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "license": "MIT", + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/8..latest", + "firefox/20..latest", + "firefox/nightly", + "chrome/25..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2..latest" + ] + } +} diff --git a/website/node_modules/boxen/index.d.ts b/website/node_modules/boxen/index.d.ts new file mode 100644 index 00000000..ebd495c1 --- /dev/null +++ b/website/node_modules/boxen/index.d.ts @@ -0,0 +1,260 @@ +import {LiteralUnion} from 'type-fest'; +import {BoxStyle, Boxes} from 'cli-boxes'; + +/** +Characters used for custom border. + +@example +``` +// attttb +// l r +// dbbbbc + +const border: CustomBorderStyle = { + topLeft: 'a', + topRight: 'b', + bottomRight: 'c', + bottomLeft: 'd', + left: 'l', + right: 'r', + top: 't', + bottom: 'b', +}; +``` +*/ +export interface CustomBorderStyle extends BoxStyle { + /** + @deprecated Use `top` and `bottom` instead. + */ + horizontal?: string; + + /** + @deprecated Use `left` and `right` instead. + */ + vertical?: string; +} + +/** +Spacing used for `padding` and `margin`. +*/ +export interface Spacing { + readonly top: number; + readonly right: number; + readonly bottom: number; + readonly left: number; +} + +export interface Options { + /** + Color of the box border. + */ + readonly borderColor?: LiteralUnion< + | 'black' + | 'red' + | 'green' + | 'yellow' + | 'blue' + | 'magenta' + | 'cyan' + | 'white' + | 'gray' + | 'grey' + | 'blackBright' + | 'redBright' + | 'greenBright' + | 'yellowBright' + | 'blueBright' + | 'magentaBright' + | 'cyanBright' + | 'whiteBright', + string + >; + + /** + Style of the box border. + + @default 'single' + */ + readonly borderStyle?: keyof Boxes | CustomBorderStyle; + + /** + Reduce opacity of the border. + + @default false + */ + readonly dimBorder?: boolean; + + /** + Space between the text and box border. + + @default 0 + */ + readonly padding?: number | Spacing; + + /** + Space around the box. + + @default 0 + */ + readonly margin?: number | Spacing; + + /** + Float the box on the available terminal screen space. + + @default 'left' + */ + readonly float?: 'left' | 'right' | 'center'; + + /** + Color of the background. + */ + readonly backgroundColor?: LiteralUnion< + | 'black' + | 'red' + | 'green' + | 'yellow' + | 'blue' + | 'magenta' + | 'cyan' + | 'white' + | 'blackBright' + | 'redBright' + | 'greenBright' + | 'yellowBright' + | 'blueBright' + | 'magentaBright' + | 'cyanBright' + | 'whiteBright', + string + >; + + /** + Align the text in the box based on the widest line. + + @default 'left' + @deprecated Use `textAlignment` instead. + */ + readonly align?: 'left' | 'right' | 'center'; + + /** + Align the text in the box based on the widest line. + + @default 'left' + */ + readonly textAlignment?: 'left' | 'right' | 'center'; + + /** + Display a title at the top of the box. + If needed, the box will horizontally expand to fit the title. + + @example + ``` + console.log(boxen('foo bar', {title: 'example'})); + // ┌ example ┐ + // │foo bar │ + // └─────────┘ + ``` + */ + readonly title?: string; + + /** + Align the title in the top bar. + + @default 'left' + + @example + ``` + console.log(boxen('foo bar foo bar', {title: 'example', titleAlignment: 'center'})); + // ┌─── example ───┐ + // │foo bar foo bar│ + // └───────────────┘ + + console.log(boxen('foo bar foo bar', {title: 'example', titleAlignment: 'right'})); + // ┌────── example ┐ + // │foo bar foo bar│ + // └───────────────┘ + ``` + */ + readonly titleAlignment?: 'left' | 'right' | 'center'; + + /** + Set a fixed width for the box. + + __Note__: This disables terminal overflow handling and may cause the box to look broken if the user's terminal is not wide enough. + + @example + ``` + import boxen from 'boxen'; + + console.log(boxen('foo bar', {width: 15})); + // ┌─────────────┐ + // │foo bar │ + // └─────────────┘ + ``` + */ + readonly width?: number; + + /** + Set a fixed height for the box. + + __Note__: This option will crop overflowing content. + + @example + ``` + import boxen from 'boxen'; + + console.log(boxen('foo bar', {height: 5})); + // ┌───────┐ + // │foo bar│ + // │ │ + // │ │ + // └───────┘ + ``` + */ + readonly height?: number; + + /** + __boolean__: Wether or not to fit all available space within the terminal. + + __function__: Pass a callback function to control box dimensions. + + @example + ``` + import boxen from 'boxen'; + + console.log(boxen('foo bar', { + fullscreen: (width, height) => [width, height - 1]; + })); + ``` + */ + readonly fullscreen?: boolean | ((width: number, height: number) => [width: number, height: number]); +} + +/** +Creates a box in the terminal. + +@param text - The text inside the box. +@returns The box. + +@example +``` +import boxen from 'boxen'; + +console.log(boxen('unicorn', {padding: 1})); +// ┌─────────────┐ +// │ │ +// │ unicorn │ +// │ │ +// └─────────────┘ + +console.log(boxen('unicorn', {padding: 1, margin: 1, borderStyle: 'double'})); +// +// ╔═════════════╗ +// ║ ║ +// ║ unicorn ║ +// ║ ║ +// ╚═════════════╝ +// +``` +*/ +export default function boxen(text: string, options?: Options): string; diff --git a/website/node_modules/boxen/index.js b/website/node_modules/boxen/index.js new file mode 100644 index 00000000..6a17500d --- /dev/null +++ b/website/node_modules/boxen/index.js @@ -0,0 +1,361 @@ +import process from 'node:process'; +import stringWidth from 'string-width'; +import chalk from 'chalk'; +import widestLine from 'widest-line'; +import cliBoxes from 'cli-boxes'; +import camelCase from 'camelcase'; +import ansiAlign from 'ansi-align'; +import wrapAnsi from 'wrap-ansi'; + +const NEWLINE = '\n'; +const PAD = ' '; +const BORDERS_WIDTH = 2; + +const terminalColumns = () => { + const {env, stdout, stderr} = process; + + if (stdout?.columns) { + return stdout.columns; + } + + if (stderr?.columns) { + return stderr.columns; + } + + if (env.COLUMNS) { + return Number.parseInt(env.COLUMNS, 10); + } + + return 80; +}; + +const getObject = detail => typeof detail === 'number' ? { + top: detail, + right: detail * 3, + bottom: detail, + left: detail * 3, +} : { + top: 0, + right: 0, + bottom: 0, + left: 0, + ...detail, +}; + +const getBorderChars = borderStyle => { + const sides = [ + 'topLeft', + 'topRight', + 'bottomRight', + 'bottomLeft', + 'left', + 'right', + 'top', + 'bottom', + ]; + + let characters; + + if (typeof borderStyle === 'string') { + characters = cliBoxes[borderStyle]; + + if (!characters) { + throw new TypeError(`Invalid border style: ${borderStyle}`); + } + } else { + // Ensure retro-compatibility + if (typeof borderStyle?.vertical === 'string') { + borderStyle.left = borderStyle.vertical; + borderStyle.right = borderStyle.vertical; + } + + // Ensure retro-compatibility + if (typeof borderStyle?.horizontal === 'string') { + borderStyle.top = borderStyle.horizontal; + borderStyle.bottom = borderStyle.horizontal; + } + + for (const side of sides) { + if (!borderStyle[side] || typeof borderStyle[side] !== 'string') { + throw new TypeError(`Invalid border style: ${side}`); + } + } + + characters = borderStyle; + } + + return characters; +}; + +const makeTitle = (text, horizontal, alignement) => { + let title = ''; + + const textWidth = stringWidth(text); + + switch (alignement) { + case 'left': + title = text + horizontal.slice(textWidth); + break; + case 'right': + title = horizontal.slice(textWidth) + text; + break; + default: + horizontal = horizontal.slice(textWidth); + + if (horizontal.length % 2 === 1) { // This is needed in case the length is odd + horizontal = horizontal.slice(Math.floor(horizontal.length / 2)); + title = horizontal.slice(1) + text + horizontal; // We reduce the left part of one character to avoid the bar to go beyond its limit + } else { + horizontal = horizontal.slice(horizontal.length / 2); + title = horizontal + text + horizontal; + } + + break; + } + + return title; +}; + +const makeContentText = (text, {padding, width, textAlignment, height}) => { + text = ansiAlign(text, {align: textAlignment}); + let lines = text.split(NEWLINE); + const textWidth = widestLine(text); + + const max = width - padding.left - padding.right; + + if (textWidth > max) { + const newLines = []; + for (const line of lines) { + const createdLines = wrapAnsi(line, max, {hard: true}); + const alignedLines = ansiAlign(createdLines, {align: textAlignment}); + const alignedLinesArray = alignedLines.split('\n'); + const longestLength = Math.max(...alignedLinesArray.map(s => stringWidth(s))); + + for (const alignedLine of alignedLinesArray) { + let paddedLine; + switch (textAlignment) { + case 'center': + paddedLine = PAD.repeat((max - longestLength) / 2) + alignedLine; + break; + case 'right': + paddedLine = PAD.repeat(max - longestLength) + alignedLine; + break; + default: + paddedLine = alignedLine; + break; + } + + newLines.push(paddedLine); + } + } + + lines = newLines; + } + + if (textAlignment === 'center' && textWidth < max) { + lines = lines.map(line => PAD.repeat((max - textWidth) / 2) + line); + } else if (textAlignment === 'right' && textWidth < max) { + lines = lines.map(line => PAD.repeat(max - textWidth) + line); + } + + const paddingLeft = PAD.repeat(padding.left); + const paddingRight = PAD.repeat(padding.right); + + lines = lines.map(line => paddingLeft + line + paddingRight); + + lines = lines.map(line => { + if (width - stringWidth(line) > 0) { + switch (textAlignment) { + case 'center': + return line + PAD.repeat(width - stringWidth(line)); + case 'right': + return line + PAD.repeat(width - stringWidth(line)); + default: + return line + PAD.repeat(width - stringWidth(line)); + } + } + + return line; + }); + + if (padding.top > 0) { + lines = [...Array.from({length: padding.top}).fill(PAD.repeat(width)), ...lines]; + } + + if (padding.bottom > 0) { + lines = [...lines, ...Array.from({length: padding.bottom}).fill(PAD.repeat(width))]; + } + + if (height && lines.length > height) { + lines = lines.slice(0, height); + } else if (height && lines.length < height) { + lines = [...lines, ...Array.from({length: height - lines.length}).fill(PAD.repeat(width))]; + } + + return lines.join(NEWLINE); +}; + +const boxContent = (content, contentWidth, options) => { + const colorizeBorder = border => { + const newBorder = options.borderColor ? getColorFn(options.borderColor)(border) : border; + return options.dimBorder ? chalk.dim(newBorder) : newBorder; + }; + + const colorizeContent = content => options.backgroundColor ? getBGColorFn(options.backgroundColor)(content) : content; + + const chars = getBorderChars(options.borderStyle); + const columns = terminalColumns(); + let marginLeft = PAD.repeat(options.margin.left); + + if (options.float === 'center') { + const marginWidth = Math.max((columns - contentWidth - BORDERS_WIDTH) / 2, 0); + marginLeft = PAD.repeat(marginWidth); + } else if (options.float === 'right') { + const marginWidth = Math.max(columns - contentWidth - options.margin.right - BORDERS_WIDTH, 0); + marginLeft = PAD.repeat(marginWidth); + } + + const top = colorizeBorder(NEWLINE.repeat(options.margin.top) + marginLeft + chars.topLeft + (options.title ? makeTitle(options.title, chars.top.repeat(contentWidth), options.titleAlignment) : chars.top.repeat(contentWidth)) + chars.topRight); + const bottom = colorizeBorder(marginLeft + chars.bottomLeft + chars.bottom.repeat(contentWidth) + chars.bottomRight + NEWLINE.repeat(options.margin.bottom)); + + const LINE_SEPARATOR = (contentWidth + BORDERS_WIDTH + options.margin.left >= columns) ? '' : NEWLINE; + + const lines = content.split(NEWLINE); + + const middle = lines.map(line => marginLeft + colorizeBorder(chars.left) + colorizeContent(line) + colorizeBorder(chars.right)).join(LINE_SEPARATOR); + + return top + LINE_SEPARATOR + middle + LINE_SEPARATOR + bottom; +}; + +const sanitizeOptions = options => { + // If fullscreen is enabled, max-out unspecified width/height + if (options.fullscreen && process?.stdout) { + let newDimensions = [process.stdout.columns, process.stdout.rows]; + + if (typeof options.fullscreen === 'function') { + newDimensions = options.fullscreen(...newDimensions); + } + + if (!options.width) { + options.width = newDimensions[0]; + } + + if (!options.height) { + options.height = newDimensions[1]; + } + } + + // If width is provided, make sure it's not below 1 + if (options.width) { + options.width = Math.max(1, options.width - BORDERS_WIDTH); + } + + // If height is provided, make sure it's not below 1 + if (options.height) { + options.height = Math.max(1, options.height - BORDERS_WIDTH); + } + + return options; +}; + +const determineDimensions = (text, options) => { + options = sanitizeOptions(options); + const widthOverride = options.width !== undefined; + const columns = terminalColumns(); + const maxWidth = columns - options.margin.left - options.margin.right - BORDERS_WIDTH; + + const widest = widestLine(wrapAnsi(text, columns - BORDERS_WIDTH, {hard: true, trim: false})) + options.padding.left + options.padding.right; + + // If title and width are provided, title adheres to fixed width + if (options.title && widthOverride) { + options.title = options.title.slice(0, Math.max(0, options.width - 2)); + if (options.title) { + options.title = ` ${options.title} `; + } + } else if (options.title) { + options.title = options.title.slice(0, Math.max(0, maxWidth - 2)); + + // Recheck if title isn't empty now + if (options.title) { + options.title = ` ${options.title} `; + // If the title is larger than content, box adheres to title width + if (stringWidth(options.title) > widest) { + options.width = stringWidth(options.title); + } + } + } + + // If fixed width is provided, use it or content width as reference + options.width = options.width ? options.width : widest; + + if (!widthOverride) { + if ((options.margin.left && options.margin.right) && options.width > maxWidth) { + // Let's assume we have margins: left = 3, right = 5, in total = 8 + const spaceForMargins = columns - options.width - BORDERS_WIDTH; + // Let's assume we have space = 4 + const multiplier = spaceForMargins / (options.margin.left + options.margin.right); + // Here: multiplier = 4/8 = 0.5 + options.margin.left = Math.max(0, Math.floor(options.margin.left * multiplier)); + options.margin.right = Math.max(0, Math.floor(options.margin.right * multiplier)); + // Left: 3 * 0.5 = 1.5 -> 1 + // Right: 6 * 0.5 = 3 + } + + // Re-cap width considering the margins after shrinking + options.width = Math.min(options.width, columns - BORDERS_WIDTH - options.margin.left - options.margin.right); + } + + // Prevent padding overflow + if (options.width - (options.padding.left + options.padding.right) <= 0) { + options.padding.left = 0; + options.padding.right = 0; + } + + if (options.height && options.height - (options.padding.top + options.padding.bottom) <= 0) { + options.padding.top = 0; + options.padding.bottom = 0; + } + + return options; +}; + +const isHex = color => color.match(/^#(?:[0-f]{3}){1,2}$/i); +const isColorValid = color => typeof color === 'string' && (chalk[color] ?? isHex(color)); +const getColorFn = color => isHex(color) ? chalk.hex(color) : chalk[color]; +const getBGColorFn = color => isHex(color) ? chalk.bgHex(color) : chalk[camelCase(['bg', color])]; + +export default function boxen(text, options) { + options = { + padding: 0, + borderStyle: 'single', + dimBorder: false, + textAlignment: 'left', + float: 'left', + titleAlignment: 'left', + ...options, + }; + + // This option is deprecated + if (options.align) { + options.textAlignment = options.align; + } + + if (options.borderColor && !isColorValid(options.borderColor)) { + throw new Error(`${options.borderColor} is not a valid borderColor`); + } + + if (options.backgroundColor && !isColorValid(options.backgroundColor)) { + throw new Error(`${options.backgroundColor} is not a valid backgroundColor`); + } + + options.padding = getObject(options.padding); + options.margin = getObject(options.margin); + + options = determineDimensions(text, options); + + text = makeContentText(text, options); + + return boxContent(text, options.width, options); +} + +export {default as _borderStyles} from 'cli-boxes'; diff --git a/website/node_modules/boxen/license b/website/node_modules/boxen/license new file mode 100644 index 00000000..fa7ceba3 --- /dev/null +++ b/website/node_modules/boxen/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/boxen/package.json b/website/node_modules/boxen/package.json new file mode 100644 index 00000000..b42c92ee --- /dev/null +++ b/website/node_modules/boxen/package.json @@ -0,0 +1,60 @@ +{ + "name": "boxen", + "version": "7.0.0", + "description": "Create boxes in the terminal", + "license": "MIT", + "repository": "sindresorhus/boxen", + "funding": "https://github.com/sponsors/sindresorhus", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "type": "module", + "exports": "./index.js", + "engines": { + "node": ">=14.16" + }, + "scripts": { + "test": "xo && nyc ava && tsd" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "cli", + "box", + "boxes", + "terminal", + "term", + "console", + "ascii", + "unicode", + "border", + "text" + ], + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.0", + "chalk": "^5.0.1", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "devDependencies": { + "ava": "^4.3.0", + "nyc": "^15.1.0", + "tsd": "^0.21.0", + "xo": "^0.50.0" + }, + "ava": { + "snapshotDir": "tests/snapshots", + "environmentVariables": { + "COLUMNS": "60", + "FORCE_COLOR": "0" + } + } +} diff --git a/website/node_modules/boxen/readme.md b/website/node_modules/boxen/readme.md new file mode 100644 index 00000000..e1ab14b8 --- /dev/null +++ b/website/node_modules/boxen/readme.md @@ -0,0 +1,309 @@ +# boxen + +> Create boxes in the terminal + +![](screenshot.png) + +## Install + +```sh +npm install boxen +``` + +## Usage + +```js +import boxen from 'boxen'; + +console.log(boxen('unicorn', {padding: 1})); +/* +┌─────────────┐ +│ │ +│ unicorn │ +│ │ +└─────────────┘ +*/ + +console.log(boxen('unicorn', {padding: 1, margin: 1, borderStyle: 'double'})); +/* + + ╔═════════════╗ + ║ ║ + ║ unicorn ║ + ║ ║ + ╚═════════════╝ + +*/ + +console.log(boxen('unicorns love rainbows', {title: 'magical', titleAlignment: 'center'})); +/* +┌────── magical ───────┐ +│unicorns love rainbows│ +└──────────────────────┘ +*/ +``` + +## API + +### boxen(text, options?) + +#### text + +Type: `string` + +Text inside the box. + +#### options + +Type: `object` + +##### borderColor + +Type: `string`\ +Values: `'black'` `'red'` `'green'` `'yellow'` `'blue'` `'magenta'` `'cyan'` `'white'` `'gray'` or a hex value like `'#ff0000'` + +Color of the box border. + +##### borderStyle + +Type: `string | object`\ +Default: `'single'`\ +Values: +- `'single'` +``` +┌───┐ +│foo│ +└───┘ +``` +- `'double'` +``` +╔═══╗ +║foo║ +╚═══╝ +``` +- `'round'` (`'single'` sides with round corners) +``` +╭───╮ +│foo│ +╰───╯ +``` +- `'bold'` +``` +┏━━━┓ +┃foo┃ +┗━━━┛ +``` +- `'singleDouble'` (`'single'` on top and bottom, `'double'` on right and left) +``` +╓───╖ +║foo║ +╙───╜ +``` +- `'doubleSingle'` (`'double'` on top and bottom, `'single'` on right and left) +``` +╒═══╕ +│foo│ +╘═══╛ +``` +- `'classic'` +``` ++---+ +|foo| ++---+ +``` +- `'arrow'` +``` +↘↓↓↓↙ +→foo← +↗↑↑↑↖ +``` + +Style of the box border. + +Can be any of the above predefined styles or an object with the following keys: + +```js +{ + topLeft: '+', + topRight: '+', + bottomLeft: '+', + bottomRight: '+', + top: '-', + bottom: '-', + left: '|', + right: '|' +} +``` + +##### dimBorder + +Type: `boolean`\ +Default: `false` + +Reduce opacity of the border. + +##### title + +Type: `string` + +Display a title at the top of the box. +If needed, the box will horizontally expand to fit the title. + +Example: +```js +console.log(boxen('foo bar', {title: 'example'})); +/* +┌ example ┐ +│foo bar │ +└─────────┘ +*/ +``` + +##### titleAlignment + +Type: `string`\ +Default: `'left'` + +Align the title in the top bar. + +Values: +- `'left'` +```js +/* +┌ example ──────┐ +│foo bar foo bar│ +└───────────────┘ +*/ +``` +- `'center'` +```js +/* +┌─── example ───┐ +│foo bar foo bar│ +└───────────────┘ +*/ +``` +- `'right'` +```js +/* +┌────── example ┐ +│foo bar foo bar│ +└───────────────┘ +*/ +``` + +##### width + +Type: `number` + +Set a fixed width for the box. + +*Note:* This disables terminal overflow handling and may cause the box to look broken if the user's terminal is not wide enough. + +```js +import boxen from 'boxen'; + +console.log(boxen('foo bar', {width: 15})); +// ┌─────────────┐ +// │foo bar │ +// └─────────────┘ +``` + +##### height + +Type: `number` + +Set a fixed height for the box. + +*Note:* This option will crop overflowing content. + +```js +import boxen from 'boxen'; + +console.log(boxen('foo bar', {height: 5})); +// ┌───────┐ +// │foo bar│ +// │ │ +// │ │ +// └───────┘ +``` + +##### fullscreen + +Type: `boolean | (width: number, height: number) => [width: number, height: number]` + +Wether or not to fit all available space within the terminal. + +Pass a callback function to control box dimensions: + +```js +import boxen from 'boxen'; + +console.log(boxen('foo bar', { + fullscreen: (width, height) => [width, height - 1]; +})); +``` + +##### padding + +Type: `number | object`\ +Default: `0` + +Space between the text and box border. + +Accepts a number or an object with any of the `top`, `right`, `bottom`, `left` properties. When a number is specified, the left/right padding is 3 times the top/bottom to make it look nice. + +##### margin + +Type: `number | object`\ +Default: `0` + +Space around the box. + +Accepts a number or an object with any of the `top`, `right`, `bottom`, `left` properties. When a number is specified, the left/right margin is 3 times the top/bottom to make it look nice. + +##### float + +Type: `string`\ +Default: `'left'`\ +Values: `'right'` `'center'` `'left'` + +Float the box on the available terminal screen space. + +##### backgroundColor + +Type: `string`\ +Values: `'black'` `'red'` `'green'` `'yellow'` `'blue'` `'magenta'` `'cyan'` `'white'` `'gray'` or a hex value like `'#ff0000'` + +Color of the background. + +##### textAlignment + +Type: `string`\ +Default: `'left'`\ +Values: `'left'` `'center'` `'right'` + +Align the text in the box based on the widest line. + +## Maintainer + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Caesarovich](https://github.com/Caesarovich) + +## Related + +- [boxen-cli](https://github.com/sindresorhus/boxen-cli) - CLI for this module +- [cli-boxes](https://github.com/sindresorhus/cli-boxes) - Boxes for use in the terminal +- [ink-box](https://github.com/sindresorhus/ink-box) - Box component for Ink that uses this package + +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/website/node_modules/brace-expansion/LICENSE b/website/node_modules/brace-expansion/LICENSE new file mode 100644 index 00000000..de322667 --- /dev/null +++ b/website/node_modules/brace-expansion/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013 Julian Gruber + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/website/node_modules/brace-expansion/README.md b/website/node_modules/brace-expansion/README.md new file mode 100644 index 00000000..6b4e0e16 --- /dev/null +++ b/website/node_modules/brace-expansion/README.md @@ -0,0 +1,129 @@ +# brace-expansion + +[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html), +as known from sh/bash, in JavaScript. + +[![build status](https://secure.travis-ci.org/juliangruber/brace-expansion.svg)](http://travis-ci.org/juliangruber/brace-expansion) +[![downloads](https://img.shields.io/npm/dm/brace-expansion.svg)](https://www.npmjs.org/package/brace-expansion) +[![Greenkeeper badge](https://badges.greenkeeper.io/juliangruber/brace-expansion.svg)](https://greenkeeper.io/) + +[![testling badge](https://ci.testling.com/juliangruber/brace-expansion.png)](https://ci.testling.com/juliangruber/brace-expansion) + +## Example + +```js +var expand = require('brace-expansion'); + +expand('file-{a,b,c}.jpg') +// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg'] + +expand('-v{,,}') +// => ['-v', '-v', '-v'] + +expand('file{0..2}.jpg') +// => ['file0.jpg', 'file1.jpg', 'file2.jpg'] + +expand('file-{a..c}.jpg') +// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg'] + +expand('file{2..0}.jpg') +// => ['file2.jpg', 'file1.jpg', 'file0.jpg'] + +expand('file{0..4..2}.jpg') +// => ['file0.jpg', 'file2.jpg', 'file4.jpg'] + +expand('file-{a..e..2}.jpg') +// => ['file-a.jpg', 'file-c.jpg', 'file-e.jpg'] + +expand('file{00..10..5}.jpg') +// => ['file00.jpg', 'file05.jpg', 'file10.jpg'] + +expand('{{A..C},{a..c}}') +// => ['A', 'B', 'C', 'a', 'b', 'c'] + +expand('ppp{,config,oe{,conf}}') +// => ['ppp', 'pppconfig', 'pppoe', 'pppoeconf'] +``` + +## API + +```js +var expand = require('brace-expansion'); +``` + +### var expanded = expand(str) + +Return an array of all possible and valid expansions of `str`. If none are +found, `[str]` is returned. + +Valid expansions are: + +```js +/^(.*,)+(.+)?$/ +// {a,b,...} +``` + +A comma separated list of options, like `{a,b}` or `{a,{b,c}}` or `{,a,}`. + +```js +/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/ +// {x..y[..incr]} +``` + +A numeric sequence from `x` to `y` inclusive, with optional increment. +If `x` or `y` start with a leading `0`, all the numbers will be padded +to have equal length. Negative numbers and backwards iteration work too. + +```js +/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/ +// {x..y[..incr]} +``` + +An alphabetic sequence from `x` to `y` inclusive, with optional increment. +`x` and `y` must be exactly one character, and if given, `incr` must be a +number. + +For compatibility reasons, the string `${` is not eligible for brace expansion. + +## Installation + +With [npm](https://npmjs.org) do: + +```bash +npm install brace-expansion +``` + +## Contributors + +- [Julian Gruber](https://github.com/juliangruber) +- [Isaac Z. Schlueter](https://github.com/isaacs) + +## Sponsors + +This module is proudly supported by my [Sponsors](https://github.com/juliangruber/sponsors)! + +Do you want to support modules like this to improve their quality, stability and weigh in on new features? Then please consider donating to my [Patreon](https://www.patreon.com/juliangruber). Not sure how much of my modules you're using? Try [feross/thanks](https://github.com/feross/thanks)! + +## License + +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/website/node_modules/brace-expansion/index.js b/website/node_modules/brace-expansion/index.js new file mode 100644 index 00000000..bd19fe68 --- /dev/null +++ b/website/node_modules/brace-expansion/index.js @@ -0,0 +1,201 @@ +var concatMap = require('concat-map'); +var balanced = require('balanced-match'); + +module.exports = expandTop; + +var escSlash = '\0SLASH'+Math.random()+'\0'; +var escOpen = '\0OPEN'+Math.random()+'\0'; +var escClose = '\0CLOSE'+Math.random()+'\0'; +var escComma = '\0COMMA'+Math.random()+'\0'; +var escPeriod = '\0PERIOD'+Math.random()+'\0'; + +function numeric(str) { + return parseInt(str, 10) == str + ? parseInt(str, 10) + : str.charCodeAt(0); +} + +function escapeBraces(str) { + return str.split('\\\\').join(escSlash) + .split('\\{').join(escOpen) + .split('\\}').join(escClose) + .split('\\,').join(escComma) + .split('\\.').join(escPeriod); +} + +function unescapeBraces(str) { + return str.split(escSlash).join('\\') + .split(escOpen).join('{') + .split(escClose).join('}') + .split(escComma).join(',') + .split(escPeriod).join('.'); +} + + +// Basically just str.split(","), but handling cases +// where we have nested braced sections, which should be +// treated as individual members, like {a,{b,c},d} +function parseCommaParts(str) { + if (!str) + return ['']; + + var parts = []; + var m = balanced('{', '}', str); + + if (!m) + return str.split(','); + + var pre = m.pre; + var body = m.body; + var post = m.post; + var p = pre.split(','); + + p[p.length-1] += '{' + body + '}'; + var postParts = parseCommaParts(post); + if (post.length) { + p[p.length-1] += postParts.shift(); + p.push.apply(p, postParts); + } + + parts.push.apply(parts, p); + + return parts; +} + +function expandTop(str) { + if (!str) + return []; + + // I don't know why Bash 4.3 does this, but it does. + // Anything starting with {} will have the first two bytes preserved + // but *only* at the top level, so {},a}b will not expand to anything, + // but a{},b}c will be expanded to [a}c,abc]. + // One could argue that this is a bug in Bash, but since the goal of + // this module is to match Bash's rules, we escape a leading {} + if (str.substr(0, 2) === '{}') { + str = '\\{\\}' + str.substr(2); + } + + return expand(escapeBraces(str), true).map(unescapeBraces); +} + +function identity(e) { + return e; +} + +function embrace(str) { + return '{' + str + '}'; +} +function isPadded(el) { + return /^-?0\d/.test(el); +} + +function lte(i, y) { + return i <= y; +} +function gte(i, y) { + return i >= y; +} + +function expand(str, isTop) { + var expansions = []; + + var m = balanced('{', '}', str); + if (!m || /\$$/.test(m.pre)) return [str]; + + var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + var isSequence = isNumericSequence || isAlphaSequence; + var isOptions = m.body.indexOf(',') >= 0; + if (!isSequence && !isOptions) { + // {a},b} + if (m.post.match(/,(?!,).*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; + return expand(str); + } + return [str]; + } + + var n; + if (isSequence) { + n = m.body.split(/\.\./); + } else { + n = parseCommaParts(m.body); + if (n.length === 1) { + // x{{a,b}}y ==> x{a}y x{b}y + n = expand(n[0], false).map(embrace); + if (n.length === 1) { + var post = m.post.length + ? expand(m.post, false) + : ['']; + return post.map(function(p) { + return m.pre + n[0] + p; + }); + } + } + } + + // at this point, n is the parts, and we know it's not a comma set + // with a single entry. + + // no need to expand pre, since it is guaranteed to be free of brace-sets + var pre = m.pre; + var post = m.post.length + ? expand(m.post, false) + : ['']; + + var N; + + if (isSequence) { + var x = numeric(n[0]); + var y = numeric(n[1]); + var width = Math.max(n[0].length, n[1].length) + var incr = n.length == 3 + ? Math.abs(numeric(n[2])) + : 1; + var test = lte; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte; + } + var pad = n.some(isPadded); + + N = []; + + for (var i = x; test(i, y); i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === '\\') + c = ''; + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join('0'); + if (i < 0) + c = '-' + z + c.slice(1); + else + c = z + c; + } + } + } + N.push(c); + } + } else { + N = concatMap(n, function(el) { return expand(el, false) }); + } + + for (var j = 0; j < N.length; j++) { + for (var k = 0; k < post.length; k++) { + var expansion = pre + N[j] + post[k]; + if (!isTop || isSequence || expansion) + expansions.push(expansion); + } + } + + return expansions; +} + diff --git a/website/node_modules/brace-expansion/package.json b/website/node_modules/brace-expansion/package.json new file mode 100644 index 00000000..34478881 --- /dev/null +++ b/website/node_modules/brace-expansion/package.json @@ -0,0 +1,50 @@ +{ + "name": "brace-expansion", + "description": "Brace expansion as known from sh/bash", + "version": "1.1.12", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/brace-expansion.git" + }, + "homepage": "https://github.com/juliangruber/brace-expansion", + "main": "index.js", + "scripts": { + "test": "tape test/*.js", + "gentest": "bash test/generate.sh", + "bench": "matcha test/perf/bench.js" + }, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + }, + "devDependencies": { + "matcha": "^0.7.0", + "tape": "^4.6.0" + }, + "keywords": [], + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "license": "MIT", + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/8..latest", + "firefox/20..latest", + "firefox/nightly", + "chrome/25..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2..latest" + ] + }, + "publishConfig": { + "tag": "1.x" + } +} diff --git a/website/node_modules/bytes/History.md b/website/node_modules/bytes/History.md new file mode 100644 index 00000000..d60ce0e6 --- /dev/null +++ b/website/node_modules/bytes/History.md @@ -0,0 +1,97 @@ +3.1.2 / 2022-01-27 +================== + + * Fix return value for un-parsable strings + +3.1.1 / 2021-11-15 +================== + + * Fix "thousandsSeparator" incorrecting formatting fractional part + +3.1.0 / 2019-01-22 +================== + + * Add petabyte (`pb`) support + +3.0.0 / 2017-08-31 +================== + + * Change "kB" to "KB" in format output + * Remove support for Node.js 0.6 + * Remove support for ComponentJS + +2.5.0 / 2017-03-24 +================== + + * Add option "unit" + +2.4.0 / 2016-06-01 +================== + + * Add option "unitSeparator" + +2.3.0 / 2016-02-15 +================== + + * Drop partial bytes on all parsed units + * Fix non-finite numbers to `.format` to return `null` + * Fix parsing byte string that looks like hex + * perf: hoist regular expressions + +2.2.0 / 2015-11-13 +================== + + * add option "decimalPlaces" + * add option "fixedDecimals" + +2.1.0 / 2015-05-21 +================== + + * add `.format` export + * add `.parse` export + +2.0.2 / 2015-05-20 +================== + + * remove map recreation + * remove unnecessary object construction + +2.0.1 / 2015-05-07 +================== + + * fix browserify require + * remove node.extend dependency + +2.0.0 / 2015-04-12 +================== + + * add option "case" + * add option "thousandsSeparator" + * return "null" on invalid parse input + * support proper round-trip: bytes(bytes(num)) === num + * units no longer case sensitive when parsing + +1.0.0 / 2014-05-05 +================== + + * add negative support. fixes #6 + +0.3.0 / 2014-03-19 +================== + + * added terabyte support + +0.2.1 / 2013-04-01 +================== + + * add .component + +0.2.0 / 2012-10-28 +================== + + * bytes(200).should.eql('200b') + +0.1.0 / 2012-07-04 +================== + + * add bytes to string conversion [yields] diff --git a/website/node_modules/bytes/LICENSE b/website/node_modules/bytes/LICENSE new file mode 100644 index 00000000..63e95a96 --- /dev/null +++ b/website/node_modules/bytes/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2012-2014 TJ Holowaychuk +Copyright (c) 2015 Jed Watson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/bytes/Readme.md b/website/node_modules/bytes/Readme.md new file mode 100644 index 00000000..5790e23e --- /dev/null +++ b/website/node_modules/bytes/Readme.md @@ -0,0 +1,152 @@ +# Bytes utility + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Build Status][ci-image]][ci-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Utility to parse a string bytes (ex: `1TB`) to bytes (`1099511627776`) and vice-versa. + +## Installation + +This is a [Node.js](https://nodejs.org/en/) module available through the +[npm registry](https://www.npmjs.com/). Installation is done using the +[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): + +```bash +$ npm install bytes +``` + +## Usage + +```js +var bytes = require('bytes'); +``` + +#### bytes(number|string value, [options]): number|string|null + +Default export function. Delegates to either `bytes.format` or `bytes.parse` based on the type of `value`. + +**Arguments** + +| Name | Type | Description | +|---------|----------|--------------------| +| value | `number`|`string` | Number value to format or string value to parse | +| options | `Object` | Conversion options for `format` | + +**Returns** + +| Name | Type | Description | +|---------|------------------|-------------------------------------------------| +| results | `string`|`number`|`null` | Return null upon error. Numeric value in bytes, or string value otherwise. | + +**Example** + +```js +bytes(1024); +// output: '1KB' + +bytes('1KB'); +// output: 1024 +``` + +#### bytes.format(number value, [options]): string|null + +Format the given value in bytes into a string. If the value is negative, it is kept as such. If it is a float, it is + rounded. + +**Arguments** + +| Name | Type | Description | +|---------|----------|--------------------| +| value | `number` | Value in bytes | +| options | `Object` | Conversion options | + +**Options** + +| Property | Type | Description | +|-------------------|--------|-----------------------------------------------------------------------------------------| +| decimalPlaces | `number`|`null` | Maximum number of decimal places to include in output. Default value to `2`. | +| fixedDecimals | `boolean`|`null` | Whether to always display the maximum number of decimal places. Default value to `false` | +| thousandsSeparator | `string`|`null` | Example of values: `' '`, `','` and `'.'`... Default value to `''`. | +| unit | `string`|`null` | The unit in which the result will be returned (B/KB/MB/GB/TB). Default value to `''` (which means auto detect). | +| unitSeparator | `string`|`null` | Separator to use between number and unit. Default value to `''`. | + +**Returns** + +| Name | Type | Description | +|---------|------------------|-------------------------------------------------| +| results | `string`|`null` | Return null upon error. String value otherwise. | + +**Example** + +```js +bytes.format(1024); +// output: '1KB' + +bytes.format(1000); +// output: '1000B' + +bytes.format(1000, {thousandsSeparator: ' '}); +// output: '1 000B' + +bytes.format(1024 * 1.7, {decimalPlaces: 0}); +// output: '2KB' + +bytes.format(1024, {unitSeparator: ' '}); +// output: '1 KB' +``` + +#### bytes.parse(string|number value): number|null + +Parse the string value into an integer in bytes. If no unit is given, or `value` +is a number, it is assumed the value is in bytes. + +Supported units and abbreviations are as follows and are case-insensitive: + + * `b` for bytes + * `kb` for kilobytes + * `mb` for megabytes + * `gb` for gigabytes + * `tb` for terabytes + * `pb` for petabytes + +The units are in powers of two, not ten. This means 1kb = 1024b according to this parser. + +**Arguments** + +| Name | Type | Description | +|---------------|--------|--------------------| +| value | `string`|`number` | String to parse, or number in bytes. | + +**Returns** + +| Name | Type | Description | +|---------|-------------|-------------------------| +| results | `number`|`null` | Return null upon error. Value in bytes otherwise. | + +**Example** + +```js +bytes.parse('1KB'); +// output: 1024 + +bytes.parse('1024'); +// output: 1024 + +bytes.parse(1024); +// output: 1024 +``` + +## License + +[MIT](LICENSE) + +[ci-image]: https://badgen.net/github/checks/visionmedia/bytes.js/master?label=ci +[ci-url]: https://github.com/visionmedia/bytes.js/actions?query=workflow%3Aci +[coveralls-image]: https://badgen.net/coveralls/c/github/visionmedia/bytes.js/master +[coveralls-url]: https://coveralls.io/r/visionmedia/bytes.js?branch=master +[downloads-image]: https://badgen.net/npm/dm/bytes +[downloads-url]: https://npmjs.org/package/bytes +[npm-image]: https://badgen.net/npm/v/bytes +[npm-url]: https://npmjs.org/package/bytes diff --git a/website/node_modules/bytes/index.js b/website/node_modules/bytes/index.js new file mode 100644 index 00000000..6f2d0f89 --- /dev/null +++ b/website/node_modules/bytes/index.js @@ -0,0 +1,170 @@ +/*! + * bytes + * Copyright(c) 2012-2014 TJ Holowaychuk + * Copyright(c) 2015 Jed Watson + * MIT Licensed + */ + +'use strict'; + +/** + * Module exports. + * @public + */ + +module.exports = bytes; +module.exports.format = format; +module.exports.parse = parse; + +/** + * Module variables. + * @private + */ + +var formatThousandsRegExp = /\B(?=(\d{3})+(?!\d))/g; + +var formatDecimalsRegExp = /(?:\.0*|(\.[^0]+)0+)$/; + +var map = { + b: 1, + kb: 1 << 10, + mb: 1 << 20, + gb: 1 << 30, + tb: Math.pow(1024, 4), + pb: Math.pow(1024, 5), +}; + +var parseRegExp = /^((-|\+)?(\d+(?:\.\d+)?)) *(kb|mb|gb|tb|pb)$/i; + +/** + * Convert the given value in bytes into a string or parse to string to an integer in bytes. + * + * @param {string|number} value + * @param {{ + * case: [string], + * decimalPlaces: [number] + * fixedDecimals: [boolean] + * thousandsSeparator: [string] + * unitSeparator: [string] + * }} [options] bytes options. + * + * @returns {string|number|null} + */ + +function bytes(value, options) { + if (typeof value === 'string') { + return parse(value); + } + + if (typeof value === 'number') { + return format(value, options); + } + + return null; +} + +/** + * Format the given value in bytes into a string. + * + * If the value is negative, it is kept as such. If it is a float, + * it is rounded. + * + * @param {number} value + * @param {object} [options] + * @param {number} [options.decimalPlaces=2] + * @param {number} [options.fixedDecimals=false] + * @param {string} [options.thousandsSeparator=] + * @param {string} [options.unit=] + * @param {string} [options.unitSeparator=] + * + * @returns {string|null} + * @public + */ + +function format(value, options) { + if (!Number.isFinite(value)) { + return null; + } + + var mag = Math.abs(value); + var thousandsSeparator = (options && options.thousandsSeparator) || ''; + var unitSeparator = (options && options.unitSeparator) || ''; + var decimalPlaces = (options && options.decimalPlaces !== undefined) ? options.decimalPlaces : 2; + var fixedDecimals = Boolean(options && options.fixedDecimals); + var unit = (options && options.unit) || ''; + + if (!unit || !map[unit.toLowerCase()]) { + if (mag >= map.pb) { + unit = 'PB'; + } else if (mag >= map.tb) { + unit = 'TB'; + } else if (mag >= map.gb) { + unit = 'GB'; + } else if (mag >= map.mb) { + unit = 'MB'; + } else if (mag >= map.kb) { + unit = 'KB'; + } else { + unit = 'B'; + } + } + + var val = value / map[unit.toLowerCase()]; + var str = val.toFixed(decimalPlaces); + + if (!fixedDecimals) { + str = str.replace(formatDecimalsRegExp, '$1'); + } + + if (thousandsSeparator) { + str = str.split('.').map(function (s, i) { + return i === 0 + ? s.replace(formatThousandsRegExp, thousandsSeparator) + : s + }).join('.'); + } + + return str + unitSeparator + unit; +} + +/** + * Parse the string value into an integer in bytes. + * + * If no unit is given, it is assumed the value is in bytes. + * + * @param {number|string} val + * + * @returns {number|null} + * @public + */ + +function parse(val) { + if (typeof val === 'number' && !isNaN(val)) { + return val; + } + + if (typeof val !== 'string') { + return null; + } + + // Test if the string passed is valid + var results = parseRegExp.exec(val); + var floatValue; + var unit = 'b'; + + if (!results) { + // Nothing could be extracted from the given string + floatValue = parseInt(val, 10); + unit = 'b' + } else { + // Retrieve the value and the unit + floatValue = parseFloat(results[1]); + unit = results[4].toLowerCase(); + } + + if (isNaN(floatValue)) { + return null; + } + + return Math.floor(map[unit] * floatValue); +} diff --git a/website/node_modules/bytes/package.json b/website/node_modules/bytes/package.json new file mode 100644 index 00000000..f2b6a8b0 --- /dev/null +++ b/website/node_modules/bytes/package.json @@ -0,0 +1,42 @@ +{ + "name": "bytes", + "description": "Utility to parse a string bytes to bytes and vice-versa", + "version": "3.1.2", + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "contributors": [ + "Jed Watson ", + "Théo FIDRY " + ], + "license": "MIT", + "keywords": [ + "byte", + "bytes", + "utility", + "parse", + "parser", + "convert", + "converter" + ], + "repository": "visionmedia/bytes.js", + "devDependencies": { + "eslint": "7.32.0", + "eslint-plugin-markdown": "2.2.1", + "mocha": "9.2.0", + "nyc": "15.1.0" + }, + "files": [ + "History.md", + "LICENSE", + "Readme.md", + "index.js" + ], + "engines": { + "node": ">= 0.8" + }, + "scripts": { + "lint": "eslint .", + "test": "mocha --check-leaks --reporter spec", + "test-ci": "nyc --reporter=lcov --reporter=text npm test", + "test-cov": "nyc --reporter=html --reporter=text npm test" + } +} diff --git a/website/node_modules/camelcase/index.d.ts b/website/node_modules/camelcase/index.d.ts new file mode 100644 index 00000000..9d519ec5 --- /dev/null +++ b/website/node_modules/camelcase/index.d.ts @@ -0,0 +1,102 @@ +export type Options = { + /** + Uppercase the first character: `foo-bar` → `FooBar`. + + @default false + */ + readonly pascalCase?: boolean; + + /** + Preserve consecutive uppercase characters: `foo-BAR` → `FooBAR`. + + @default false + */ + readonly preserveConsecutiveUppercase?: boolean; + + /** + The locale parameter indicates the locale to be used to convert to upper/lower case according to any locale-specific case mappings. If multiple locales are given in an array, the best available locale is used. + + Setting `locale: false` ignores the platform locale and uses the [Unicode Default Case Conversion](https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#simple-single-character-case-mapping) algorithm. + + Default: The host environment’s current locale. + + @example + ``` + import camelCase from 'camelcase'; + + camelCase('lorem-ipsum', {locale: 'en-US'}); + //=> 'loremIpsum' + + camelCase('lorem-ipsum', {locale: 'tr-TR'}); + //=> 'loremİpsum' + + camelCase('lorem-ipsum', {locale: ['en-US', 'en-GB']}); + //=> 'loremIpsum' + + camelCase('lorem-ipsum', {locale: ['tr', 'TR', 'tr-TR']}); + //=> 'loremİpsum' + ``` + */ + readonly locale?: false | string | readonly string[]; +}; + +/** +Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: `foo-bar` → `fooBar`. + +Correctly handles Unicode strings. + +@param input - String to convert to camel case. + +@example +``` +import camelCase from 'camelcase'; + +camelCase('foo-bar'); +//=> 'fooBar' + +camelCase('foo_bar'); +//=> 'fooBar' + +camelCase('Foo-Bar'); +//=> 'fooBar' + +camelCase('розовый_пушистый_единорог'); +//=> 'розовыйПушистыйЕдинорог' + +camelCase('Foo-Bar', {pascalCase: true}); +//=> 'FooBar' + +camelCase('--foo.bar', {pascalCase: false}); +//=> 'fooBar' + +camelCase('Foo-BAR', {preserveConsecutiveUppercase: true}); +//=> 'fooBAR' + +camelCase('fooBAR', {pascalCase: true, preserveConsecutiveUppercase: true})); +//=> 'FooBAR' + +camelCase('foo bar'); +//=> 'fooBar' + +console.log(process.argv[3]); +//=> '--foo-bar' +camelCase(process.argv[3]); +//=> 'fooBar' + +camelCase(['foo', 'bar']); +//=> 'fooBar' + +camelCase(['__foo__', '--bar'], {pascalCase: true}); +//=> 'FooBar' + +camelCase(['foo', 'BAR'], {pascalCase: true, preserveConsecutiveUppercase: true}) +//=> 'FooBAR' + +camelCase('lorem-ipsum', {locale: 'en-US'}); +//=> 'loremIpsum' +``` +*/ +export default function camelcase( + input: string | readonly string[], + options?: Options +): string; diff --git a/website/node_modules/camelcase/index.js b/website/node_modules/camelcase/index.js new file mode 100644 index 00000000..6d803165 --- /dev/null +++ b/website/node_modules/camelcase/index.js @@ -0,0 +1,109 @@ +const UPPERCASE = /[\p{Lu}]/u; +const LOWERCASE = /[\p{Ll}]/u; +const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu; +const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u; +const SEPARATORS = /[_.\- ]+/; + +const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source); +const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu'); +const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu'); + +const preserveCamelCase = (string, toLowerCase, toUpperCase, preserveConsecutiveUppercase) => { + let isLastCharLower = false; + let isLastCharUpper = false; + let isLastLastCharUpper = false; + let isLastLastCharPreserved = false; + + for (let index = 0; index < string.length; index++) { + const character = string[index]; + isLastLastCharPreserved = index > 2 ? string[index - 3] === '-' : true; + + if (isLastCharLower && UPPERCASE.test(character)) { + string = string.slice(0, index) + '-' + string.slice(index); + isLastCharLower = false; + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = true; + index++; + } else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character) && (!isLastLastCharPreserved || preserveConsecutiveUppercase)) { + string = string.slice(0, index - 1) + '-' + string.slice(index - 1); + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = false; + isLastCharLower = true; + } else { + isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character; + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character; + } + } + + return string; +}; + +const preserveConsecutiveUppercase = (input, toLowerCase) => { + LEADING_CAPITAL.lastIndex = 0; + + return input.replace(LEADING_CAPITAL, m1 => toLowerCase(m1)); +}; + +const postProcess = (input, toUpperCase) => { + SEPARATORS_AND_IDENTIFIER.lastIndex = 0; + NUMBERS_AND_IDENTIFIER.lastIndex = 0; + + return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier)) + .replace(NUMBERS_AND_IDENTIFIER, m => toUpperCase(m)); +}; + +export default function camelCase(input, options) { + if (!(typeof input === 'string' || Array.isArray(input))) { + throw new TypeError('Expected the input to be `string | string[]`'); + } + + options = { + pascalCase: false, + preserveConsecutiveUppercase: false, + ...options, + }; + + if (Array.isArray(input)) { + input = input.map(x => x.trim()) + .filter(x => x.length) + .join('-'); + } else { + input = input.trim(); + } + + if (input.length === 0) { + return ''; + } + + const toLowerCase = options.locale === false + ? string => string.toLowerCase() + : string => string.toLocaleLowerCase(options.locale); + + const toUpperCase = options.locale === false + ? string => string.toUpperCase() + : string => string.toLocaleUpperCase(options.locale); + + if (input.length === 1) { + if (SEPARATORS.test(input)) { + return ''; + } + + return options.pascalCase ? toUpperCase(input) : toLowerCase(input); + } + + const hasUpperCase = input !== toLowerCase(input); + + if (hasUpperCase) { + input = preserveCamelCase(input, toLowerCase, toUpperCase, options.preserveConsecutiveUppercase); + } + + input = input.replace(LEADING_SEPARATORS, ''); + input = options.preserveConsecutiveUppercase ? preserveConsecutiveUppercase(input, toLowerCase) : toLowerCase(input); + + if (options.pascalCase) { + input = toUpperCase(input.charAt(0)) + input.slice(1); + } + + return postProcess(input, toUpperCase); +} diff --git a/website/node_modules/camelcase/license b/website/node_modules/camelcase/license new file mode 100644 index 00000000..fa7ceba3 --- /dev/null +++ b/website/node_modules/camelcase/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/camelcase/package.json b/website/node_modules/camelcase/package.json new file mode 100644 index 00000000..b62c0a63 --- /dev/null +++ b/website/node_modules/camelcase/package.json @@ -0,0 +1,47 @@ +{ + "name": "camelcase", + "version": "7.0.1", + "description": "Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: `foo-bar` → `fooBar`", + "license": "MIT", + "repository": "sindresorhus/camelcase", + "funding": "https://github.com/sponsors/sindresorhus", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "type": "module", + "exports": "./index.js", + "types": "./index.d.ts", + "engines": { + "node": ">=14.16" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "camelcase", + "camel-case", + "camel", + "case", + "dash", + "hyphen", + "dot", + "underscore", + "separator", + "string", + "text", + "convert", + "pascalcase", + "pascal-case" + ], + "devDependencies": { + "ava": "^4.3.0", + "tsd": "^0.20.0", + "xo": "^0.49.0" + } +} diff --git a/website/node_modules/camelcase/readme.md b/website/node_modules/camelcase/readme.md new file mode 100644 index 00000000..ef8ef972 --- /dev/null +++ b/website/node_modules/camelcase/readme.md @@ -0,0 +1,142 @@ +# camelcase + +> Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: `foo-bar` → `fooBar` + +Correctly handles Unicode strings. + +If you use this on untrusted user input, don't forget to limit the length to something reasonable. + +## Install + +```sh +npm install camelcase +``` + +## Usage + +```js +import camelCase from 'camelcase'; + +camelCase('foo-bar'); +//=> 'fooBar' + +camelCase('foo_bar'); +//=> 'fooBar' + +camelCase('Foo-Bar'); +//=> 'fooBar' + +camelCase('розовый_пушистый_единорог'); +//=> 'розовыйПушистыйЕдинорог' + +camelCase('Foo-Bar', {pascalCase: true}); +//=> 'FooBar' + +camelCase('--foo.bar', {pascalCase: false}); +//=> 'fooBar' + +camelCase('Foo-BAR', {preserveConsecutiveUppercase: true}); +//=> 'fooBAR' + +camelCase('fooBAR', {pascalCase: true, preserveConsecutiveUppercase: true})); +//=> 'FooBAR' + +camelCase('foo bar'); +//=> 'fooBar' + +console.log(process.argv[3]); +//=> '--foo-bar' +camelCase(process.argv[3]); +//=> 'fooBar' + +camelCase(['foo', 'bar']); +//=> 'fooBar' + +camelCase(['__foo__', '--bar'], {pascalCase: true}); +//=> 'FooBar' + +camelCase(['foo', 'BAR'], {pascalCase: true, preserveConsecutiveUppercase: true}) +//=> 'FooBAR' + +camelCase('lorem-ipsum', {locale: 'en-US'}); +//=> 'loremIpsum' +``` + +## API + +### camelCase(input, options?) + +#### input + +Type: `string | string[]` + +String to convert to camel case. + +#### options + +Type: `object` + +##### pascalCase + +Type: `boolean`\ +Default: `false` + +Uppercase the first character: `foo-bar` → `FooBar` + +##### preserveConsecutiveUppercase + +Type: `boolean`\ +Default: `false` + +Preserve consecutive uppercase characters: `foo-BAR` → `FooBAR`. + +##### locale + +Type: `false | string | string[]`\ +Default: The host environment’s current locale. + +The locale parameter indicates the locale to be used to convert to upper/lower case according to any locale-specific case mappings. If multiple locales are given in an array, the best available locale is used. + +```js +import camelCase from 'camelcase'; + +camelCase('lorem-ipsum', {locale: 'en-US'}); +//=> 'loremIpsum' + +camelCase('lorem-ipsum', {locale: 'tr-TR'}); +//=> 'loremİpsum' + +camelCase('lorem-ipsum', {locale: ['en-US', 'en-GB']}); +//=> 'loremIpsum' + +camelCase('lorem-ipsum', {locale: ['tr', 'TR', 'tr-TR']}); +//=> 'loremİpsum' +``` + +Setting `locale: false` ignores the platform locale and uses the [Unicode Default Case Conversion](https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#simple-single-character-case-mapping) algorithm: + +```js +import camelCase from 'camelcase'; + +// On a platform with 'tr-TR' + +camelCase('lorem-ipsum'); +//=> 'loremİpsum' + +camelCase('lorem-ipsum', {locale: false}); +//=> 'loremIpsum' +``` + +## camelcase for enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of camelcase and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-camelcase?utm_source=npm-camelcase&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + +## Related + +- [decamelize](https://github.com/sindresorhus/decamelize) - The inverse of this module +- [uppercamelcase](https://github.com/SamVerschueren/uppercamelcase) - Like this module, but to PascalCase instead of camelCase +- [titleize](https://github.com/sindresorhus/titleize) - Capitalize every word in string +- [humanize-string](https://github.com/sindresorhus/humanize-string) - Convert a camelized/dasherized/underscored string into a humanized one +- [camelcase-keys](https://github.com/sindresorhus/camelcase-keys) - Convert object keys to camel case diff --git a/website/node_modules/chalk-template/index.d.ts b/website/node_modules/chalk-template/index.d.ts new file mode 100644 index 00000000..10bfe71f --- /dev/null +++ b/website/node_modules/chalk-template/index.d.ts @@ -0,0 +1,35 @@ +/** +Terminal string styling with [tagged template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) + +@example +``` +import chalkTemplate from 'chalk-template'; + +log(chalkTemplate` +CPU: {red ${cpu.totalPercent}%} +RAM: {green ${ram.used / ram.total * 100}%} +DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%} +`); +``` + +@example +``` +import chalkTemplate from 'chalk-template'; +import chalk from 'chalk'; + +log(chalk.red.bgBlack(chalkTemplate`2 + 3 = {bold ${2 + 3}}`)); +``` +*/ +export default function chalkTemplate(text: TemplateStringsArray, ...placeholders: unknown[]): string; + +/** +Terminal string styling. It is preferred that you use the template tag (default export) but this function is useful if you'd like to wrap the color template function. + +@example +``` +import { template } from 'chalk-template'; + +log(template('Today is {red hot}') +``` +*/ +export function template(text: string): string; diff --git a/website/node_modules/chalk-template/index.js b/website/node_modules/chalk-template/index.js new file mode 100644 index 00000000..7686387e --- /dev/null +++ b/website/node_modules/chalk-template/index.js @@ -0,0 +1,172 @@ +import chalk from 'chalk'; + +// eslint-disable-next-line unicorn/better-regex +const TEMPLATE_REGEX = /(?:\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.))|(?:{(~)?(#?[\w:]+(?:\([^)]*\))?(?:\.#?[\w:]+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(})|((?:.|[\r\n\f])+?)/gi; +const STYLE_REGEX = /(?:^|\.)(?:(?:(\w+)(?:\(([^)]*)\))?)|(?:#(?=[:a-fA-F\d]{2,})([a-fA-F\d]{6})?(?::([a-fA-F\d]{6}))?))/g; +const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; +const ESCAPE_REGEX = /\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi; + +const ESCAPES = new Map([ + ['n', '\n'], + ['r', '\r'], + ['t', '\t'], + ['b', '\b'], + ['f', '\f'], + ['v', '\v'], + ['0', '\0'], + ['\\', '\\'], + ['e', '\u001B'], + ['a', '\u0007'], +]); + +function unescape(c) { + const u = c[0] === 'u'; + const bracket = c[1] === '{'; + + if ((u && !bracket && c.length === 5) || (c[0] === 'x' && c.length === 3)) { + return String.fromCharCode(Number.parseInt(c.slice(1), 16)); + } + + if (u && bracket) { + return String.fromCodePoint(Number.parseInt(c.slice(2, -1), 16)); + } + + return ESCAPES.get(c) || c; +} + +function parseArguments(name, arguments_) { + const results = []; + const chunks = arguments_.trim().split(/\s*,\s*/g); + let matches; + + for (const chunk of chunks) { + const number = Number(chunk); + if (!Number.isNaN(number)) { + results.push(number); + } else if ((matches = chunk.match(STRING_REGEX))) { + results.push(matches[2].replace(ESCAPE_REGEX, (_, escape, character) => escape ? unescape(escape) : character)); + } else { + throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); + } + } + + return results; +} + +function parseHex(hex) { + const n = Number.parseInt(hex, 16); + return [ + // eslint-disable-next-line no-bitwise + (n >> 16) & 0xFF, + // eslint-disable-next-line no-bitwise + (n >> 8) & 0xFF, + // eslint-disable-next-line no-bitwise + n & 0xFF, + ]; +} + +function parseStyle(style) { + STYLE_REGEX.lastIndex = 0; + + const results = []; + let matches; + + while ((matches = STYLE_REGEX.exec(style)) !== null) { + const name = matches[1]; + + if (matches[2]) { + results.push([name, ...parseArguments(name, matches[2])]); + } else if (matches[3] || matches[4]) { + if (matches[3]) { + results.push(['rgb', ...parseHex(matches[3])]); + } + + if (matches[4]) { + results.push(['bgRgb', ...parseHex(matches[4])]); + } + } else { + results.push([name]); + } + } + + return results; +} + +function buildStyle(styles) { + const enabled = {}; + + for (const layer of styles) { + for (const style of layer.styles) { + enabled[style[0]] = layer.inverse ? null : style.slice(1); + } + } + + let current = chalk; + for (const [styleName, styles] of Object.entries(enabled)) { + if (!Array.isArray(styles)) { + continue; + } + + if (!(styleName in current)) { + throw new Error(`Unknown Chalk style: ${styleName}`); + } + + current = styles.length > 0 ? current[styleName](...styles) : current[styleName]; + } + + return current; +} + +export function template(string) { + const styles = []; + const chunks = []; + let chunk = []; + + // eslint-disable-next-line max-params + string.replace(TEMPLATE_REGEX, (_, escapeCharacter, inverse, style, close, character) => { + if (escapeCharacter) { + chunk.push(unescape(escapeCharacter)); + } else if (style) { + const string = chunk.join(''); + chunk = []; + chunks.push(styles.length === 0 ? string : buildStyle(styles)(string)); + styles.push({inverse, styles: parseStyle(style)}); + } else if (close) { + if (styles.length === 0) { + throw new Error('Found extraneous } in Chalk template literal'); + } + + chunks.push(buildStyle(styles)(chunk.join(''))); + chunk = []; + styles.pop(); + } else { + chunk.push(character); + } + }); + + chunks.push(chunk.join('')); + + if (styles.length > 0) { + throw new Error(`Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`); + } + + return chunks.join(''); +} + +export default function chalkTemplate(firstString, ...arguments_) { + if (!Array.isArray(firstString) || !Array.isArray(firstString.raw)) { + // If chalkTemplate() was called by itself or with a string + throw new TypeError('A tagged template literal must be provided'); + } + + const parts = [firstString.raw[0]]; + + for (let index = 1; index < firstString.raw.length; index++) { + parts.push( + String(arguments_[index - 1]).replace(/[{}\\]/g, '\\$&'), + String(firstString.raw[index]), + ); + } + + return template(parts.join('')); +} diff --git a/website/node_modules/chalk-template/license b/website/node_modules/chalk-template/license new file mode 100644 index 00000000..84ce2880 --- /dev/null +++ b/website/node_modules/chalk-template/license @@ -0,0 +1,10 @@ +MIT License + +Copyright (c) Josh Junon +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/chalk-template/node_modules/ansi-styles/index.d.ts b/website/node_modules/chalk-template/node_modules/ansi-styles/index.d.ts new file mode 100644 index 00000000..44a907e5 --- /dev/null +++ b/website/node_modules/chalk-template/node_modules/ansi-styles/index.d.ts @@ -0,0 +1,345 @@ +declare type CSSColor = + | 'aliceblue' + | 'antiquewhite' + | 'aqua' + | 'aquamarine' + | 'azure' + | 'beige' + | 'bisque' + | 'black' + | 'blanchedalmond' + | 'blue' + | 'blueviolet' + | 'brown' + | 'burlywood' + | 'cadetblue' + | 'chartreuse' + | 'chocolate' + | 'coral' + | 'cornflowerblue' + | 'cornsilk' + | 'crimson' + | 'cyan' + | 'darkblue' + | 'darkcyan' + | 'darkgoldenrod' + | 'darkgray' + | 'darkgreen' + | 'darkgrey' + | 'darkkhaki' + | 'darkmagenta' + | 'darkolivegreen' + | 'darkorange' + | 'darkorchid' + | 'darkred' + | 'darksalmon' + | 'darkseagreen' + | 'darkslateblue' + | 'darkslategray' + | 'darkslategrey' + | 'darkturquoise' + | 'darkviolet' + | 'deeppink' + | 'deepskyblue' + | 'dimgray' + | 'dimgrey' + | 'dodgerblue' + | 'firebrick' + | 'floralwhite' + | 'forestgreen' + | 'fuchsia' + | 'gainsboro' + | 'ghostwhite' + | 'gold' + | 'goldenrod' + | 'gray' + | 'green' + | 'greenyellow' + | 'grey' + | 'honeydew' + | 'hotpink' + | 'indianred' + | 'indigo' + | 'ivory' + | 'khaki' + | 'lavender' + | 'lavenderblush' + | 'lawngreen' + | 'lemonchiffon' + | 'lightblue' + | 'lightcoral' + | 'lightcyan' + | 'lightgoldenrodyellow' + | 'lightgray' + | 'lightgreen' + | 'lightgrey' + | 'lightpink' + | 'lightsalmon' + | 'lightseagreen' + | 'lightskyblue' + | 'lightslategray' + | 'lightslategrey' + | 'lightsteelblue' + | 'lightyellow' + | 'lime' + | 'limegreen' + | 'linen' + | 'magenta' + | 'maroon' + | 'mediumaquamarine' + | 'mediumblue' + | 'mediumorchid' + | 'mediumpurple' + | 'mediumseagreen' + | 'mediumslateblue' + | 'mediumspringgreen' + | 'mediumturquoise' + | 'mediumvioletred' + | 'midnightblue' + | 'mintcream' + | 'mistyrose' + | 'moccasin' + | 'navajowhite' + | 'navy' + | 'oldlace' + | 'olive' + | 'olivedrab' + | 'orange' + | 'orangered' + | 'orchid' + | 'palegoldenrod' + | 'palegreen' + | 'paleturquoise' + | 'palevioletred' + | 'papayawhip' + | 'peachpuff' + | 'peru' + | 'pink' + | 'plum' + | 'powderblue' + | 'purple' + | 'rebeccapurple' + | 'red' + | 'rosybrown' + | 'royalblue' + | 'saddlebrown' + | 'salmon' + | 'sandybrown' + | 'seagreen' + | 'seashell' + | 'sienna' + | 'silver' + | 'skyblue' + | 'slateblue' + | 'slategray' + | 'slategrey' + | 'snow' + | 'springgreen' + | 'steelblue' + | 'tan' + | 'teal' + | 'thistle' + | 'tomato' + | 'turquoise' + | 'violet' + | 'wheat' + | 'white' + | 'whitesmoke' + | 'yellow' + | 'yellowgreen'; + +declare namespace ansiStyles { + interface ColorConvert { + /** + The RGB color space. + + @param red - (`0`-`255`) + @param green - (`0`-`255`) + @param blue - (`0`-`255`) + */ + rgb(red: number, green: number, blue: number): string; + + /** + The RGB HEX color space. + + @param hex - A hexadecimal string containing RGB data. + */ + hex(hex: string): string; + + /** + @param keyword - A CSS color name. + */ + keyword(keyword: CSSColor): string; + + /** + The HSL color space. + + @param hue - (`0`-`360`) + @param saturation - (`0`-`100`) + @param lightness - (`0`-`100`) + */ + hsl(hue: number, saturation: number, lightness: number): string; + + /** + The HSV color space. + + @param hue - (`0`-`360`) + @param saturation - (`0`-`100`) + @param value - (`0`-`100`) + */ + hsv(hue: number, saturation: number, value: number): string; + + /** + The HSV color space. + + @param hue - (`0`-`360`) + @param whiteness - (`0`-`100`) + @param blackness - (`0`-`100`) + */ + hwb(hue: number, whiteness: number, blackness: number): string; + + /** + Use a [4-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4-bit) to set text color. + */ + ansi(ansi: number): string; + + /** + Use an [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set text color. + */ + ansi256(ansi: number): string; + } + + interface CSPair { + /** + The ANSI terminal control sequence for starting this style. + */ + readonly open: string; + + /** + The ANSI terminal control sequence for ending this style. + */ + readonly close: string; + } + + interface ColorBase { + readonly ansi: ColorConvert; + readonly ansi256: ColorConvert; + readonly ansi16m: ColorConvert; + + /** + The ANSI terminal control sequence for ending this color. + */ + readonly close: string; + } + + interface Modifier { + /** + Resets the current color chain. + */ + readonly reset: CSPair; + + /** + Make text bold. + */ + readonly bold: CSPair; + + /** + Emitting only a small amount of light. + */ + readonly dim: CSPair; + + /** + Make text italic. (Not widely supported) + */ + readonly italic: CSPair; + + /** + Make text underline. (Not widely supported) + */ + readonly underline: CSPair; + + /** + Inverse background and foreground colors. + */ + readonly inverse: CSPair; + + /** + Prints the text, but makes it invisible. + */ + readonly hidden: CSPair; + + /** + Puts a horizontal line through the center of the text. (Not widely supported) + */ + readonly strikethrough: CSPair; + } + + interface ForegroundColor { + readonly black: CSPair; + readonly red: CSPair; + readonly green: CSPair; + readonly yellow: CSPair; + readonly blue: CSPair; + readonly cyan: CSPair; + readonly magenta: CSPair; + readonly white: CSPair; + + /** + Alias for `blackBright`. + */ + readonly gray: CSPair; + + /** + Alias for `blackBright`. + */ + readonly grey: CSPair; + + readonly blackBright: CSPair; + readonly redBright: CSPair; + readonly greenBright: CSPair; + readonly yellowBright: CSPair; + readonly blueBright: CSPair; + readonly cyanBright: CSPair; + readonly magentaBright: CSPair; + readonly whiteBright: CSPair; + } + + interface BackgroundColor { + readonly bgBlack: CSPair; + readonly bgRed: CSPair; + readonly bgGreen: CSPair; + readonly bgYellow: CSPair; + readonly bgBlue: CSPair; + readonly bgCyan: CSPair; + readonly bgMagenta: CSPair; + readonly bgWhite: CSPair; + + /** + Alias for `bgBlackBright`. + */ + readonly bgGray: CSPair; + + /** + Alias for `bgBlackBright`. + */ + readonly bgGrey: CSPair; + + readonly bgBlackBright: CSPair; + readonly bgRedBright: CSPair; + readonly bgGreenBright: CSPair; + readonly bgYellowBright: CSPair; + readonly bgBlueBright: CSPair; + readonly bgCyanBright: CSPair; + readonly bgMagentaBright: CSPair; + readonly bgWhiteBright: CSPair; + } +} + +declare const ansiStyles: { + readonly modifier: ansiStyles.Modifier; + readonly color: ansiStyles.ForegroundColor & ansiStyles.ColorBase; + readonly bgColor: ansiStyles.BackgroundColor & ansiStyles.ColorBase; + readonly codes: ReadonlyMap; +} & ansiStyles.BackgroundColor & ansiStyles.ForegroundColor & ansiStyles.Modifier; + +export = ansiStyles; diff --git a/website/node_modules/chalk-template/node_modules/ansi-styles/index.js b/website/node_modules/chalk-template/node_modules/ansi-styles/index.js new file mode 100644 index 00000000..5d82581a --- /dev/null +++ b/website/node_modules/chalk-template/node_modules/ansi-styles/index.js @@ -0,0 +1,163 @@ +'use strict'; + +const wrapAnsi16 = (fn, offset) => (...args) => { + const code = fn(...args); + return `\u001B[${code + offset}m`; +}; + +const wrapAnsi256 = (fn, offset) => (...args) => { + const code = fn(...args); + return `\u001B[${38 + offset};5;${code}m`; +}; + +const wrapAnsi16m = (fn, offset) => (...args) => { + const rgb = fn(...args); + return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +}; + +const ansi2ansi = n => n; +const rgb2rgb = (r, g, b) => [r, g, b]; + +const setLazyProperty = (object, property, get) => { + Object.defineProperty(object, property, { + get: () => { + const value = get(); + + Object.defineProperty(object, property, { + value, + enumerable: true, + configurable: true + }); + + return value; + }, + enumerable: true, + configurable: true + }); +}; + +/** @type {typeof import('color-convert')} */ +let colorConvert; +const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => { + if (colorConvert === undefined) { + colorConvert = require('color-convert'); + } + + const offset = isBackground ? 10 : 0; + const styles = {}; + + for (const [sourceSpace, suite] of Object.entries(colorConvert)) { + const name = sourceSpace === 'ansi16' ? 'ansi' : sourceSpace; + if (sourceSpace === targetSpace) { + styles[name] = wrap(identity, offset); + } else if (typeof suite === 'object') { + styles[name] = wrap(suite[targetSpace], offset); + } + } + + return styles; +}; + +function assembleStyles() { + const codes = new Map(); + const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29] + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + + // Bright color + blackBright: [90, 39], + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39] + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], + + // Bright color + bgBlackBright: [100, 49], + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49] + } + }; + + // Alias bright black as gray (and grey) + styles.color.gray = styles.color.blackBright; + styles.bgColor.bgGray = styles.bgColor.bgBlackBright; + styles.color.grey = styles.color.blackBright; + styles.bgColor.bgGrey = styles.bgColor.bgBlackBright; + + for (const [groupName, group] of Object.entries(styles)) { + for (const [styleName, style] of Object.entries(group)) { + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m` + }; + + group[styleName] = styles[styleName]; + + codes.set(style[0], style[1]); + } + + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); + } + + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); + + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; + + setLazyProperty(styles.color, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, false)); + setLazyProperty(styles.color, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, false)); + setLazyProperty(styles.color, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, false)); + setLazyProperty(styles.bgColor, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, true)); + setLazyProperty(styles.bgColor, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, true)); + setLazyProperty(styles.bgColor, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, true)); + + return styles; +} + +// Make the export immutable +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); diff --git a/website/node_modules/chalk-template/node_modules/ansi-styles/license b/website/node_modules/chalk-template/node_modules/ansi-styles/license new file mode 100644 index 00000000..e7af2f77 --- /dev/null +++ b/website/node_modules/chalk-template/node_modules/ansi-styles/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/chalk-template/node_modules/ansi-styles/package.json b/website/node_modules/chalk-template/node_modules/ansi-styles/package.json new file mode 100644 index 00000000..75393284 --- /dev/null +++ b/website/node_modules/chalk-template/node_modules/ansi-styles/package.json @@ -0,0 +1,56 @@ +{ + "name": "ansi-styles", + "version": "4.3.0", + "description": "ANSI escape codes for styling strings in the terminal", + "license": "MIT", + "repository": "chalk/ansi-styles", + "funding": "https://github.com/chalk/ansi-styles?sponsor=1", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=8" + }, + "scripts": { + "test": "xo && ava && tsd", + "screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "dependencies": { + "color-convert": "^2.0.1" + }, + "devDependencies": { + "@types/color-convert": "^1.9.0", + "ava": "^2.3.0", + "svg-term-cli": "^2.1.1", + "tsd": "^0.11.0", + "xo": "^0.25.3" + } +} diff --git a/website/node_modules/chalk-template/node_modules/ansi-styles/readme.md b/website/node_modules/chalk-template/node_modules/ansi-styles/readme.md new file mode 100644 index 00000000..24883de8 --- /dev/null +++ b/website/node_modules/chalk-template/node_modules/ansi-styles/readme.md @@ -0,0 +1,152 @@ +# ansi-styles [![Build Status](https://travis-ci.org/chalk/ansi-styles.svg?branch=master)](https://travis-ci.org/chalk/ansi-styles) + +> [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal + +You probably want the higher-level [chalk](https://github.com/chalk/chalk) module for styling your strings. + + + +## Install + +``` +$ npm install ansi-styles +``` + +## Usage + +```js +const style = require('ansi-styles'); + +console.log(`${style.green.open}Hello world!${style.green.close}`); + + +// Color conversion between 16/256/truecolor +// NOTE: If conversion goes to 16 colors or 256 colors, the original color +// may be degraded to fit that color palette. This means terminals +// that do not support 16 million colors will best-match the +// original color. +console.log(style.bgColor.ansi.hsl(120, 80, 72) + 'Hello world!' + style.bgColor.close); +console.log(style.color.ansi256.rgb(199, 20, 250) + 'Hello world!' + style.color.close); +console.log(style.color.ansi16m.hex('#abcdef') + 'Hello world!' + style.color.close); +``` + +## API + +Each style has an `open` and `close` property. + +## Styles + +### Modifiers + +- `reset` +- `bold` +- `dim` +- `italic` *(Not widely supported)* +- `underline` +- `inverse` +- `hidden` +- `strikethrough` *(Not widely supported)* + +### Colors + +- `black` +- `red` +- `green` +- `yellow` +- `blue` +- `magenta` +- `cyan` +- `white` +- `blackBright` (alias: `gray`, `grey`) +- `redBright` +- `greenBright` +- `yellowBright` +- `blueBright` +- `magentaBright` +- `cyanBright` +- `whiteBright` + +### Background colors + +- `bgBlack` +- `bgRed` +- `bgGreen` +- `bgYellow` +- `bgBlue` +- `bgMagenta` +- `bgCyan` +- `bgWhite` +- `bgBlackBright` (alias: `bgGray`, `bgGrey`) +- `bgRedBright` +- `bgGreenBright` +- `bgYellowBright` +- `bgBlueBright` +- `bgMagentaBright` +- `bgCyanBright` +- `bgWhiteBright` + +## Advanced usage + +By default, you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module. + +- `style.modifier` +- `style.color` +- `style.bgColor` + +###### Example + +```js +console.log(style.color.green.open); +``` + +Raw escape codes (i.e. without the CSI escape prefix `\u001B[` and render mode postfix `m`) are available under `style.codes`, which returns a `Map` with the open codes as keys and close codes as values. + +###### Example + +```js +console.log(style.codes.get(36)); +//=> 39 +``` + +## [256 / 16 million (TrueColor) support](https://gist.github.com/XVilka/8346728) + +`ansi-styles` uses the [`color-convert`](https://github.com/Qix-/color-convert) package to allow for converting between various colors and ANSI escapes, with support for 256 and 16 million colors. + +The following color spaces from `color-convert` are supported: + +- `rgb` +- `hex` +- `keyword` +- `hsl` +- `hsv` +- `hwb` +- `ansi` +- `ansi256` + +To use these, call the associated conversion function with the intended output, for example: + +```js +style.color.ansi.rgb(100, 200, 15); // RGB to 16 color ansi foreground code +style.bgColor.ansi.rgb(100, 200, 15); // RGB to 16 color ansi background code + +style.color.ansi256.hsl(120, 100, 60); // HSL to 256 color ansi foreground code +style.bgColor.ansi256.hsl(120, 100, 60); // HSL to 256 color ansi foreground code + +style.color.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color foreground code +style.bgColor.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color background code +``` + +## Related + +- [ansi-escapes](https://github.com/sindresorhus/ansi-escapes) - ANSI escape codes for manipulating the terminal + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + +## For enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of `ansi-styles` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-ansi-styles?utm_source=npm-ansi-styles&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) diff --git a/website/node_modules/chalk-template/node_modules/chalk/index.d.ts b/website/node_modules/chalk-template/node_modules/chalk/index.d.ts new file mode 100644 index 00000000..9cd88f38 --- /dev/null +++ b/website/node_modules/chalk-template/node_modules/chalk/index.d.ts @@ -0,0 +1,415 @@ +/** +Basic foreground colors. + +[More colors here.](https://github.com/chalk/chalk/blob/master/readme.md#256-and-truecolor-color-support) +*/ +declare type ForegroundColor = + | 'black' + | 'red' + | 'green' + | 'yellow' + | 'blue' + | 'magenta' + | 'cyan' + | 'white' + | 'gray' + | 'grey' + | 'blackBright' + | 'redBright' + | 'greenBright' + | 'yellowBright' + | 'blueBright' + | 'magentaBright' + | 'cyanBright' + | 'whiteBright'; + +/** +Basic background colors. + +[More colors here.](https://github.com/chalk/chalk/blob/master/readme.md#256-and-truecolor-color-support) +*/ +declare type BackgroundColor = + | 'bgBlack' + | 'bgRed' + | 'bgGreen' + | 'bgYellow' + | 'bgBlue' + | 'bgMagenta' + | 'bgCyan' + | 'bgWhite' + | 'bgGray' + | 'bgGrey' + | 'bgBlackBright' + | 'bgRedBright' + | 'bgGreenBright' + | 'bgYellowBright' + | 'bgBlueBright' + | 'bgMagentaBright' + | 'bgCyanBright' + | 'bgWhiteBright'; + +/** +Basic colors. + +[More colors here.](https://github.com/chalk/chalk/blob/master/readme.md#256-and-truecolor-color-support) +*/ +declare type Color = ForegroundColor | BackgroundColor; + +declare type Modifiers = + | 'reset' + | 'bold' + | 'dim' + | 'italic' + | 'underline' + | 'inverse' + | 'hidden' + | 'strikethrough' + | 'visible'; + +declare namespace chalk { + /** + Levels: + - `0` - All colors disabled. + - `1` - Basic 16 colors support. + - `2` - ANSI 256 colors support. + - `3` - Truecolor 16 million colors support. + */ + type Level = 0 | 1 | 2 | 3; + + interface Options { + /** + Specify the color support for Chalk. + + By default, color support is automatically detected based on the environment. + + Levels: + - `0` - All colors disabled. + - `1` - Basic 16 colors support. + - `2` - ANSI 256 colors support. + - `3` - Truecolor 16 million colors support. + */ + level?: Level; + } + + /** + Return a new Chalk instance. + */ + type Instance = new (options?: Options) => Chalk; + + /** + Detect whether the terminal supports color. + */ + interface ColorSupport { + /** + The color level used by Chalk. + */ + level: Level; + + /** + Return whether Chalk supports basic 16 colors. + */ + hasBasic: boolean; + + /** + Return whether Chalk supports ANSI 256 colors. + */ + has256: boolean; + + /** + Return whether Chalk supports Truecolor 16 million colors. + */ + has16m: boolean; + } + + interface ChalkFunction { + /** + Use a template string. + + @remarks Template literals are unsupported for nested calls (see [issue #341](https://github.com/chalk/chalk/issues/341)) + + @example + ``` + import chalk = require('chalk'); + + log(chalk` + CPU: {red ${cpu.totalPercent}%} + RAM: {green ${ram.used / ram.total * 100}%} + DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%} + `); + ``` + + @example + ``` + import chalk = require('chalk'); + + log(chalk.red.bgBlack`2 + 3 = {bold ${2 + 3}}`) + ``` + */ + (text: TemplateStringsArray, ...placeholders: unknown[]): string; + + (...text: unknown[]): string; + } + + interface Chalk extends ChalkFunction { + /** + Return a new Chalk instance. + */ + Instance: Instance; + + /** + The color support for Chalk. + + By default, color support is automatically detected based on the environment. + + Levels: + - `0` - All colors disabled. + - `1` - Basic 16 colors support. + - `2` - ANSI 256 colors support. + - `3` - Truecolor 16 million colors support. + */ + level: Level; + + /** + Use HEX value to set text color. + + @param color - Hexadecimal value representing the desired color. + + @example + ``` + import chalk = require('chalk'); + + chalk.hex('#DEADED'); + ``` + */ + hex(color: string): Chalk; + + /** + Use keyword color value to set text color. + + @param color - Keyword value representing the desired color. + + @example + ``` + import chalk = require('chalk'); + + chalk.keyword('orange'); + ``` + */ + keyword(color: string): Chalk; + + /** + Use RGB values to set text color. + */ + rgb(red: number, green: number, blue: number): Chalk; + + /** + Use HSL values to set text color. + */ + hsl(hue: number, saturation: number, lightness: number): Chalk; + + /** + Use HSV values to set text color. + */ + hsv(hue: number, saturation: number, value: number): Chalk; + + /** + Use HWB values to set text color. + */ + hwb(hue: number, whiteness: number, blackness: number): Chalk; + + /** + Use a [Select/Set Graphic Rendition](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters) (SGR) [color code number](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit) to set text color. + + 30 <= code && code < 38 || 90 <= code && code < 98 + For example, 31 for red, 91 for redBright. + */ + ansi(code: number): Chalk; + + /** + Use a [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set text color. + */ + ansi256(index: number): Chalk; + + /** + Use HEX value to set background color. + + @param color - Hexadecimal value representing the desired color. + + @example + ``` + import chalk = require('chalk'); + + chalk.bgHex('#DEADED'); + ``` + */ + bgHex(color: string): Chalk; + + /** + Use keyword color value to set background color. + + @param color - Keyword value representing the desired color. + + @example + ``` + import chalk = require('chalk'); + + chalk.bgKeyword('orange'); + ``` + */ + bgKeyword(color: string): Chalk; + + /** + Use RGB values to set background color. + */ + bgRgb(red: number, green: number, blue: number): Chalk; + + /** + Use HSL values to set background color. + */ + bgHsl(hue: number, saturation: number, lightness: number): Chalk; + + /** + Use HSV values to set background color. + */ + bgHsv(hue: number, saturation: number, value: number): Chalk; + + /** + Use HWB values to set background color. + */ + bgHwb(hue: number, whiteness: number, blackness: number): Chalk; + + /** + Use a [Select/Set Graphic Rendition](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters) (SGR) [color code number](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit) to set background color. + + 30 <= code && code < 38 || 90 <= code && code < 98 + For example, 31 for red, 91 for redBright. + Use the foreground code, not the background code (for example, not 41, nor 101). + */ + bgAnsi(code: number): Chalk; + + /** + Use a [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set background color. + */ + bgAnsi256(index: number): Chalk; + + /** + Modifier: Resets the current color chain. + */ + readonly reset: Chalk; + + /** + Modifier: Make text bold. + */ + readonly bold: Chalk; + + /** + Modifier: Emitting only a small amount of light. + */ + readonly dim: Chalk; + + /** + Modifier: Make text italic. (Not widely supported) + */ + readonly italic: Chalk; + + /** + Modifier: Make text underline. (Not widely supported) + */ + readonly underline: Chalk; + + /** + Modifier: Inverse background and foreground colors. + */ + readonly inverse: Chalk; + + /** + Modifier: Prints the text, but makes it invisible. + */ + readonly hidden: Chalk; + + /** + Modifier: Puts a horizontal line through the center of the text. (Not widely supported) + */ + readonly strikethrough: Chalk; + + /** + Modifier: Prints the text only when Chalk has a color support level > 0. + Can be useful for things that are purely cosmetic. + */ + readonly visible: Chalk; + + readonly black: Chalk; + readonly red: Chalk; + readonly green: Chalk; + readonly yellow: Chalk; + readonly blue: Chalk; + readonly magenta: Chalk; + readonly cyan: Chalk; + readonly white: Chalk; + + /* + Alias for `blackBright`. + */ + readonly gray: Chalk; + + /* + Alias for `blackBright`. + */ + readonly grey: Chalk; + + readonly blackBright: Chalk; + readonly redBright: Chalk; + readonly greenBright: Chalk; + readonly yellowBright: Chalk; + readonly blueBright: Chalk; + readonly magentaBright: Chalk; + readonly cyanBright: Chalk; + readonly whiteBright: Chalk; + + readonly bgBlack: Chalk; + readonly bgRed: Chalk; + readonly bgGreen: Chalk; + readonly bgYellow: Chalk; + readonly bgBlue: Chalk; + readonly bgMagenta: Chalk; + readonly bgCyan: Chalk; + readonly bgWhite: Chalk; + + /* + Alias for `bgBlackBright`. + */ + readonly bgGray: Chalk; + + /* + Alias for `bgBlackBright`. + */ + readonly bgGrey: Chalk; + + readonly bgBlackBright: Chalk; + readonly bgRedBright: Chalk; + readonly bgGreenBright: Chalk; + readonly bgYellowBright: Chalk; + readonly bgBlueBright: Chalk; + readonly bgMagentaBright: Chalk; + readonly bgCyanBright: Chalk; + readonly bgWhiteBright: Chalk; + } +} + +/** +Main Chalk object that allows to chain styles together. +Call the last one as a method with a string argument. +Order doesn't matter, and later styles take precedent in case of a conflict. +This simply means that `chalk.red.yellow.green` is equivalent to `chalk.green`. +*/ +declare const chalk: chalk.Chalk & chalk.ChalkFunction & { + supportsColor: chalk.ColorSupport | false; + Level: chalk.Level; + Color: Color; + ForegroundColor: ForegroundColor; + BackgroundColor: BackgroundColor; + Modifiers: Modifiers; + stderr: chalk.Chalk & {supportsColor: chalk.ColorSupport | false}; +}; + +export = chalk; diff --git a/website/node_modules/chalk-template/node_modules/chalk/license b/website/node_modules/chalk-template/node_modules/chalk/license new file mode 100644 index 00000000..e7af2f77 --- /dev/null +++ b/website/node_modules/chalk-template/node_modules/chalk/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/chalk-template/node_modules/chalk/package.json b/website/node_modules/chalk-template/node_modules/chalk/package.json new file mode 100644 index 00000000..47c23f29 --- /dev/null +++ b/website/node_modules/chalk-template/node_modules/chalk/package.json @@ -0,0 +1,68 @@ +{ + "name": "chalk", + "version": "4.1.2", + "description": "Terminal string styling done right", + "license": "MIT", + "repository": "chalk/chalk", + "funding": "https://github.com/chalk/chalk?sponsor=1", + "main": "source", + "engines": { + "node": ">=10" + }, + "scripts": { + "test": "xo && nyc ava && tsd", + "bench": "matcha benchmark.js" + }, + "files": [ + "source", + "index.d.ts" + ], + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "str", + "ansi", + "style", + "styles", + "tty", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "devDependencies": { + "ava": "^2.4.0", + "coveralls": "^3.0.7", + "execa": "^4.0.0", + "import-fresh": "^3.1.0", + "matcha": "^0.7.0", + "nyc": "^15.0.0", + "resolve-from": "^5.0.0", + "tsd": "^0.7.4", + "xo": "^0.28.2" + }, + "xo": { + "rules": { + "unicorn/prefer-string-slice": "off", + "unicorn/prefer-includes": "off", + "@typescript-eslint/member-ordering": "off", + "no-redeclare": "off", + "unicorn/string-content": "off", + "unicorn/better-regex": "off" + } + } +} diff --git a/website/node_modules/chalk-template/node_modules/chalk/readme.md b/website/node_modules/chalk-template/node_modules/chalk/readme.md new file mode 100644 index 00000000..a055d21c --- /dev/null +++ b/website/node_modules/chalk-template/node_modules/chalk/readme.md @@ -0,0 +1,341 @@ +

+
+
+ Chalk +
+
+
+

+ +> Terminal string styling done right + +[![Build Status](https://travis-ci.org/chalk/chalk.svg?branch=master)](https://travis-ci.org/chalk/chalk) [![Coverage Status](https://coveralls.io/repos/github/chalk/chalk/badge.svg?branch=master)](https://coveralls.io/github/chalk/chalk?branch=master) [![npm dependents](https://badgen.net/npm/dependents/chalk)](https://www.npmjs.com/package/chalk?activeTab=dependents) [![Downloads](https://badgen.net/npm/dt/chalk)](https://www.npmjs.com/package/chalk) [![](https://img.shields.io/badge/unicorn-approved-ff69b4.svg)](https://www.youtube.com/watch?v=9auOCbH5Ns4) [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/xojs/xo) ![TypeScript-ready](https://img.shields.io/npm/types/chalk.svg) [![run on repl.it](https://repl.it/badge/github/chalk/chalk)](https://repl.it/github/chalk/chalk) + + + +
+ +--- + + + +--- + +
+ +## Highlights + +- Expressive API +- Highly performant +- Ability to nest styles +- [256/Truecolor color support](#256-and-truecolor-color-support) +- Auto-detects color support +- Doesn't extend `String.prototype` +- Clean and focused +- Actively maintained +- [Used by ~50,000 packages](https://www.npmjs.com/browse/depended/chalk) as of January 1, 2020 + +## Install + +```console +$ npm install chalk +``` + +## Usage + +```js +const chalk = require('chalk'); + +console.log(chalk.blue('Hello world!')); +``` + +Chalk comes with an easy to use composable API where you just chain and nest the styles you want. + +```js +const chalk = require('chalk'); +const log = console.log; + +// Combine styled and normal strings +log(chalk.blue('Hello') + ' World' + chalk.red('!')); + +// Compose multiple styles using the chainable API +log(chalk.blue.bgRed.bold('Hello world!')); + +// Pass in multiple arguments +log(chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz')); + +// Nest styles +log(chalk.red('Hello', chalk.underline.bgBlue('world') + '!')); + +// Nest styles of the same type even (color, underline, background) +log(chalk.green( + 'I am a green line ' + + chalk.blue.underline.bold('with a blue substring') + + ' that becomes green again!' +)); + +// ES2015 template literal +log(` +CPU: ${chalk.red('90%')} +RAM: ${chalk.green('40%')} +DISK: ${chalk.yellow('70%')} +`); + +// ES2015 tagged template literal +log(chalk` +CPU: {red ${cpu.totalPercent}%} +RAM: {green ${ram.used / ram.total * 100}%} +DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%} +`); + +// Use RGB colors in terminal emulators that support it. +log(chalk.keyword('orange')('Yay for orange colored text!')); +log(chalk.rgb(123, 45, 67).underline('Underlined reddish color')); +log(chalk.hex('#DEADED').bold('Bold gray!')); +``` + +Easily define your own themes: + +```js +const chalk = require('chalk'); + +const error = chalk.bold.red; +const warning = chalk.keyword('orange'); + +console.log(error('Error!')); +console.log(warning('Warning!')); +``` + +Take advantage of console.log [string substitution](https://nodejs.org/docs/latest/api/console.html#console_console_log_data_args): + +```js +const name = 'Sindre'; +console.log(chalk.green('Hello %s'), name); +//=> 'Hello Sindre' +``` + +## API + +### chalk.`

Index of  ';var arr1=it.paths;if(arr1){var value,index=-1,l1=arr1.length-1;while(index'+encodeHTML(value.name)+' ';} } out+='

';return out; +}var itself=directory, _encodeHTML=(function(doNotSkipEncoded) { + var encodeHTMLRules = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "/": "/" }, + matchHTML = doNotSkipEncoded ? /[&<>"'\/]/g : /&(?!#?\w+;)|<|>|"|'|\//g; + return function(code) { + return code ? code.toString().replace(matchHTML, function(m) {return encodeHTMLRules[m] || m;}) : ""; + }; + }());if(typeof module!=='undefined' && module.exports) module.exports=itself;else if(typeof define==='function')define(function(){return itself;});else {window.render=window.render||{};window.render['directory']=itself;}}()); \ No newline at end of file diff --git a/website/node_modules/serve-handler/src/error.js b/website/node_modules/serve-handler/src/error.js new file mode 100644 index 00000000..d8646bef --- /dev/null +++ b/website/node_modules/serve-handler/src/error.js @@ -0,0 +1,10 @@ +(function(){function error(it +) { +var out='
'+(it.statusCode)+'

'+(it.message)+'

';return out; +}var itself=error, _encodeHTML=(function(doNotSkipEncoded) { + var encodeHTMLRules = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "/": "/" }, + matchHTML = doNotSkipEncoded ? /[&<>"'\/]/g : /&(?!#?\w+;)|<|>|"|'|\//g; + return function(code) { + return code ? code.toString().replace(matchHTML, function(m) {return encodeHTMLRules[m] || m;}) : ""; + }; + }());if(typeof module!=='undefined' && module.exports) module.exports=itself;else if(typeof define==='function')define(function(){return itself;});else {window.render=window.render||{};window.render['error']=itself;}}()); \ No newline at end of file diff --git a/website/node_modules/serve-handler/src/glob-slash.js b/website/node_modules/serve-handler/src/glob-slash.js new file mode 100644 index 00000000..08187e48 --- /dev/null +++ b/website/node_modules/serve-handler/src/glob-slash.js @@ -0,0 +1,9 @@ +/* ! The MIT License (MIT) Copyright (c) 2014 Scott Corgan */ + +// This is adopted from https://github.com/scottcorgan/glob-slash/ + +const path = require('path'); +const normalize = value => path.posix.normalize(path.posix.join('/', value)); + +module.exports = value => (value.charAt(0) === '!' ? `!${normalize(value.substr(1))}` : normalize(value)); +module.exports.normalize = normalize; diff --git a/website/node_modules/serve-handler/src/index.js b/website/node_modules/serve-handler/src/index.js new file mode 100644 index 00000000..564f0123 --- /dev/null +++ b/website/node_modules/serve-handler/src/index.js @@ -0,0 +1,769 @@ +// Native +const {promisify} = require('util'); +const path = require('path'); +const {createHash} = require('crypto'); +const {realpath, lstat, createReadStream, readdir} = require('fs'); + +// Packages +const url = require('url'); +const slasher = require('./glob-slash'); +const minimatch = require('minimatch'); +const pathToRegExp = require('path-to-regexp'); +const mime = require('mime-types'); +const bytes = require('bytes'); +const contentDisposition = require('content-disposition'); +const isPathInside = require('path-is-inside'); +const parseRange = require('range-parser'); + +// Other +const directoryTemplate = require('./directory'); +const errorTemplate = require('./error'); + +const etags = new Map(); + +const calculateSha = (handlers, absolutePath) => + new Promise((resolve, reject) => { + const hash = createHash('sha1'); + hash.update(path.extname(absolutePath)); + hash.update('-'); + const rs = handlers.createReadStream(absolutePath); + rs.on('error', reject); + rs.on('data', buf => hash.update(buf)); + rs.on('end', () => { + const sha = hash.digest('hex'); + resolve(sha); + }); + }); + +const sourceMatches = (source, requestPath, allowSegments) => { + const keys = []; + const slashed = slasher(source); + const resolvedPath = path.posix.resolve(requestPath); + + let results = null; + + if (allowSegments) { + const normalized = slashed.replace('*', '(.*)'); + const expression = pathToRegExp(normalized, keys); + + results = expression.exec(resolvedPath); + + if (!results) { + // clear keys so that they are not used + // later with empty results. this may + // happen if minimatch returns true + keys.length = 0; + } + } + + if (results || minimatch(resolvedPath, slashed)) { + return { + keys, + results + }; + } + + return null; +}; + +const toTarget = (source, destination, previousPath) => { + const matches = sourceMatches(source, previousPath, true); + + if (!matches) { + return null; + } + + const {keys, results} = matches; + + const props = {}; + const {protocol} = url.parse(destination); + const normalizedDest = protocol ? destination : slasher(destination); + const toPath = pathToRegExp.compile(normalizedDest); + + for (let index = 0; index < keys.length; index++) { + const {name} = keys[index]; + props[name] = results[index + 1]; + } + + return toPath(props); +}; + +const applyRewrites = (requestPath, rewrites = [], repetitive) => { + // We need to copy the array, since we're going to modify it. + const rewritesCopy = rewrites.slice(); + + // If the method was called again, the path was already rewritten + // so we need to make sure to return it. + const fallback = repetitive ? requestPath : null; + + if (rewritesCopy.length === 0) { + return fallback; + } + + for (let index = 0; index < rewritesCopy.length; index++) { + const {source, destination} = rewrites[index]; + const target = toTarget(source, destination, requestPath); + + if (target) { + // Remove rules that were already applied + rewritesCopy.splice(index, 1); + + // Check if there are remaining ones to be applied + return applyRewrites(slasher(target), rewritesCopy, true); + } + } + + return fallback; +}; + +const ensureSlashStart = target => (target.startsWith('/') ? target : `/${target}`); + +const shouldRedirect = (decodedPath, {redirects = [], trailingSlash}, cleanUrl) => { + const slashing = typeof trailingSlash === 'boolean'; + const defaultType = 301; + const matchHTML = /(\.html|\/index)$/g; + + if (redirects.length === 0 && !slashing && !cleanUrl) { + return null; + } + + // By stripping the HTML parts from the decoded + // path *before* handling the trailing slash, we make + // sure that only *one* redirect occurs if both + // config options are used. + if (cleanUrl && matchHTML.test(decodedPath)) { + decodedPath = decodedPath.replace(matchHTML, ''); + if (decodedPath.indexOf('//') > -1) { + decodedPath = decodedPath.replace(/\/+/g, '/'); + } + return { + target: ensureSlashStart(decodedPath), + statusCode: defaultType + }; + } + + if (slashing) { + const {ext, name} = path.parse(decodedPath); + const isTrailed = decodedPath.endsWith('/'); + const isDotfile = name.startsWith('.'); + + let target = null; + + if (!trailingSlash && isTrailed) { + target = decodedPath.slice(0, -1); + } else if (trailingSlash && !isTrailed && !ext && !isDotfile) { + target = `${decodedPath}/`; + } + + if (decodedPath.indexOf('//') > -1) { + target = decodedPath.replace(/\/+/g, '/'); + } + + if (target) { + return { + target: ensureSlashStart(target), + statusCode: defaultType + }; + } + } + + // This is currently the fastest way to + // iterate over an array + for (let index = 0; index < redirects.length; index++) { + const {source, destination, type} = redirects[index]; + const target = toTarget(source, destination, decodedPath); + + if (target) { + return { + target, + statusCode: type || defaultType + }; + } + } + + return null; +}; + +const appendHeaders = (target, source) => { + for (let index = 0; index < source.length; index++) { + const {key, value} = source[index]; + target[key] = value; + } +}; + +const getHeaders = async (handlers, config, current, absolutePath, stats) => { + const {headers: customHeaders = [], etag = false} = config; + const related = {}; + const {base} = path.parse(absolutePath); + const relativePath = path.relative(current, absolutePath); + + if (customHeaders.length > 0) { + // By iterating over all headers and never stopping, developers + // can specify multiple header sources in the config that + // might match a single path. + for (let index = 0; index < customHeaders.length; index++) { + const {source, headers} = customHeaders[index]; + + if (sourceMatches(source, slasher(relativePath))) { + appendHeaders(related, headers); + } + } + } + + let defaultHeaders = {}; + + if (stats) { + defaultHeaders = { + 'Content-Length': stats.size, + // Default to "inline", which always tries to render in the browser, + // if that's not working, it will save the file. But to be clear: This + // only happens if it cannot find a appropiate value. + 'Content-Disposition': contentDisposition(base, { + type: 'inline' + }), + 'Accept-Ranges': 'bytes' + }; + + if (etag) { + let [mtime, sha] = etags.get(absolutePath) || []; + if (Number(mtime) !== Number(stats.mtime)) { + sha = await calculateSha(handlers, absolutePath); + etags.set(absolutePath, [stats.mtime, sha]); + } + defaultHeaders['ETag'] = `"${sha}"`; + } else { + defaultHeaders['Last-Modified'] = stats.mtime.toUTCString(); + } + + const contentType = mime.contentType(base); + + if (contentType) { + defaultHeaders['Content-Type'] = contentType; + } + } + + const headers = Object.assign(defaultHeaders, related); + + for (const key in headers) { + if (headers.hasOwnProperty(key) && headers[key] === null) { + delete headers[key]; + } + } + + return headers; +}; + +const applicable = (decodedPath, configEntry) => { + if (typeof configEntry === 'boolean') { + return configEntry; + } + + if (Array.isArray(configEntry)) { + for (let index = 0; index < configEntry.length; index++) { + const source = configEntry[index]; + + if (sourceMatches(source, decodedPath)) { + return true; + } + } + + return false; + } + + return true; +}; + +const getPossiblePaths = (relativePath, extension) => [ + path.join(relativePath, `index${extension}`), + relativePath.endsWith('/') ? relativePath.replace(/\/$/g, extension) : (relativePath + extension) +].filter(item => path.basename(item) !== extension); + +const findRelated = async (current, relativePath, rewrittenPath, originalStat) => { + const possible = rewrittenPath ? [rewrittenPath] : getPossiblePaths(relativePath, '.html'); + + let stats = null; + + for (let index = 0; index < possible.length; index++) { + const related = possible[index]; + const absolutePath = path.join(current, related); + + try { + stats = await originalStat(absolutePath); + } catch (err) { + if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { + throw err; + } + } + + if (stats) { + return { + stats, + absolutePath + }; + } + } + + return null; +}; + +const canBeListed = (excluded, file) => { + const slashed = slasher(file); + let whether = true; + + for (let mark = 0; mark < excluded.length; mark++) { + const source = excluded[mark]; + + if (sourceMatches(source, slashed)) { + whether = false; + break; + } + } + + return whether; +}; + +const renderDirectory = async (current, acceptsJSON, handlers, methods, config, paths) => { + const {directoryListing, trailingSlash, unlisted = [], renderSingle} = config; + const slashSuffix = typeof trailingSlash === 'boolean' ? (trailingSlash ? '/' : '') : '/'; + const {relativePath, absolutePath} = paths; + + const excluded = [ + '.DS_Store', + '.git', + ...unlisted + ]; + + if (!applicable(relativePath, directoryListing) && !renderSingle) { + return {}; + } + + let files = await handlers.readdir(absolutePath); + + const canRenderSingle = renderSingle && (files.length === 1); + + for (let index = 0; index < files.length; index++) { + const file = files[index]; + + const filePath = path.resolve(absolutePath, file); + const details = path.parse(filePath); + + // It's important to indicate that the `stat` call was + // spawned by the directory listing, as Now is + // simulating those calls and needs to special-case this. + let stats = null; + + if (methods.lstat) { + stats = await handlers.lstat(filePath, true); + } else { + stats = await handlers.lstat(filePath); + } + + details.relative = path.join(relativePath, details.base); + + if (stats.isDirectory()) { + details.base += slashSuffix; + details.relative += slashSuffix; + details.type = 'folder'; + } else { + if (canRenderSingle) { + return { + singleFile: true, + absolutePath: filePath, + stats + }; + } + + details.ext = details.ext.split('.')[1] || 'txt'; + details.type = 'file'; + + details.size = bytes(stats.size, { + unitSeparator: ' ', + decimalPlaces: 0 + }); + } + + details.title = details.base; + + if (canBeListed(excluded, file)) { + files[index] = details; + } else { + delete files[index]; + } + } + + const toRoot = path.relative(current, absolutePath); + const directory = path.join(path.basename(current), toRoot, slashSuffix); + const pathParts = directory.split(path.sep).filter(Boolean); + + // Sort to list directories first, then sort alphabetically + files = files.sort((a, b) => { + const aIsDir = a.type === 'directory'; + const bIsDir = b.type === 'directory'; + + /* istanbul ignore next */ + if (aIsDir && !bIsDir) { + return -1; + } + + if ((bIsDir && !aIsDir) || (a.base > b.base)) { + return 1; + } + + /* istanbul ignore next */ + if (a.base < b.base) { + return -1; + } + + /* istanbul ignore next */ + return 0; + }).filter(Boolean); + + // Add parent directory to the head of the sorted files array + if (toRoot.length > 0) { + const directoryPath = [...pathParts].slice(1); + const relative = path.join('/', ...directoryPath, '..', slashSuffix); + + files.unshift({ + type: 'directory', + base: '..', + relative, + title: relative, + ext: '' + }); + } + + const subPaths = []; + + for (let index = 0; index < pathParts.length; index++) { + const parents = []; + const isLast = index === (pathParts.length - 1); + + let before = 0; + + while (before <= index) { + parents.push(pathParts[before]); + before++; + } + + parents.shift(); + + subPaths.push({ + name: pathParts[index] + (isLast ? slashSuffix : '/'), + url: index === 0 ? '' : parents.join('/') + slashSuffix + }); + } + + const spec = { + files, + directory, + paths: subPaths + }; + + const output = acceptsJSON ? JSON.stringify(spec) : directoryTemplate(spec); + + return {directory: output}; +}; + +const sendError = async (absolutePath, response, acceptsJSON, current, handlers, config, spec) => { + const {err: original, message, code, statusCode} = spec; + + /* istanbul ignore next */ + if (original && process.env.NODE_ENV !== 'test') { + console.error(original); + } + + response.statusCode = statusCode; + + if (acceptsJSON) { + response.setHeader('Content-Type', 'application/json; charset=utf-8'); + + response.end(JSON.stringify({ + error: { + code, + message + } + })); + + return; + } + + let stats = null; + + const errorPage = path.join(current, `${statusCode}.html`); + + try { + stats = await handlers.lstat(errorPage); + } catch (err) { + if (err.code !== 'ENOENT') { + console.error(err); + } + } + + if (stats) { + let stream = null; + + try { + stream = await handlers.createReadStream(errorPage); + + const headers = await getHeaders(handlers, config, current, errorPage, stats); + + response.writeHead(statusCode, headers); + stream.pipe(response); + + return; + } catch (err) { + console.error(err); + } + } + + const headers = await getHeaders(handlers, config, current, absolutePath, null); + headers['Content-Type'] = 'text/html; charset=utf-8'; + + response.writeHead(statusCode, headers); + response.end(errorTemplate({statusCode, message})); +}; + +const internalError = async (...args) => { + const lastIndex = args.length - 1; + const err = args[lastIndex]; + + args[lastIndex] = { + statusCode: 500, + code: 'internal_server_error', + message: 'A server error has occurred', + err + }; + + return sendError(...args); +}; + +const getHandlers = methods => Object.assign({ + lstat: promisify(lstat), + realpath: promisify(realpath), + createReadStream, + readdir: promisify(readdir), + sendError +}, methods); + +module.exports = async (request, response, config = {}, methods = {}) => { + const cwd = process.cwd(); + const current = config.public ? path.resolve(cwd, config.public) : cwd; + const handlers = getHandlers(methods); + + let relativePath = null; + let acceptsJSON = null; + + if (request.headers.accept) { + acceptsJSON = request.headers.accept.includes('application/json'); + } + + try { + relativePath = decodeURIComponent(url.parse(request.url).pathname); + } catch (err) { + return sendError('/', response, acceptsJSON, current, handlers, config, { + statusCode: 400, + code: 'bad_request', + message: 'Bad Request' + }); + } + + let absolutePath = path.join(current, relativePath); + + // Prevent path traversal vulnerabilities. We could do this + // by ourselves, but using the package covers all the edge cases. + if (!isPathInside(absolutePath, current)) { + return sendError(absolutePath, response, acceptsJSON, current, handlers, config, { + statusCode: 400, + code: 'bad_request', + message: 'Bad Request' + }); + } + + const cleanUrl = applicable(relativePath, config.cleanUrls); + const redirect = shouldRedirect(relativePath, config, cleanUrl); + + if (redirect) { + response.writeHead(redirect.statusCode, { + Location: encodeURI(redirect.target) + }); + + response.end(); + return; + } + + let stats = null; + + // It's extremely important that we're doing multiple stat calls. This one + // right here could technically be removed, but then the program + // would be slower. Because for directories, we always want to see if a related file + // exists and then (after that), fetch the directory itself if no + // related file was found. However (for files, of which most have extensions), we should + // always stat right away. + // + // When simulating a file system without directory indexes, calculating whether a + // directory exists requires loading all the file paths and then checking if + // one of them includes the path of the directory. As that's a very + // performance-expensive thing to do, we need to ensure it's not happening if not really necessary. + + if (path.extname(relativePath) !== '') { + try { + stats = await handlers.lstat(absolutePath); + } catch (err) { + if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { + return internalError(absolutePath, response, acceptsJSON, current, handlers, config, err); + } + } + } + + const rewrittenPath = applyRewrites(relativePath, config.rewrites); + + if (!stats && (cleanUrl || rewrittenPath)) { + try { + const related = await findRelated(current, relativePath, rewrittenPath, handlers.lstat); + + if (related) { + ({stats, absolutePath} = related); + } + } catch (err) { + if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { + return internalError(absolutePath, response, acceptsJSON, current, handlers, config, err); + } + } + } + + if (!stats) { + try { + stats = await handlers.lstat(absolutePath); + } catch (err) { + if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { + return internalError(absolutePath, response, acceptsJSON, current, handlers, config, err); + } + } + } + + if (stats && stats.isDirectory()) { + let directory = null; + let singleFile = null; + + try { + const related = await renderDirectory(current, acceptsJSON, handlers, methods, config, { + relativePath, + absolutePath + }); + + if (related.singleFile) { + ({stats, absolutePath, singleFile} = related); + } else { + ({directory} = related); + } + } catch (err) { + if (err.code !== 'ENOENT') { + return internalError(absolutePath, response, acceptsJSON, current, handlers, config, err); + } + } + + if (directory) { + const contentType = acceptsJSON ? 'application/json; charset=utf-8' : 'text/html; charset=utf-8'; + + response.statusCode = 200; + response.setHeader('Content-Type', contentType); + response.end(directory); + + return; + } + + if (!singleFile) { + // The directory listing is disabled, so we want to + // render a 404 error. + stats = null; + } + } + + const isSymLink = stats && stats.isSymbolicLink(); + + // There are two scenarios in which we want to reply with + // a 404 error: Either the path does not exist, or it is a + // symlink while the `symlinks` option is disabled (which it is by default). + if (!stats || (!config.symlinks && isSymLink)) { + // allow for custom 404 handling + return handlers.sendError(absolutePath, response, acceptsJSON, current, handlers, config, { + statusCode: 404, + code: 'not_found', + message: 'The requested path could not be found' + }); + } + + // If we figured out that the target is a symlink, we need to + // resolve the symlink and run a new `stat` call just for the + // target of that symlink. + if (isSymLink) { + try { + absolutePath = await handlers.realpath(absolutePath); + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + + // The requested symlink is invalid + return handlers.sendError(absolutePath, response, acceptsJSON, current, handlers, config, { + statusCode: 404, + code: 'not_found', + message: 'The requested path could not be found' + }); + } + stats = await handlers.lstat(absolutePath); + } + + const streamOpts = {}; + + // TODO ? if-range + if (request.headers.range && stats.size) { + const range = parseRange(stats.size, request.headers.range); + + if (typeof range === 'object' && range.type === 'bytes') { + const {start, end} = range[0]; + + streamOpts.start = start; + streamOpts.end = end; + + response.statusCode = 206; + } else { + response.statusCode = 416; + response.setHeader('Content-Range', `bytes */${stats.size}`); + } + } + + // TODO ? multiple ranges + + let stream = null; + + try { + stream = await handlers.createReadStream(absolutePath, streamOpts); + } catch (err) { + return internalError(absolutePath, response, acceptsJSON, current, handlers, config, err); + } + + const headers = await getHeaders(handlers, config, current, absolutePath, stats); + + // eslint-disable-next-line no-undefined + if (streamOpts.start !== undefined && streamOpts.end !== undefined) { + headers['Content-Range'] = `bytes ${streamOpts.start}-${streamOpts.end}/${stats.size}`; + headers['Content-Length'] = streamOpts.end - streamOpts.start + 1; + } + + // We need to check for `headers.ETag` being truthy first, otherwise it will + // match `undefined` being equal to `undefined`, which is true. + // + // Checking for `undefined` and `null` is also important, because `Range` can be `0`. + // + // eslint-disable-next-line no-eq-null + if (request.headers.range == null && headers.ETag && headers.ETag === request.headers['if-none-match']) { + response.statusCode = 304; + response.end(); + + return; + } + + response.writeHead(response.statusCode || 200, headers); + stream.pipe(response); +}; diff --git a/website/node_modules/serve/build/main.js b/website/node_modules/serve/build/main.js new file mode 100755 index 00000000..b53e9779 --- /dev/null +++ b/website/node_modules/serve/build/main.js @@ -0,0 +1,606 @@ +#!/usr/bin/env node + +// source/main.ts +import { cwd as getPwd, exit, env as env2, stdout } from "node:process"; +import path from "node:path"; +import chalk4 from "chalk"; +import boxen from "boxen"; +import clipboard from "clipboardy"; + +// package.json +var package_default = { + name: "serve", + version: "14.2.5", + description: "Static file serving and directory listing", + keywords: [ + "vercel", + "serve", + "micro", + "http-server" + ], + repository: "vercel/serve", + license: "MIT", + type: "module", + bin: { + serve: "./build/main.js" + }, + files: [ + "build/" + ], + engines: { + node: ">= 14" + }, + scripts: { + develop: "tsx watch ./source/main.ts", + start: "node ./build/main.js", + compile: "tsup ./source/main.ts", + "test:tsc": "tsc --project tsconfig.json", + "test:unit": "vitest run --config config/vitest.ts", + "test:watch": "vitest watch --config config/vitest.ts", + test: "pnpm test:tsc && pnpm test:unit", + "lint:code": "eslint --max-warnings 0 source/**/*.ts", + "lint:style": "prettier --check --ignore-path .gitignore .", + lint: "pnpm lint:code && pnpm lint:style", + format: "prettier --write --ignore-path .gitignore .", + prepare: "husky install config/husky && pnpm compile", + changeset: "changeset", + "version-packages": "changeset version", + release: "pnpm compile && changeset publish" + }, + dependencies: { + "@zeit/schemas": "2.36.0", + ajv: "8.12.0", + arg: "5.0.2", + boxen: "7.0.0", + chalk: "5.0.1", + "chalk-template": "0.4.0", + clipboardy: "3.0.0", + compression: "1.8.1", + "is-port-reachable": "4.0.0", + "serve-handler": "6.1.6", + "update-check": "1.5.4" + }, + devDependencies: { + "@changesets/cli": "2.29.6", + "@types/compression": "1.7.2", + "@types/serve-handler": "6.1.1", + "@vercel/style-guide": "3.0.0", + "@vitest/coverage-v8": "2.1.3", + eslint: "8.19.0", + got: "12.1.0", + husky: "8.0.1", + "lint-staged": "13.0.3", + prettier: "2.7.1", + tsup: "8.3.0", + tsx: "4.19.1", + typescript: "5.6.3", + vitest: "2.1.3" + }, + tsup: { + target: "esnext", + format: [ + "esm" + ], + outDir: "./build/" + }, + prettier: "@vercel/style-guide/prettier", + eslintConfig: { + extends: [ + "./node_modules/@vercel/style-guide/eslint/node.js", + "./node_modules/@vercel/style-guide/eslint/typescript.js" + ], + parserOptions: { + project: "tsconfig.json" + } + }, + "lint-staged": { + "*": [ + "prettier --ignore-unknown --write" + ], + "source/**/*.ts": [ + "eslint --max-warnings 0 --fix", + "vitest related --run" + ], + tests: [ + "vitest --run" + ] + }, + packageManager: "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4" +}; + +// source/utilities/promise.ts +import { promisify } from "node:util"; +var resolve = async (promiseLike) => { + try { + const data = await promiseLike; + return [void 0, data]; + } catch (error2) { + return [error2, void 0]; + } +}; + +// source/utilities/server.ts +import http2 from "node:http"; +import https from "node:https"; +import { readFile } from "node:fs/promises"; +import handler from "serve-handler"; +import compression from "compression"; +import isPortReachable from "is-port-reachable"; +import chalk2 from "chalk"; + +// source/utilities/http.ts +import { networkInterfaces as getNetworkInterfaces } from "node:os"; +var networkInterfaces = getNetworkInterfaces(); +var registerCloseListener = (fn) => { + let run = false; + const wrapper = () => { + if (!run) { + run = true; + fn(); + } + }; + process.on("SIGINT", wrapper); + process.on("SIGTERM", wrapper); + process.on("exit", wrapper); +}; +var getNetworkAddress = () => { + for (const interfaceDetails of Object.values(networkInterfaces)) { + if (!interfaceDetails) continue; + for (const details of interfaceDetails) { + const { address, family, internal } = details; + if (family === "IPv4" && !internal) return address; + } + } +}; + +// source/utilities/logger.ts +import chalk from "chalk"; +var http = (...message) => console.info(chalk.bgBlue.bold(" HTTP "), ...message); +var info = (...message) => console.info(chalk.bgMagenta.bold(" INFO "), ...message); +var warn = (...message) => console.error(chalk.bgYellow.bold(" WARN "), ...message); +var error = (...message) => console.error(chalk.bgRed.bold(" ERROR "), ...message); +var log = console.log; +var logger = { http, info, warn, error, log }; + +// source/utilities/server.ts +var compress = promisify(compression()); +var startServer = async (endpoint, config2, args2, previous) => { + const serverHandler = (request, response) => { + const run = async () => { + const requestTime = /* @__PURE__ */ new Date(); + const formattedTime = `${requestTime.toLocaleDateString()} ${requestTime.toLocaleTimeString()}`; + const ipAddress = request.socket.remoteAddress?.replace("::ffff:", "") ?? "unknown"; + const requestUrl = `${request.method ?? "GET"} ${request.url ?? "/"}`; + if (!args2["--no-request-logging"]) + logger.http( + chalk2.dim(formattedTime), + chalk2.yellow(ipAddress), + chalk2.cyan(requestUrl) + ); + if (args2["--cors"]) { + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Headers", "*"); + response.setHeader("Access-Control-Allow-Credentials", "true"); + response.setHeader("Access-Control-Allow-Private-Network", "true"); + } + if (!args2["--no-compression"]) + await compress(request, response); + await handler(request, response, config2); + const responseTime = Date.now() - requestTime.getTime(); + if (!args2["--no-request-logging"]) + logger.http( + chalk2.dim(formattedTime), + chalk2.yellow(ipAddress), + chalk2[response.statusCode < 400 ? "green" : "red"]( + `Returned ${response.statusCode} in ${responseTime} ms` + ) + ); + }; + run().catch((error2) => { + throw error2; + }); + }; + const sslCert = args2["--ssl-cert"]; + const sslKey = args2["--ssl-key"]; + const sslPass = args2["--ssl-pass"]; + const isPFXFormat = sslCert && /[.](?pfx|p12)$/.exec(sslCert) !== null; + const useSsl = sslCert && (sslKey || sslPass || isPFXFormat); + let serverConfig = {}; + if (useSsl && sslCert && sslKey) { + serverConfig = { + key: await readFile(sslKey), + cert: await readFile(sslCert), + passphrase: sslPass ? await readFile(sslPass, "utf8") : "" + }; + } else if (useSsl && sslCert && isPFXFormat) { + serverConfig = { + pfx: await readFile(sslCert), + passphrase: sslPass ? await readFile(sslPass, "utf8") : "" + }; + } + const server = useSsl ? https.createServer(serverConfig, serverHandler) : http2.createServer(serverHandler); + const getServerDetails = () => { + registerCloseListener(() => server.close()); + const details = server.address(); + let local; + let network; + if (typeof details === "string") { + local = details; + } else if (typeof details === "object" && details.port) { + let address; + if (details.address === "::") address = "localhost"; + else if (details.family === "IPv6") address = `[${details.address}]`; + else address = details.address; + const ip = getNetworkAddress(); + const protocol = useSsl ? "https" : "http"; + local = `${protocol}://${address}:${details.port}`; + network = ip ? `${protocol}://${ip}:${details.port}` : void 0; + } + return { + local, + network, + previous + }; + }; + server.on("error", (error2) => { + throw new Error( + `Failed to serve: ${error2.stack?.toString() ?? error2.message}` + ); + }); + if (typeof endpoint.port === "number" && !isNaN(endpoint.port) && endpoint.port !== 0) { + const port = endpoint.port; + const isClosed = await isPortReachable(port, { + host: endpoint.host ?? "localhost" + }); + if (isClosed) return startServer({ port: 0 }, config2, args2, port); + } + return new Promise((resolve2, _reject) => { + if (typeof endpoint.port !== "undefined" && typeof endpoint.host === "undefined") + server.listen(endpoint.port, () => resolve2(getServerDetails())); + else if (typeof endpoint.port === "undefined" && typeof endpoint.host !== "undefined") + server.listen(endpoint.host, () => resolve2(getServerDetails())); + else if (typeof endpoint.port !== "undefined" && typeof endpoint.host !== "undefined") + server.listen( + endpoint.port, + endpoint.host, + () => resolve2(getServerDetails()) + ); + }); +}; + +// source/utilities/cli.ts +import { parse as parseUrl } from "node:url"; +import { env } from "node:process"; +import chalk3 from "chalk"; +import chalkTemplate from "chalk-template"; +import parseArgv from "arg"; +import checkForUpdate from "update-check"; +var helpText = chalkTemplate` + {bold.cyan serve} - Static file serving and directory listing + + {bold USAGE} + + {bold $} {cyan serve} --help + {bold $} {cyan serve} --version + {bold $} {cyan serve} folder_name + {bold $} {cyan serve} [-l {underline listen_uri} [-l ...]] [{underline directory}] + + By default, {cyan serve} will listen on {bold 0.0.0.0:3000} and serve the + current working directory on that address. + + Specifying a single {bold --listen} argument will overwrite the default, not supplement it. + + {bold OPTIONS} + + --help Shows this help message + + -v, --version Displays the current version of serve + + -l, --listen {underline listen_uri} Specify a URI endpoint on which to listen (see below) - + more than one may be specified to listen in multiple places + + -p Specify custom port + + -s, --single Rewrite all not-found requests to \`index.html\` + + -d, --debug Show debugging information + + -c, --config Specify custom path to \`serve.json\` + + -L, --no-request-logging Do not log any request information to the console. + + -C, --cors Enable CORS, sets \`Access-Control-Allow-Origin\` to \`*\` + + -n, --no-clipboard Do not copy the local address to the clipboard + + -u, --no-compression Do not compress files + + --no-etag Send \`Last-Modified\` header instead of \`ETag\` + + -S, --symlinks Resolve symlinks instead of showing 404 errors + + --ssl-cert Optional path to an SSL/TLS certificate to serve with HTTPS + {grey Supported formats: PEM (default) and PKCS12 (PFX)} + + --ssl-key Optional path to the SSL/TLS certificate\'s private key + {grey Applicable only for PEM certificates} + + --ssl-pass Optional path to the SSL/TLS certificate\'s passphrase + + --no-port-switching Do not open a port other than the one specified when it\'s taken. + + {bold ENDPOINTS} + + Listen endpoints (specified by the {bold --listen} or {bold -l} options above) instruct {cyan serve} + to listen on one or more interfaces/ports, UNIX domain sockets, or Windows named pipes. + + For TCP ports on hostname "localhost": + + {bold $} {cyan serve} -l {underline 1234} + + For TCP (traditional host/port) endpoints: + + {bold $} {cyan serve} -l tcp://{underline hostname}:{underline 1234} + + For UNIX domain socket endpoints: + + {bold $} {cyan serve} -l unix:{underline /path/to/socket.sock} + + For Windows named pipe endpoints: + + {bold $} {cyan serve} -l pipe:\\\\.\\pipe\\{underline PipeName} +`; +var getHelpText = () => helpText; +var parseEndpoint = (uriOrPort) => { + if (!isNaN(Number(uriOrPort))) return { port: Number(uriOrPort) }; + const endpoint = uriOrPort; + const url = parseUrl(endpoint); + switch (url.protocol) { + case "pipe:": { + const pipe = endpoint.replace(/^pipe:/, ""); + if (!pipe.startsWith("\\\\.\\")) + throw new Error(`Invalid Windows named pipe endpoint: ${endpoint}`); + return { host: pipe }; + } + case "unix:": + if (!url.pathname) + throw new Error(`Invalid UNIX domain socket endpoint: ${endpoint}`); + return { host: url.pathname }; + case "tcp:": + url.port = url.port ?? "3000"; + url.hostname = url.hostname ?? "localhost"; + return { + port: Number(url.port), + host: url.hostname + }; + default: + throw new Error( + `Unknown --listen endpoint scheme (protocol): ${url.protocol ?? "undefined"}` + ); + } +}; +var options = { + "--help": Boolean, + "--version": Boolean, + "--listen": [parseEndpoint], + "--single": Boolean, + "--debug": Boolean, + "--config": String, + "--no-clipboard": Boolean, + "--no-compression": Boolean, + "--no-etag": Boolean, + "--symlinks": Boolean, + "--cors": Boolean, + "--no-port-switching": Boolean, + "--ssl-cert": String, + "--ssl-key": String, + "--ssl-pass": String, + "--no-request-logging": Boolean, + // A list of aliases for the above options. + "-h": "--help", + "-v": "--version", + "-l": "--listen", + "-s": "--single", + "-d": "--debug", + "-c": "--config", + "-n": "--no-clipboard", + "-u": "--no-compression", + "-S": "--symlinks", + "-C": "--cors", + "-L": "--no-request-logging", + // The `-p` option is deprecated and is kept only for backwards-compatibility. + "-p": "--listen" +}; +var parseArguments = () => parseArgv(options); +var checkForUpdates = async (manifest) => { + if (env.NO_UPDATE_CHECK) return; + const [error2, update] = await resolve(checkForUpdate(manifest)); + if (error2) throw error2; + if (!update) return; + logger.log( + chalk3.bgRed.white(" UPDATE "), + `The latest version of \`serve\` is ${update.latest}` + ); +}; + +// source/utilities/config.ts +import { + resolve as resolvePath, + relative as resolveRelativePath +} from "node:path"; +import { readFile as readFile2 } from "node:fs/promises"; +import Ajv from "ajv"; +import schema from "@zeit/schemas/deployment/config-static.js"; +var loadConfiguration = async (presentDirectory2, directoryToServe2, args2) => { + const files = ["serve.json", "now.json", "package.json"]; + if (args2["--config"]) files.unshift(args2["--config"]); + const config2 = {}; + for (const file of files) { + const location = resolvePath(directoryToServe2, file); + const [error2, rawContents] = await resolve( + readFile2(location, "utf8") + ); + if (error2) { + if (error2.code === "ENOENT" && file !== args2["--config"]) continue; + else + throw new Error( + `Could not read configuration from file ${location}: ${error2.message}` + ); + } + let parsedJson; + try { + parsedJson = JSON.parse(rawContents); + if (typeof parsedJson !== "object") + throw new Error("configuration is not an object"); + } catch (parserError) { + throw new Error( + `Could not parse ${location} as JSON: ${parserError.message}` + ); + } + if (file === "now.json") { + parsedJson = parsedJson; + parsedJson = parsedJson.now.static; + } else if (file === "package.json") { + parsedJson = parsedJson; + parsedJson = parsedJson.static; + } + if (!parsedJson) continue; + Object.assign(config2, parsedJson); + if (file === "now.json" || file === "package.json") + logger.warn( + "The config files `now.json` and `package.json` are deprecated. Please use `serve.json`." + ); + break; + } + if (directoryToServe2) { + const staticDirectory = config2.public; + config2.public = resolveRelativePath( + presentDirectory2, + staticDirectory ? resolvePath(directoryToServe2, staticDirectory) : directoryToServe2 + ); + } + if (Object.keys(config2).length !== 0) { + const ajv = new Ajv({ allowUnionTypes: true }); + const validate = ajv.compile(schema); + if (!validate(config2) && validate.errors) { + const defaultMessage = "The configuration you provided is invalid:"; + const error2 = validate.errors[0]; + throw new Error( + `${defaultMessage} +${error2.message ?? ""} +${JSON.stringify( + error2.params + )}` + ); + } + } + config2.etag = !args2["--no-etag"]; + config2.symlinks = args2["--symlinks"] || config2.symlinks; + return config2; +}; + +// source/main.ts +var [parseError, args] = await resolve(parseArguments()); +if (parseError || !args) { + logger.error(parseError.message); + exit(1); +} +var [updateError] = await resolve(checkForUpdates(package_default)); +if (updateError) { + const suffix = args["--debug"] ? ":" : " (use `--debug` to see full error)"; + logger.warn(`Checking for updates failed${suffix}`); + if (args["--debug"]) logger.error(updateError.message); +} +if (args["--version"]) { + logger.log(package_default.version); + exit(0); +} +if (args["--help"]) { + logger.log(getHelpText()); + exit(0); +} +if (!args["--listen"]) + args["--listen"] = [{ port: parseInt(env2.PORT ?? "3000", 10) }]; +if (args._.length > 1) { + logger.error("Please provide one path argument at maximum"); + exit(1); +} +var presentDirectory = getPwd(); +var directoryToServe = args._[0] ? path.resolve(args._[0]) : presentDirectory; +var [configError, config] = await resolve( + loadConfiguration(presentDirectory, directoryToServe, args) +); +if (configError || !config) { + logger.error(configError.message); + exit(1); +} +if (args["--single"]) { + const { rewrites } = config; + const existingRewrites = Array.isArray(rewrites) ? rewrites : []; + config.rewrites = [ + { + source: "**", + destination: "/index.html" + }, + ...existingRewrites + ]; +} +for (const endpoint of args["--listen"]) { + const { local, network, previous } = await startServer( + endpoint, + config, + args + ); + const copyAddress = !args["--no-clipboard"]; + if (!stdout.isTTY || env2.NODE_ENV === "production") { + const suffix = local ? ` at ${local}` : ""; + logger.info(`Accepting connections${suffix}`); + continue; + } + let message = chalk4.green("Serving!"); + if (local) { + const prefix = network ? "- " : ""; + const space = network ? " " : " "; + message += ` + +${chalk4.bold(`${prefix}Local:`)}${space}${local}`; + } + if (network) message += ` +${chalk4.bold("- Network:")} ${network}`; + if (previous) + message += chalk4.red( + ` + +This port was picked because ${chalk4.underline( + previous.toString() + )} is in use.` + ); + if (copyAddress && local) { + try { + await clipboard.write(local); + message += ` + +${chalk4.grey("Copied local address to clipboard!")}`; + } catch (error2) { + logger.error( + `Cannot copy server address to clipboard: ${error2.message}.` + ); + } + } + logger.log( + boxen(message, { + padding: 1, + borderColor: "green", + margin: 1 + }) + ); +} +registerCloseListener(() => { + logger.log(); + logger.info("Gracefully shutting down. Please wait..."); + process.on("SIGINT", () => { + logger.log(); + logger.warn("Force-closing all open sockets..."); + exit(0); + }); +}); diff --git a/website/node_modules/serve/license.md b/website/node_modules/serve/license.md new file mode 100644 index 00000000..1d8a93f3 --- /dev/null +++ b/website/node_modules/serve/license.md @@ -0,0 +1,20 @@ +# The MIT License (MIT) + +Copyright (c) 2023 Vercel, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/serve/package.json b/website/node_modules/serve/package.json new file mode 100644 index 00000000..d5196c19 --- /dev/null +++ b/website/node_modules/serve/package.json @@ -0,0 +1,97 @@ +{ + "name": "serve", + "version": "14.2.5", + "description": "Static file serving and directory listing", + "keywords": [ + "vercel", + "serve", + "micro", + "http-server" + ], + "repository": "vercel/serve", + "license": "MIT", + "type": "module", + "bin": { + "serve": "./build/main.js" + }, + "files": [ + "build/" + ], + "engines": { + "node": ">= 14" + }, + "dependencies": { + "@zeit/schemas": "2.36.0", + "ajv": "8.12.0", + "arg": "5.0.2", + "boxen": "7.0.0", + "chalk": "5.0.1", + "chalk-template": "0.4.0", + "clipboardy": "3.0.0", + "compression": "1.8.1", + "is-port-reachable": "4.0.0", + "serve-handler": "6.1.6", + "update-check": "1.5.4" + }, + "devDependencies": { + "@changesets/cli": "2.29.6", + "@types/compression": "1.7.2", + "@types/serve-handler": "6.1.1", + "@vercel/style-guide": "3.0.0", + "@vitest/coverage-v8": "2.1.3", + "eslint": "8.19.0", + "got": "12.1.0", + "husky": "8.0.1", + "lint-staged": "13.0.3", + "prettier": "2.7.1", + "tsup": "8.3.0", + "tsx": "4.19.1", + "typescript": "5.6.3", + "vitest": "2.1.3" + }, + "tsup": { + "target": "esnext", + "format": [ + "esm" + ], + "outDir": "./build/" + }, + "prettier": "@vercel/style-guide/prettier", + "eslintConfig": { + "extends": [ + "./node_modules/@vercel/style-guide/eslint/node.js", + "./node_modules/@vercel/style-guide/eslint/typescript.js" + ], + "parserOptions": { + "project": "tsconfig.json" + } + }, + "lint-staged": { + "*": [ + "prettier --ignore-unknown --write" + ], + "source/**/*.ts": [ + "eslint --max-warnings 0 --fix", + "vitest related --run" + ], + "tests": [ + "vitest --run" + ] + }, + "scripts": { + "develop": "tsx watch ./source/main.ts", + "start": "node ./build/main.js", + "compile": "tsup ./source/main.ts", + "test:tsc": "tsc --project tsconfig.json", + "test:unit": "vitest run --config config/vitest.ts", + "test:watch": "vitest watch --config config/vitest.ts", + "test": "pnpm test:tsc && pnpm test:unit", + "lint:code": "eslint --max-warnings 0 source/**/*.ts", + "lint:style": "prettier --check --ignore-path .gitignore .", + "lint": "pnpm lint:code && pnpm lint:style", + "format": "prettier --write --ignore-path .gitignore .", + "changeset": "changeset", + "version-packages": "changeset version", + "release": "pnpm compile && changeset publish" + } +} \ No newline at end of file diff --git a/website/node_modules/serve/readme.md b/website/node_modules/serve/readme.md new file mode 100644 index 00000000..241a3fbe --- /dev/null +++ b/website/node_modules/serve/readme.md @@ -0,0 +1,96 @@ +![Serve Logo](https://raw.githubusercontent.com/vercel/serve/main/media/banner.png) + + + +--- + +`serve` helps you serve a static site, single page application or just a static file (no matter if on your device or on the local network). It also provides a neat interface for listing the directory's contents: + +![Listing UI](https://raw.githubusercontent.com/vercel/serve/main/media/listing-ui.png) + +> Once it's time to push your site to production, we recommend using [Vercel](https://vercel.com). + +## Usage + +> `serve` v14 onwards requires Node v14 to run. Please use `serve` v13 if you cannot upgrade to Node v14. + +The quickest way to get started is to just run `npx serve` in your project's directory. + +If you prefer, you can also install the package globally (you'll need at least [Node LTS](https://github.com/nodejs/Release#release-schedule)): + +```bash +> npm install --global serve +``` + +Once that's done, you can run this command inside your project's directory... + +```bash +> serve +``` + +...or specify which folder you want to serve: + +```bash +> serve folder-name/ +``` + +Finally, run this command to see a list of all available options: + +```bash +> serve --help +``` + +Now you understand how the package works! :tada: + +## Configuration + +To customize `serve`'s behavior, create a `serve.json` file in the public folder and insert any of [these properties](https://github.com/vercel/serve-handler#options). + +## API + +The core of `serve` is [`serve-handler`](https://github.com/vercel/serve-handler), which can be used as middleware in existing HTTP servers: + +```js +const handler = require('serve-handler'); +const http = require('http'); + +const server = http.createServer((request, response) => { + // You pass two more arguments for config and middleware + // More details here: https://github.com/vercel/serve-handler#options + return handler(request, response); +}); + +server.listen(3000, () => { + console.log('Running at http://localhost:3000'); +}); +``` + +> You can also replace `http.createServer` with [`micro`](https://github.com/vercel/micro). + +## Issues and Contributing + +If you want a feature to be added, or wish to report a bug, please open an issue [here](https://github.com/vercel/serve/issues/new). + +If you wish to contribute to the project, please read the [contributing guide](contributing.md) first. + +## Credits + +This project used to be called `list` and `micro-list`. But thanks to [TJ Holowaychuk](https://github.com/tj) handing us the new name, it's now called `serve` (which is much more definite). + +## Author + +Leo Lamprecht ([@leo](https://x.com/leo)) diff --git a/website/node_modules/shebang-command/index.js b/website/node_modules/shebang-command/index.js new file mode 100644 index 00000000..f35db308 --- /dev/null +++ b/website/node_modules/shebang-command/index.js @@ -0,0 +1,19 @@ +'use strict'; +const shebangRegex = require('shebang-regex'); + +module.exports = (string = '') => { + const match = string.match(shebangRegex); + + if (!match) { + return null; + } + + const [path, argument] = match[0].replace(/#! ?/, '').split(' '); + const binary = path.split('/').pop(); + + if (binary === 'env') { + return argument; + } + + return argument ? `${binary} ${argument}` : binary; +}; diff --git a/website/node_modules/shebang-command/license b/website/node_modules/shebang-command/license new file mode 100644 index 00000000..db6bc32c --- /dev/null +++ b/website/node_modules/shebang-command/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Kevin Mårtensson (github.com/kevva) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/shebang-command/package.json b/website/node_modules/shebang-command/package.json new file mode 100644 index 00000000..18e3c046 --- /dev/null +++ b/website/node_modules/shebang-command/package.json @@ -0,0 +1,34 @@ +{ + "name": "shebang-command", + "version": "2.0.0", + "description": "Get the command from a shebang", + "license": "MIT", + "repository": "kevva/shebang-command", + "author": { + "name": "Kevin Mårtensson", + "email": "kevinmartensson@gmail.com", + "url": "github.com/kevva" + }, + "engines": { + "node": ">=8" + }, + "scripts": { + "test": "xo && ava" + }, + "files": [ + "index.js" + ], + "keywords": [ + "cmd", + "command", + "parse", + "shebang" + ], + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "devDependencies": { + "ava": "^2.3.0", + "xo": "^0.24.0" + } +} diff --git a/website/node_modules/shebang-command/readme.md b/website/node_modules/shebang-command/readme.md new file mode 100644 index 00000000..84feb442 --- /dev/null +++ b/website/node_modules/shebang-command/readme.md @@ -0,0 +1,34 @@ +# shebang-command [![Build Status](https://travis-ci.org/kevva/shebang-command.svg?branch=master)](https://travis-ci.org/kevva/shebang-command) + +> Get the command from a shebang + + +## Install + +``` +$ npm install shebang-command +``` + + +## Usage + +```js +const shebangCommand = require('shebang-command'); + +shebangCommand('#!/usr/bin/env node'); +//=> 'node' + +shebangCommand('#!/bin/bash'); +//=> 'bash' +``` + + +## API + +### shebangCommand(string) + +#### string + +Type: `string` + +String containing a shebang. diff --git a/website/node_modules/shebang-regex/index.d.ts b/website/node_modules/shebang-regex/index.d.ts new file mode 100644 index 00000000..61d034b3 --- /dev/null +++ b/website/node_modules/shebang-regex/index.d.ts @@ -0,0 +1,22 @@ +/** +Regular expression for matching a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) line. + +@example +``` +import shebangRegex = require('shebang-regex'); + +const string = '#!/usr/bin/env node\nconsole.log("unicorns");'; + +shebangRegex.test(string); +//=> true + +shebangRegex.exec(string)[0]; +//=> '#!/usr/bin/env node' + +shebangRegex.exec(string)[1]; +//=> '/usr/bin/env node' +``` +*/ +declare const shebangRegex: RegExp; + +export = shebangRegex; diff --git a/website/node_modules/shebang-regex/index.js b/website/node_modules/shebang-regex/index.js new file mode 100644 index 00000000..63fc4a0b --- /dev/null +++ b/website/node_modules/shebang-regex/index.js @@ -0,0 +1,2 @@ +'use strict'; +module.exports = /^#!(.*)/; diff --git a/website/node_modules/shebang-regex/license b/website/node_modules/shebang-regex/license new file mode 100644 index 00000000..e7af2f77 --- /dev/null +++ b/website/node_modules/shebang-regex/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/shebang-regex/package.json b/website/node_modules/shebang-regex/package.json new file mode 100644 index 00000000..00ab30fe --- /dev/null +++ b/website/node_modules/shebang-regex/package.json @@ -0,0 +1,35 @@ +{ + "name": "shebang-regex", + "version": "3.0.0", + "description": "Regular expression for matching a shebang line", + "license": "MIT", + "repository": "sindresorhus/shebang-regex", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=8" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "regex", + "regexp", + "shebang", + "match", + "test", + "line" + ], + "devDependencies": { + "ava": "^1.4.1", + "tsd": "^0.7.2", + "xo": "^0.24.0" + } +} diff --git a/website/node_modules/shebang-regex/readme.md b/website/node_modules/shebang-regex/readme.md new file mode 100644 index 00000000..5ecf863a --- /dev/null +++ b/website/node_modules/shebang-regex/readme.md @@ -0,0 +1,33 @@ +# shebang-regex [![Build Status](https://travis-ci.org/sindresorhus/shebang-regex.svg?branch=master)](https://travis-ci.org/sindresorhus/shebang-regex) + +> Regular expression for matching a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) line + + +## Install + +``` +$ npm install shebang-regex +``` + + +## Usage + +```js +const shebangRegex = require('shebang-regex'); + +const string = '#!/usr/bin/env node\nconsole.log("unicorns");'; + +shebangRegex.test(string); +//=> true + +shebangRegex.exec(string)[0]; +//=> '#!/usr/bin/env node' + +shebangRegex.exec(string)[1]; +//=> '/usr/bin/env node' +``` + + +## License + +MIT © [Sindre Sorhus](https://sindresorhus.com) diff --git a/website/node_modules/signal-exit/LICENSE.txt b/website/node_modules/signal-exit/LICENSE.txt new file mode 100644 index 00000000..eead04a1 --- /dev/null +++ b/website/node_modules/signal-exit/LICENSE.txt @@ -0,0 +1,16 @@ +The ISC License + +Copyright (c) 2015, Contributors + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/website/node_modules/signal-exit/README.md b/website/node_modules/signal-exit/README.md new file mode 100644 index 00000000..f9c7c007 --- /dev/null +++ b/website/node_modules/signal-exit/README.md @@ -0,0 +1,39 @@ +# signal-exit + +[![Build Status](https://travis-ci.org/tapjs/signal-exit.png)](https://travis-ci.org/tapjs/signal-exit) +[![Coverage](https://coveralls.io/repos/tapjs/signal-exit/badge.svg?branch=master)](https://coveralls.io/r/tapjs/signal-exit?branch=master) +[![NPM version](https://img.shields.io/npm/v/signal-exit.svg)](https://www.npmjs.com/package/signal-exit) +[![Standard Version](https://img.shields.io/badge/release-standard%20version-brightgreen.svg)](https://github.com/conventional-changelog/standard-version) + +When you want to fire an event no matter how a process exits: + +* reaching the end of execution. +* explicitly having `process.exit(code)` called. +* having `process.kill(pid, sig)` called. +* receiving a fatal signal from outside the process + +Use `signal-exit`. + +```js +var onExit = require('signal-exit') + +onExit(function (code, signal) { + console.log('process exited!') +}) +``` + +## API + +`var remove = onExit(function (code, signal) {}, options)` + +The return value of the function is a function that will remove the +handler. + +Note that the function *only* fires for signals if the signal would +cause the process to exit. That is, there are no other listeners, and +it is a fatal signal. + +## Options + +* `alwaysLast`: Run this handler after any other signal or exit + handlers. This causes `process.emit` to be monkeypatched. diff --git a/website/node_modules/signal-exit/index.js b/website/node_modules/signal-exit/index.js new file mode 100644 index 00000000..93703f36 --- /dev/null +++ b/website/node_modules/signal-exit/index.js @@ -0,0 +1,202 @@ +// Note: since nyc uses this module to output coverage, any lines +// that are in the direct sync flow of nyc's outputCoverage are +// ignored, since we can never get coverage for them. +// grab a reference to node's real process object right away +var process = global.process + +const processOk = function (process) { + return process && + typeof process === 'object' && + typeof process.removeListener === 'function' && + typeof process.emit === 'function' && + typeof process.reallyExit === 'function' && + typeof process.listeners === 'function' && + typeof process.kill === 'function' && + typeof process.pid === 'number' && + typeof process.on === 'function' +} + +// some kind of non-node environment, just no-op +/* istanbul ignore if */ +if (!processOk(process)) { + module.exports = function () { + return function () {} + } +} else { + var assert = require('assert') + var signals = require('./signals.js') + var isWin = /^win/i.test(process.platform) + + var EE = require('events') + /* istanbul ignore if */ + if (typeof EE !== 'function') { + EE = EE.EventEmitter + } + + var emitter + if (process.__signal_exit_emitter__) { + emitter = process.__signal_exit_emitter__ + } else { + emitter = process.__signal_exit_emitter__ = new EE() + emitter.count = 0 + emitter.emitted = {} + } + + // Because this emitter is a global, we have to check to see if a + // previous version of this library failed to enable infinite listeners. + // I know what you're about to say. But literally everything about + // signal-exit is a compromise with evil. Get used to it. + if (!emitter.infinite) { + emitter.setMaxListeners(Infinity) + emitter.infinite = true + } + + module.exports = function (cb, opts) { + /* istanbul ignore if */ + if (!processOk(global.process)) { + return function () {} + } + assert.equal(typeof cb, 'function', 'a callback must be provided for exit handler') + + if (loaded === false) { + load() + } + + var ev = 'exit' + if (opts && opts.alwaysLast) { + ev = 'afterexit' + } + + var remove = function () { + emitter.removeListener(ev, cb) + if (emitter.listeners('exit').length === 0 && + emitter.listeners('afterexit').length === 0) { + unload() + } + } + emitter.on(ev, cb) + + return remove + } + + var unload = function unload () { + if (!loaded || !processOk(global.process)) { + return + } + loaded = false + + signals.forEach(function (sig) { + try { + process.removeListener(sig, sigListeners[sig]) + } catch (er) {} + }) + process.emit = originalProcessEmit + process.reallyExit = originalProcessReallyExit + emitter.count -= 1 + } + module.exports.unload = unload + + var emit = function emit (event, code, signal) { + /* istanbul ignore if */ + if (emitter.emitted[event]) { + return + } + emitter.emitted[event] = true + emitter.emit(event, code, signal) + } + + // { : , ... } + var sigListeners = {} + signals.forEach(function (sig) { + sigListeners[sig] = function listener () { + /* istanbul ignore if */ + if (!processOk(global.process)) { + return + } + // If there are no other listeners, an exit is coming! + // Simplest way: remove us and then re-send the signal. + // We know that this will kill the process, so we can + // safely emit now. + var listeners = process.listeners(sig) + if (listeners.length === emitter.count) { + unload() + emit('exit', null, sig) + /* istanbul ignore next */ + emit('afterexit', null, sig) + /* istanbul ignore next */ + if (isWin && sig === 'SIGHUP') { + // "SIGHUP" throws an `ENOSYS` error on Windows, + // so use a supported signal instead + sig = 'SIGINT' + } + /* istanbul ignore next */ + process.kill(process.pid, sig) + } + } + }) + + module.exports.signals = function () { + return signals + } + + var loaded = false + + var load = function load () { + if (loaded || !processOk(global.process)) { + return + } + loaded = true + + // This is the number of onSignalExit's that are in play. + // It's important so that we can count the correct number of + // listeners on signals, and don't wait for the other one to + // handle it instead of us. + emitter.count += 1 + + signals = signals.filter(function (sig) { + try { + process.on(sig, sigListeners[sig]) + return true + } catch (er) { + return false + } + }) + + process.emit = processEmit + process.reallyExit = processReallyExit + } + module.exports.load = load + + var originalProcessReallyExit = process.reallyExit + var processReallyExit = function processReallyExit (code) { + /* istanbul ignore if */ + if (!processOk(global.process)) { + return + } + process.exitCode = code || /* istanbul ignore next */ 0 + emit('exit', process.exitCode, null) + /* istanbul ignore next */ + emit('afterexit', process.exitCode, null) + /* istanbul ignore next */ + originalProcessReallyExit.call(process, process.exitCode) + } + + var originalProcessEmit = process.emit + var processEmit = function processEmit (ev, arg) { + if (ev === 'exit' && processOk(global.process)) { + /* istanbul ignore else */ + if (arg !== undefined) { + process.exitCode = arg + } + var ret = originalProcessEmit.apply(this, arguments) + /* istanbul ignore next */ + emit('exit', process.exitCode, null) + /* istanbul ignore next */ + emit('afterexit', process.exitCode, null) + /* istanbul ignore next */ + return ret + } else { + return originalProcessEmit.apply(this, arguments) + } + } +} diff --git a/website/node_modules/signal-exit/package.json b/website/node_modules/signal-exit/package.json new file mode 100644 index 00000000..e1a00311 --- /dev/null +++ b/website/node_modules/signal-exit/package.json @@ -0,0 +1,38 @@ +{ + "name": "signal-exit", + "version": "3.0.7", + "description": "when you want to fire an event no matter how a process exits.", + "main": "index.js", + "scripts": { + "test": "tap", + "snap": "tap", + "preversion": "npm test", + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags" + }, + "files": [ + "index.js", + "signals.js" + ], + "repository": { + "type": "git", + "url": "https://github.com/tapjs/signal-exit.git" + }, + "keywords": [ + "signal", + "exit" + ], + "author": "Ben Coe ", + "license": "ISC", + "bugs": { + "url": "https://github.com/tapjs/signal-exit/issues" + }, + "homepage": "https://github.com/tapjs/signal-exit", + "devDependencies": { + "chai": "^3.5.0", + "coveralls": "^3.1.1", + "nyc": "^15.1.0", + "standard-version": "^9.3.1", + "tap": "^15.1.1" + } +} diff --git a/website/node_modules/signal-exit/signals.js b/website/node_modules/signal-exit/signals.js new file mode 100644 index 00000000..3bd67a8a --- /dev/null +++ b/website/node_modules/signal-exit/signals.js @@ -0,0 +1,53 @@ +// This is not the set of all possible signals. +// +// It IS, however, the set of all signals that trigger +// an exit on either Linux or BSD systems. Linux is a +// superset of the signal names supported on BSD, and +// the unknown signals just fail to register, so we can +// catch that easily enough. +// +// Don't bother with SIGKILL. It's uncatchable, which +// means that we can't fire any callbacks anyway. +// +// If a user does happen to register a handler on a non- +// fatal signal like SIGWINCH or something, and then +// exit, it'll end up firing `process.emit('exit')`, so +// the handler will be fired anyway. +// +// SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised +// artificially, inherently leave the process in a +// state from which it is not safe to try and enter JS +// listeners. +module.exports = [ + 'SIGABRT', + 'SIGALRM', + 'SIGHUP', + 'SIGINT', + 'SIGTERM' +] + +if (process.platform !== 'win32') { + module.exports.push( + 'SIGVTALRM', + 'SIGXCPU', + 'SIGXFSZ', + 'SIGUSR2', + 'SIGTRAP', + 'SIGSYS', + 'SIGQUIT', + 'SIGIOT' + // should detect profiler and enable/disable accordingly. + // see #21 + // 'SIGPROF' + ) +} + +if (process.platform === 'linux') { + module.exports.push( + 'SIGIO', + 'SIGPOLL', + 'SIGPWR', + 'SIGSTKFLT', + 'SIGUNUSED' + ) +} diff --git a/website/node_modules/string-width/index.d.ts b/website/node_modules/string-width/index.d.ts new file mode 100644 index 00000000..aed9fdff --- /dev/null +++ b/website/node_modules/string-width/index.d.ts @@ -0,0 +1,29 @@ +export interface Options { + /** + Count [ambiguous width characters](https://www.unicode.org/reports/tr11/#Ambiguous) as having narrow width (count of 1) instead of wide width (count of 2). + + @default true + */ + readonly ambiguousIsNarrow: boolean; +} + +/** +Get the visual width of a string - the number of columns required to display it. + +Some Unicode characters are [fullwidth](https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms) and use double the normal width. [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) are stripped and doesn't affect the width. + +@example +``` +import stringWidth from 'string-width'; + +stringWidth('a'); +//=> 1 + +stringWidth('古'); +//=> 2 + +stringWidth('\u001B[1m古\u001B[22m'); +//=> 2 +``` +*/ +export default function stringWidth(string: string, options?: Options): number; diff --git a/website/node_modules/string-width/index.js b/website/node_modules/string-width/index.js new file mode 100644 index 00000000..9294488f --- /dev/null +++ b/website/node_modules/string-width/index.js @@ -0,0 +1,54 @@ +import stripAnsi from 'strip-ansi'; +import eastAsianWidth from 'eastasianwidth'; +import emojiRegex from 'emoji-regex'; + +export default function stringWidth(string, options = {}) { + if (typeof string !== 'string' || string.length === 0) { + return 0; + } + + options = { + ambiguousIsNarrow: true, + ...options + }; + + string = stripAnsi(string); + + if (string.length === 0) { + return 0; + } + + string = string.replace(emojiRegex(), ' '); + + const ambiguousCharacterWidth = options.ambiguousIsNarrow ? 1 : 2; + let width = 0; + + for (const character of string) { + const codePoint = character.codePointAt(0); + + // Ignore control characters + if (codePoint <= 0x1F || (codePoint >= 0x7F && codePoint <= 0x9F)) { + continue; + } + + // Ignore combining characters + if (codePoint >= 0x300 && codePoint <= 0x36F) { + continue; + } + + const code = eastAsianWidth.eastAsianWidth(character); + switch (code) { + case 'F': + case 'W': + width += 2; + break; + case 'A': + width += ambiguousCharacterWidth; + break; + default: + width += 1; + } + } + + return width; +} diff --git a/website/node_modules/string-width/license b/website/node_modules/string-width/license new file mode 100644 index 00000000..fa7ceba3 --- /dev/null +++ b/website/node_modules/string-width/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/string-width/package.json b/website/node_modules/string-width/package.json new file mode 100644 index 00000000..f46d6770 --- /dev/null +++ b/website/node_modules/string-width/package.json @@ -0,0 +1,59 @@ +{ + "name": "string-width", + "version": "5.1.2", + "description": "Get the visual width of a string - the number of columns required to display it", + "license": "MIT", + "repository": "sindresorhus/string-width", + "funding": "https://github.com/sponsors/sindresorhus", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "type": "module", + "exports": "./index.js", + "engines": { + "node": ">=12" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "string", + "character", + "unicode", + "width", + "visual", + "column", + "columns", + "fullwidth", + "full-width", + "full", + "ansi", + "escape", + "codes", + "cli", + "command-line", + "terminal", + "console", + "cjk", + "chinese", + "japanese", + "korean", + "fixed-width" + ], + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "devDependencies": { + "ava": "^3.15.0", + "tsd": "^0.14.0", + "xo": "^0.38.2" + } +} diff --git a/website/node_modules/string-width/readme.md b/website/node_modules/string-width/readme.md new file mode 100644 index 00000000..52910df1 --- /dev/null +++ b/website/node_modules/string-width/readme.md @@ -0,0 +1,67 @@ +# string-width + +> Get the visual width of a string - the number of columns required to display it + +Some Unicode characters are [fullwidth](https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms) and use double the normal width. [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) are stripped and doesn't affect the width. + +Useful to be able to measure the actual width of command-line output. + +## Install + +``` +$ npm install string-width +``` + +## Usage + +```js +import stringWidth from 'string-width'; + +stringWidth('a'); +//=> 1 + +stringWidth('古'); +//=> 2 + +stringWidth('\u001B[1m古\u001B[22m'); +//=> 2 +``` + +## API + +### stringWidth(string, options?) + +#### string + +Type: `string` + +The string to be counted. + +#### options + +Type: `object` + +##### ambiguousIsNarrow + +Type: `boolean`\ +Default: `false` + +Count [ambiguous width characters](https://www.unicode.org/reports/tr11/#Ambiguous) as having narrow width (count of 1) instead of wide width (count of 2). + +## Related + +- [string-width-cli](https://github.com/sindresorhus/string-width-cli) - CLI for this module +- [string-length](https://github.com/sindresorhus/string-length) - Get the real length of a string +- [widest-line](https://github.com/sindresorhus/widest-line) - Get the visual width of the widest line in a string + +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/website/node_modules/strip-ansi/index.d.ts b/website/node_modules/strip-ansi/index.d.ts new file mode 100644 index 00000000..44e954d0 --- /dev/null +++ b/website/node_modules/strip-ansi/index.d.ts @@ -0,0 +1,15 @@ +/** +Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string. + +@example +``` +import stripAnsi from 'strip-ansi'; + +stripAnsi('\u001B[4mUnicorn\u001B[0m'); +//=> 'Unicorn' + +stripAnsi('\u001B]8;;https://github.com\u0007Click\u001B]8;;\u0007'); +//=> 'Click' +``` +*/ +export default function stripAnsi(string: string): string; diff --git a/website/node_modules/strip-ansi/index.js b/website/node_modules/strip-ansi/index.js new file mode 100644 index 00000000..ba19750e --- /dev/null +++ b/website/node_modules/strip-ansi/index.js @@ -0,0 +1,14 @@ +import ansiRegex from 'ansi-regex'; + +const regex = ansiRegex(); + +export default function stripAnsi(string) { + if (typeof string !== 'string') { + throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``); + } + + // Even though the regex is global, we don't need to reset the `.lastIndex` + // because unlike `.exec()` and `.test()`, `.replace()` does it automatically + // and doing it manually has a performance penalty. + return string.replace(regex, ''); +} diff --git a/website/node_modules/strip-ansi/license b/website/node_modules/strip-ansi/license new file mode 100644 index 00000000..fa7ceba3 --- /dev/null +++ b/website/node_modules/strip-ansi/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/strip-ansi/package.json b/website/node_modules/strip-ansi/package.json new file mode 100644 index 00000000..2a59216e --- /dev/null +++ b/website/node_modules/strip-ansi/package.json @@ -0,0 +1,59 @@ +{ + "name": "strip-ansi", + "version": "7.1.2", + "description": "Strip ANSI escape codes from a string", + "license": "MIT", + "repository": "chalk/strip-ansi", + "funding": "https://github.com/chalk/strip-ansi?sponsor=1", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "type": "module", + "exports": "./index.js", + "types": "./index.d.ts", + "sideEffects": false, + "engines": { + "node": ">=12" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "keywords": [ + "strip", + "trim", + "remove", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "devDependencies": { + "ava": "^3.15.0", + "tsd": "^0.17.0", + "xo": "^0.44.0" + } +} diff --git a/website/node_modules/strip-ansi/readme.md b/website/node_modules/strip-ansi/readme.md new file mode 100644 index 00000000..109b692b --- /dev/null +++ b/website/node_modules/strip-ansi/readme.md @@ -0,0 +1,37 @@ +# strip-ansi + +> Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string + +> [!NOTE] +> Node.js has this built-in now with [`stripVTControlCharacters`](https://nodejs.org/api/util.html#utilstripvtcontrolcharactersstr). The benefit of this package is consistent behavior across Node.js versions and faster improvements. The Node.js version is actually based on this package. + +## Install + +```sh +npm install strip-ansi +``` + +## Usage + +```js +import stripAnsi from 'strip-ansi'; + +stripAnsi('\u001B[4mUnicorn\u001B[0m'); +//=> 'Unicorn' + +stripAnsi('\u001B]8;;https://github.com\u0007Click\u001B]8;;\u0007'); +//=> 'Click' +``` + +## Related + +- [strip-ansi-cli](https://github.com/chalk/strip-ansi-cli) - CLI for this module +- [strip-ansi-stream](https://github.com/chalk/strip-ansi-stream) - Streaming version of this module +- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes +- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes +- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) diff --git a/website/node_modules/strip-final-newline/index.js b/website/node_modules/strip-final-newline/index.js new file mode 100644 index 00000000..78fc0c59 --- /dev/null +++ b/website/node_modules/strip-final-newline/index.js @@ -0,0 +1,16 @@ +'use strict'; + +module.exports = input => { + const LF = typeof input === 'string' ? '\n' : '\n'.charCodeAt(); + const CR = typeof input === 'string' ? '\r' : '\r'.charCodeAt(); + + if (input[input.length - 1] === LF) { + input = input.slice(0, input.length - 1); + } + + if (input[input.length - 1] === CR) { + input = input.slice(0, input.length - 1); + } + + return input; +}; diff --git a/website/node_modules/strip-final-newline/license b/website/node_modules/strip-final-newline/license new file mode 100644 index 00000000..e7af2f77 --- /dev/null +++ b/website/node_modules/strip-final-newline/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/strip-final-newline/package.json b/website/node_modules/strip-final-newline/package.json new file mode 100644 index 00000000..d9f2a6c5 --- /dev/null +++ b/website/node_modules/strip-final-newline/package.json @@ -0,0 +1,40 @@ +{ + "name": "strip-final-newline", + "version": "2.0.0", + "description": "Strip the final newline character from a string/buffer", + "license": "MIT", + "repository": "sindresorhus/strip-final-newline", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=6" + }, + "scripts": { + "test": "xo && ava" + }, + "files": [ + "index.js" + ], + "keywords": [ + "strip", + "trim", + "remove", + "delete", + "final", + "last", + "end", + "file", + "newline", + "linebreak", + "character", + "string", + "buffer" + ], + "devDependencies": { + "ava": "^0.25.0", + "xo": "^0.23.0" + } +} diff --git a/website/node_modules/strip-final-newline/readme.md b/website/node_modules/strip-final-newline/readme.md new file mode 100644 index 00000000..32dfd509 --- /dev/null +++ b/website/node_modules/strip-final-newline/readme.md @@ -0,0 +1,30 @@ +# strip-final-newline [![Build Status](https://travis-ci.com/sindresorhus/strip-final-newline.svg?branch=master)](https://travis-ci.com/sindresorhus/strip-final-newline) + +> Strip the final [newline character](https://en.wikipedia.org/wiki/Newline) from a string/buffer + +Can be useful when parsing the output of, for example, `ChildProcess#execFile`, as [binaries usually output a newline at the end](https://stackoverflow.com/questions/729692/why-should-text-files-end-with-a-newline). Normally, you would use `stdout.trim()`, but that would also remove newlines at the start and whitespace. + + +## Install + +``` +$ npm install strip-final-newline +``` + + +## Usage + +```js +const stripFinalNewline = require('strip-final-newline'); + +stripFinalNewline('foo\nbar\n\n'); +//=> 'foo\nbar\n' + +stripFinalNewline(Buffer.from('foo\nbar\n\n')).toString(); +//=> 'foo\nbar\n' +``` + + +## License + +MIT © [Sindre Sorhus](https://sindresorhus.com) diff --git a/website/node_modules/strip-json-comments/index.js b/website/node_modules/strip-json-comments/index.js new file mode 100644 index 00000000..4e6576e6 --- /dev/null +++ b/website/node_modules/strip-json-comments/index.js @@ -0,0 +1,70 @@ +'use strict'; +var singleComment = 1; +var multiComment = 2; + +function stripWithoutWhitespace() { + return ''; +} + +function stripWithWhitespace(str, start, end) { + return str.slice(start, end).replace(/\S/g, ' '); +} + +module.exports = function (str, opts) { + opts = opts || {}; + + var currentChar; + var nextChar; + var insideString = false; + var insideComment = false; + var offset = 0; + var ret = ''; + var strip = opts.whitespace === false ? stripWithoutWhitespace : stripWithWhitespace; + + for (var i = 0; i < str.length; i++) { + currentChar = str[i]; + nextChar = str[i + 1]; + + if (!insideComment && currentChar === '"') { + var escaped = str[i - 1] === '\\' && str[i - 2] !== '\\'; + if (!escaped) { + insideString = !insideString; + } + } + + if (insideString) { + continue; + } + + if (!insideComment && currentChar + nextChar === '//') { + ret += str.slice(offset, i); + offset = i; + insideComment = singleComment; + i++; + } else if (insideComment === singleComment && currentChar + nextChar === '\r\n') { + i++; + insideComment = false; + ret += strip(str, offset, i); + offset = i; + continue; + } else if (insideComment === singleComment && currentChar === '\n') { + insideComment = false; + ret += strip(str, offset, i); + offset = i; + } else if (!insideComment && currentChar + nextChar === '/*') { + ret += str.slice(offset, i); + offset = i; + insideComment = multiComment; + i++; + continue; + } else if (insideComment === multiComment && currentChar + nextChar === '*/') { + i++; + insideComment = false; + ret += strip(str, offset, i + 1); + offset = i + 1; + continue; + } + } + + return ret + (insideComment ? strip(str.substr(offset)) : str.substr(offset)); +}; diff --git a/website/node_modules/strip-json-comments/license b/website/node_modules/strip-json-comments/license new file mode 100644 index 00000000..654d0bfe --- /dev/null +++ b/website/node_modules/strip-json-comments/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/website/node_modules/strip-json-comments/package.json b/website/node_modules/strip-json-comments/package.json new file mode 100644 index 00000000..288ecc77 --- /dev/null +++ b/website/node_modules/strip-json-comments/package.json @@ -0,0 +1,42 @@ +{ + "name": "strip-json-comments", + "version": "2.0.1", + "description": "Strip comments from JSON. Lets you use comments in your JSON files!", + "license": "MIT", + "repository": "sindresorhus/strip-json-comments", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "xo && ava" + }, + "files": [ + "index.js" + ], + "keywords": [ + "json", + "strip", + "remove", + "delete", + "trim", + "comments", + "multiline", + "parse", + "config", + "configuration", + "conf", + "settings", + "util", + "env", + "environment" + ], + "devDependencies": { + "ava": "*", + "xo": "*" + } +} diff --git a/website/node_modules/strip-json-comments/readme.md b/website/node_modules/strip-json-comments/readme.md new file mode 100644 index 00000000..0ee58dfe --- /dev/null +++ b/website/node_modules/strip-json-comments/readme.md @@ -0,0 +1,64 @@ +# strip-json-comments [![Build Status](https://travis-ci.org/sindresorhus/strip-json-comments.svg?branch=master)](https://travis-ci.org/sindresorhus/strip-json-comments) + +> Strip comments from JSON. Lets you use comments in your JSON files! + +This is now possible: + +```js +{ + // rainbows + "unicorn": /* ❤ */ "cake" +} +``` + +It will replace single-line comments `//` and multi-line comments `/**/` with whitespace. This allows JSON error positions to remain as close as possible to the original source. + +Also available as a [gulp](https://github.com/sindresorhus/gulp-strip-json-comments)/[grunt](https://github.com/sindresorhus/grunt-strip-json-comments)/[broccoli](https://github.com/sindresorhus/broccoli-strip-json-comments) plugin. + + +## Install + +``` +$ npm install --save strip-json-comments +``` + + +## Usage + +```js +const json = '{/*rainbows*/"unicorn":"cake"}'; + +JSON.parse(stripJsonComments(json)); +//=> {unicorn: 'cake'} +``` + + +## API + +### stripJsonComments(input, [options]) + +#### input + +Type: `string` + +Accepts a string with JSON and returns a string without comments. + +#### options + +##### whitespace + +Type: `boolean` +Default: `true` + +Replace comments with whitespace instead of stripping them entirely. + + +## Related + +- [strip-json-comments-cli](https://github.com/sindresorhus/strip-json-comments-cli) - CLI for this module +- [strip-css-comments](https://github.com/sindresorhus/strip-css-comments) - Strip comments from CSS + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/website/node_modules/supports-color/browser.js b/website/node_modules/supports-color/browser.js new file mode 100644 index 00000000..62afa3a7 --- /dev/null +++ b/website/node_modules/supports-color/browser.js @@ -0,0 +1,5 @@ +'use strict'; +module.exports = { + stdout: false, + stderr: false +}; diff --git a/website/node_modules/supports-color/index.js b/website/node_modules/supports-color/index.js new file mode 100644 index 00000000..6fada390 --- /dev/null +++ b/website/node_modules/supports-color/index.js @@ -0,0 +1,135 @@ +'use strict'; +const os = require('os'); +const tty = require('tty'); +const hasFlag = require('has-flag'); + +const {env} = process; + +let forceColor; +if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false') || + hasFlag('color=never')) { + forceColor = 0; +} else if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + forceColor = 1; +} + +if ('FORCE_COLOR' in env) { + if (env.FORCE_COLOR === 'true') { + forceColor = 1; + } else if (env.FORCE_COLOR === 'false') { + forceColor = 0; + } else { + forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); + } +} + +function translateLevel(level) { + if (level === 0) { + return false; + } + + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +} + +function supportsColor(haveStream, streamIsTTY) { + if (forceColor === 0) { + return 0; + } + + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; + } + + if (hasFlag('color=256')) { + return 2; + } + + if (haveStream && !streamIsTTY && forceColor === undefined) { + return 0; + } + + const min = forceColor || 0; + + if (env.TERM === 'dumb') { + return min; + } + + if (process.platform === 'win32') { + // Windows 10 build 10586 is the first Windows release that supports 256 colors. + // Windows 10 build 14931 is the first release that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(osRelease[0]) >= 10 && + Number(osRelease[2]) >= 10586 + ) { + return Number(osRelease[2]) >= 14931 ? 3 : 2; + } + + return 1; + } + + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'GITHUB_ACTIONS', 'BUILDKITE'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } + + return min; + } + + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } + + if (env.COLORTERM === 'truecolor') { + return 3; + } + + if ('TERM_PROGRAM' in env) { + const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version >= 3 ? 3 : 2; + case 'Apple_Terminal': + return 2; + // No default + } + } + + if (/-256(color)?$/i.test(env.TERM)) { + return 2; + } + + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } + + if ('COLORTERM' in env) { + return 1; + } + + return min; +} + +function getSupportLevel(stream) { + const level = supportsColor(stream, stream && stream.isTTY); + return translateLevel(level); +} + +module.exports = { + supportsColor: getSupportLevel, + stdout: translateLevel(supportsColor(true, tty.isatty(1))), + stderr: translateLevel(supportsColor(true, tty.isatty(2))) +}; diff --git a/website/node_modules/supports-color/license b/website/node_modules/supports-color/license new file mode 100644 index 00000000..e7af2f77 --- /dev/null +++ b/website/node_modules/supports-color/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/website/node_modules/supports-color/package.json b/website/node_modules/supports-color/package.json new file mode 100644 index 00000000..f7182edc --- /dev/null +++ b/website/node_modules/supports-color/package.json @@ -0,0 +1,53 @@ +{ + "name": "supports-color", + "version": "7.2.0", + "description": "Detect whether a terminal supports color", + "license": "MIT", + "repository": "chalk/supports-color", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=8" + }, + "scripts": { + "test": "xo && ava" + }, + "files": [ + "index.js", + "browser.js" + ], + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "ansi", + "styles", + "tty", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "support", + "supports", + "capability", + "detect", + "truecolor", + "16m" + ], + "dependencies": { + "has-flag": "^4.0.0" + }, + "devDependencies": { + "ava": "^1.4.1", + "import-fresh": "^3.0.0", + "xo": "^0.24.0" + }, + "browser": "browser.js" +} diff --git a/website/node_modules/supports-color/readme.md b/website/node_modules/supports-color/readme.md new file mode 100644 index 00000000..36542285 --- /dev/null +++ b/website/node_modules/supports-color/readme.md @@ -0,0 +1,76 @@ +# supports-color [![Build Status](https://travis-ci.org/chalk/supports-color.svg?branch=master)](https://travis-ci.org/chalk/supports-color) + +> Detect whether a terminal supports color + + +## Install + +``` +$ npm install supports-color +``` + + +## Usage + +```js +const supportsColor = require('supports-color'); + +if (supportsColor.stdout) { + console.log('Terminal stdout supports color'); +} + +if (supportsColor.stdout.has256) { + console.log('Terminal stdout supports 256 colors'); +} + +if (supportsColor.stderr.has16m) { + console.log('Terminal stderr supports 16 million colors (truecolor)'); +} +``` + + +## API + +Returns an `Object` with a `stdout` and `stderr` property for testing either streams. Each property is an `Object`, or `false` if color is not supported. + +The `stdout`/`stderr` objects specifies a level of support for color through a `.level` property and a corresponding flag: + +- `.level = 1` and `.hasBasic = true`: Basic color support (16 colors) +- `.level = 2` and `.has256 = true`: 256 color support +- `.level = 3` and `.has16m = true`: Truecolor support (16 million colors) + + +## Info + +It obeys the `--color` and `--no-color` CLI flags. + +For situations where using `--color` is not possible, use the environment variable `FORCE_COLOR=1` (level 1), `FORCE_COLOR=2` (level 2), or `FORCE_COLOR=3` (level 3) to forcefully enable color, or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks. + +Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=16m` flags, respectively. + + +## Related + +- [supports-color-cli](https://github.com/chalk/supports-color-cli) - CLI for this module +- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right + + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + + +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
+ +--- diff --git a/website/node_modules/type-fest/index.d.ts b/website/node_modules/type-fest/index.d.ts new file mode 100644 index 00000000..ee20662e --- /dev/null +++ b/website/node_modules/type-fest/index.d.ts @@ -0,0 +1,95 @@ +// Basic +export * from './source/primitive'; +export * from './source/typed-array'; +export * from './source/basic'; +export * from './source/observable-like'; + +// Utilities +export {Except} from './source/except'; +export {Mutable} from './source/mutable'; +export {Writable} from './source/writable'; +export {Merge} from './source/merge'; +export {MergeExclusive} from './source/merge-exclusive'; +export {RequireAtLeastOne} from './source/require-at-least-one'; +export {RequireExactlyOne} from './source/require-exactly-one'; +export {RequireAllOrNone} from './source/require-all-or-none'; +export {RemoveIndexSignature} from './source/remove-index-signature'; +export {PartialDeep, PartialDeepOptions} from './source/partial-deep'; +export {PartialOnUndefinedDeep, PartialOnUndefinedDeepOptions} from './source/partial-on-undefined-deep'; +export {ReadonlyDeep} from './source/readonly-deep'; +export {LiteralUnion} from './source/literal-union'; +export {Promisable} from './source/promisable'; +export {Opaque, UnwrapOpaque} from './source/opaque'; +export {InvariantOf} from './source/invariant-of'; +export {SetOptional} from './source/set-optional'; +export {SetRequired} from './source/set-required'; +export {SetNonNullable} from './source/set-non-nullable'; +export {ValueOf} from './source/value-of'; +export {PromiseValue} from './source/promise-value'; +export {AsyncReturnType} from './source/async-return-type'; +export {ConditionalExcept} from './source/conditional-except'; +export {ConditionalKeys} from './source/conditional-keys'; +export {ConditionalPick} from './source/conditional-pick'; +export {UnionToIntersection} from './source/union-to-intersection'; +export {Stringified} from './source/stringified'; +export {FixedLengthArray} from './source/fixed-length-array'; +export {MultidimensionalArray} from './source/multidimensional-array'; +export {MultidimensionalReadonlyArray} from './source/multidimensional-readonly-array'; +export {IterableElement} from './source/iterable-element'; +export {Entry} from './source/entry'; +export {Entries} from './source/entries'; +export {SetReturnType} from './source/set-return-type'; +export {Asyncify} from './source/asyncify'; +export {Simplify, SimplifyOptions} from './source/simplify'; +export {Jsonify} from './source/jsonify'; +export {Schema} from './source/schema'; +export {LiteralToPrimitive} from './source/literal-to-primitive'; +export { + PositiveInfinity, + NegativeInfinity, + Finite, + Integer, + Float, + NegativeFloat, + Negative, + NonNegative, + NegativeInteger, + NonNegativeInteger, +} from './source/numeric'; +export {StringKeyOf} from './source/string-key-of'; +export {Exact} from './source/exact'; +export {ReadonlyTuple} from './source/readonly-tuple'; +export {OptionalKeysOf} from './source/optional-keys-of'; +export {HasOptionalKeys} from './source/has-optional-keys'; +export {RequiredKeysOf} from './source/required-keys-of'; +export {HasRequiredKeys} from './source/has-required-keys'; +export {Spread} from './source/spread'; + +// Template literal types +export {CamelCase} from './source/camel-case'; +export {CamelCasedProperties} from './source/camel-cased-properties'; +export {CamelCasedPropertiesDeep} from './source/camel-cased-properties-deep'; +export {KebabCase} from './source/kebab-case'; +export {KebabCasedProperties} from './source/kebab-cased-properties'; +export {KebabCasedPropertiesDeep} from './source/kebab-cased-properties-deep'; +export {PascalCase} from './source/pascal-case'; +export {PascalCasedProperties} from './source/pascal-cased-properties'; +export {PascalCasedPropertiesDeep} from './source/pascal-cased-properties-deep'; +export {SnakeCase} from './source/snake-case'; +export {SnakeCasedProperties} from './source/snake-cased-properties'; +export {SnakeCasedPropertiesDeep} from './source/snake-cased-properties-deep'; +export {ScreamingSnakeCase} from './source/screaming-snake-case'; +export {DelimiterCase} from './source/delimiter-case'; +export {DelimiterCasedProperties} from './source/delimiter-cased-properties'; +export {DelimiterCasedPropertiesDeep} from './source/delimiter-cased-properties-deep'; +export {Join} from './source/join'; +export {Split} from './source/split'; +export {Trim} from './source/trim'; +export {Replace} from './source/replace'; +export {Includes} from './source/includes'; +export {Get} from './source/get'; +export {LastArrayElement} from './source/last-array-element'; + +// Miscellaneous +export {PackageJson} from './source/package-json'; +export {TsConfigJson} from './source/tsconfig-json'; diff --git a/website/node_modules/type-fest/package.json b/website/node_modules/type-fest/package.json new file mode 100644 index 00000000..319558a2 --- /dev/null +++ b/website/node_modules/type-fest/package.json @@ -0,0 +1,52 @@ +{ + "name": "type-fest", + "version": "2.19.0", + "description": "A collection of essential TypeScript types", + "license": "(MIT OR CC0-1.0)", + "repository": "sindresorhus/type-fest", + "funding": "https://github.com/sponsors/sindresorhus", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "engines": { + "node": ">=12.20" + }, + "scripts": { + "test": "xo && tsd && tsc && node script/test/source-files-extension.js" + }, + "files": [ + "index.d.ts", + "source" + ], + "keywords": [ + "typescript", + "ts", + "types", + "utility", + "util", + "utilities", + "omit", + "merge", + "json" + ], + "devDependencies": { + "@sindresorhus/tsconfig": "~0.7.0", + "expect-type": "^0.13.0", + "tsd": "^0.20.0", + "typescript": "^4.6.3", + "xo": "^0.43.0" + }, + "types": "./index.d.ts", + "xo": { + "rules": { + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/indent": "off", + "node/no-unsupported-features/es-builtins": "off", + "import/extensions": "off", + "@typescript-eslint/no-redeclare": "off", + "@typescript-eslint/no-confusing-void-expression": "off" + } + } +} diff --git a/website/node_modules/type-fest/readme.md b/website/node_modules/type-fest/readme.md new file mode 100644 index 00000000..0d310aad --- /dev/null +++ b/website/node_modules/type-fest/readme.md @@ -0,0 +1,905 @@ +
+
+
+ type-fest +
+
+ A collection of essential TypeScript types +
+
+
+
+
+ +
+
+
+
+
+ +[![](https://img.shields.io/badge/unicorn-approved-ff69b4.svg)](https://giphy.com/gifs/illustration-rainbow-unicorn-26AHG5KGFxSkUWw1i) +[![npm dependents](https://badgen.net/npm/dependents/type-fest)](https://www.npmjs.com/package/type-fest?activeTab=dependents) +[![npm downloads](https://badgen.net/npm/dt/type-fest)](https://www.npmjs.com/package/type-fest) +[![Docs](https://paka.dev/badges/v0/cute.svg)](https://paka.dev/npm/type-fest) + +Many of the types here should have been built-in. You can help by suggesting some of them to the [TypeScript project](https://github.com/Microsoft/TypeScript/blob/main/CONTRIBUTING.md). + +Either add this package as a dependency or copy-paste the needed types. No credit required. 👌 + +PR welcome for additional commonly needed types and docs improvements. Read the [contributing guidelines](.github/contributing.md) first. + +**Help wanted with reviewing [proposals](https://github.com/sindresorhus/type-fest/issues) and [pull requests](https://github.com/sindresorhus/type-fest/pulls).** + +## Install + +```sh +npm install type-fest +``` + +*Requires TypeScript >=4.2* + +## Usage + +```ts +import type {Except} from 'type-fest'; + +type Foo = { + unicorn: string; + rainbow: boolean; +}; + +type FooWithoutRainbow = Except; +//=> {unicorn: string} +``` + +## API + +Click the type names for complete docs. + +### Basic + +- [`Primitive`](source/primitive.d.ts) - Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive). +- [`Class`](source/basic.d.ts) - Matches a [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). +- [`Constructor`](source/basic.d.ts) - Matches a [`class` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). +- [`TypedArray`](source/typed-array.d.ts) - Matches any [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray), like `Uint8Array` or `Float64Array`. +- [`ObservableLike`](source/observable-like.d.ts) - Matches a value that is like an [Observable](https://github.com/tc39/proposal-observable). + +### Utilities + +- [`Except`](source/except.d.ts) - Create a type from an object type without certain keys. This is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys). +- [`Writable`](source/writable.d.ts) - Create a type that strips `readonly` from all or some of an object's keys. The inverse of `Readonly`. Formerly named `Mutable`. +- [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type. +- [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive keys. +- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given keys. +- [`RequireExactlyOne`](source/require-exactly-one.d.ts) - Create a type that requires exactly a single key of the given keys and disallows more. +- [`RequireAllOrNone`](source/require-all-or-none.d.ts) - Create a type that requires all of the given keys or none of the given keys. +- [`RemoveIndexSignature`](source/remove-index-signature.d.ts) - Create a type that only has explicitly defined properties, absent of any index signatures. +- [`PartialDeep`](source/partial-deep.d.ts) - Create a deeply optional version of another type. Use [`Partial`](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype) if you only need one level deep. +- [`PartialOnUndefinedDeep`](source/partial-on-undefined-deep.d.ts) - Create a deep version of another type where all keys accepting `undefined` type are set to optional. +- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of an `object`/`Map`/`Set`/`Array` type. Use [`Readonly`](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype) if you only need one level deep. +- [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). +- [`Opaque`](source/opaque.d.ts) - Create an [opaque type](https://codemix.com/opaque-types-in-javascript/). +- [`UnwrapOpaque`](source/opaque.d.ts) - Revert an [opaque type](https://codemix.com/opaque-types-in-javascript/) back to its original type. +- [`InvariantOf`](source/invariant-of.d.ts) - Create an [invariant type](https://basarat.gitbook.io/typescript/type-system/type-compatibility#footnote-invariance), which is a type that does not accept supertypes and subtypes. +- [`SetOptional`](source/set-optional.d.ts) - Create a type that makes the given keys optional. +- [`SetRequired`](source/set-required.d.ts) - Create a type that makes the given keys required. +- [`SetNonNullable`](source/set-non-nullable.d.ts) - Create a type that makes the given keys non-nullable. +- [`ValueOf`](source/value-of.d.ts) - Create a union of the given object's values, and optionally specify which keys to get the values from. +- [`ConditionalKeys`](source/conditional-keys.d.ts) - Extract keys from a shape where values extend the given `Condition` type. +- [`ConditionalPick`](source/conditional-pick.d.ts) - Like `Pick` except it selects properties from a shape where the values extend the given `Condition` type. +- [`ConditionalExcept`](source/conditional-except.d.ts) - Like `Omit` except it removes properties from a shape where the values extend the given `Condition` type. +- [`UnionToIntersection`](source/union-to-intersection.d.ts) - Convert a union type to an intersection type. +- [`LiteralToPrimitive`](source/literal-to-primitive.d.ts) - Convert a [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) to the [primitive type](source/primitive.d.ts) it belongs to. +- [`Stringified`](source/stringified.d.ts) - Create a type with the keys of the given type changed to `string` type. +- [`IterableElement`](source/iterable-element.d.ts) - Get the element type of an `Iterable`/`AsyncIterable`. For example, an array or a generator. +- [`Entry`](source/entry.d.ts) - Create a type that represents the type of an entry of a collection. +- [`Entries`](source/entries.d.ts) - Create a type that represents the type of the entries of a collection. +- [`SetReturnType`](source/set-return-type.d.ts) - Create a function type with a return type of your choice and the same parameters as the given function type. +- [`Simplify`](source/simplify.d.ts) - Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability. +- [`Get`](source/get.d.ts) - Get a deeply-nested property from an object using a key path, like [Lodash's `.get()`](https://lodash.com/docs/latest#get) function. +- [`StringKeyOf`](source/string-key-of.d.ts) - Get keys of the given type as strings. +- [`Schema`](source/schema.d.ts) - Create a deep version of another object type where property values are recursively replaced into a given value type. +- [`Exact`](source/exact.d.ts) - Create a type that does not allow extra properties. +- [`OptionalKeysOf`](source/optional-keys-of.d.ts) - Extract all optional keys from the given type. +- [`HasOptionalKeys`](source/has-optional-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any optional fields. +- [`RequiredKeysOf`](source/required-keys-of.d.ts) - Extract all required keys from the given type. +- [`HasRequiredKeys`](source/has-required-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any required fields. +- [`Spread`](source/spread.d.ts) - Mimic the type inferred by TypeScript when merging two objects or two arrays/tuples using the spread syntax. + +### JSON + +- [`Jsonify`](source/jsonify.d.ts) - Transform a type to one that is assignable to the `JsonValue` type. +- [`JsonPrimitive`](source/basic.d.ts) - Matches a JSON primitive. +- [`JsonObject`](source/basic.d.ts) - Matches a JSON object. +- [`JsonArray`](source/basic.d.ts) - Matches a JSON array. +- [`JsonValue`](source/basic.d.ts) - Matches any valid JSON value. + +### Async + +- [`Promisable`](source/promisable.d.ts) - Create a type that represents either the value or the value wrapped in `PromiseLike`. +- [`AsyncReturnType`](source/async-return-type.d.ts) - Unwrap the return type of a function that returns a `Promise`. +- [`Asyncify`](source/asyncify.d.ts) - Create an async version of the given function type. + +### String + +- [`Trim`](source/trim.d.ts) - Remove leading and trailing spaces from a string. +- [`Split`](source/split.d.ts) - Represents an array of strings split using a given character or character set. +- [`Replace`](source/replace.d.ts) - Represents a string with some or all matches replaced by a replacement. + +### Array + +- [`Includes`](source/includes.d.ts) - Returns a boolean for whether the given array includes the given item. +- [`Join`](source/join.d.ts) - Join an array of strings and/or numbers using the given string as a delimiter. +- [`LastArrayElement`](source/last-array-element.d.ts) - Extracts the type of the last element of an array. +- [`FixedLengthArray`](source/fixed-length-array.d.ts) - Create a type that represents an array of the given type and length. +- [`MultidimensionalArray`](source/multidimensional-array.d.ts) - Create a type that represents a multidimensional array of the given type and dimensions. +- [`MultidimensionalReadonlyArray`](source/multidimensional-readonly-array.d.ts) - Create a type that represents a multidimensional readonly array of the given type and dimensions. +- [`ReadonlyTuple`](source/readonly-tuple.d.ts) - Create a type that represents a read-only tuple of the given type and length. + +### Numeric + +- [`PositiveInfinity`](source/numeric.d.ts) - Matches the hidden `Infinity` type. +- [`NegativeInfinity`](source/numeric.d.ts) - Matches the hidden `-Infinity` type. +- [`Finite`](source/numeric.d.ts) - A finite `number`. +- [`Integer`](source/numeric.d.ts) - A `number` that is an integer. +- [`Float`](source/numeric.d.ts) - A `number` that is not an integer. +- [`NegativeFloat`](source/numeric.d.ts) - A negative (`-∞ < x < 0`) `number` that is not an integer. +- [`Negative`](source/numeric.d.ts) - A negative `number`/`bigint` (`-∞ < x < 0`) +- [`NonNegative`](source/numeric.d.ts) - A non-negative `number`/`bigint` (`0 <= x < ∞`). +- [`NegativeInteger`](source/numeric.d.ts) - A negative (`-∞ < x < 0`) `number` that is an integer. +- [`NonNegativeInteger`](source/numeric.d.ts) - A non-negative (`0 <= x < ∞`) `number` that is an integer. + +### Change case + +- [`CamelCase`](source/camel-case.d.ts) - Convert a string literal to camel-case (`fooBar`). +- [`CamelCasedProperties`](source/camel-cased-properties.d.ts) - Convert object properties to camel-case (`fooBar`). +- [`CamelCasedPropertiesDeep`](source/camel-cased-properties-deep.d.ts) - Convert object properties to camel-case recursively (`fooBar`). +- [`KebabCase`](source/kebab-case.d.ts) - Convert a string literal to kebab-case (`foo-bar`). +- [`KebabCasedProperties`](source/kebab-cased-properties.d.ts) - Convert a object properties to kebab-case recursively (`foo-bar`). +- [`KebabCasedPropertiesDeep`](source/kebab-cased-properties-deep.d.ts) - Convert object properties to kebab-case (`foo-bar`). +- [`PascalCase`](source/pascal-case.d.ts) - Converts a string literal to pascal-case (`FooBar`) +- [`PascalCasedProperties`](source/pascal-cased-properties.d.ts) - Converts object properties to pascal-case (`FooBar`) +- [`PascalCasedPropertiesDeep`](source/pascal-cased-properties-deep.d.ts) - Converts object properties to pascal-case (`FooBar`) +- [`SnakeCase`](source/snake-case.d.ts) - Convert a string literal to snake-case (`foo_bar`). +- [`SnakeCasedProperties`](source/snake-cased-properties-deep.d.ts) - Convert object properties to snake-case (`foo_bar`). +- [`SnakeCasedPropertiesDeep`](source/snake-cased-properties-deep.d.ts) - Convert object properties to snake-case recursively (`foo_bar`). +- [`ScreamingSnakeCase`](source/screaming-snake-case.d.ts) - Convert a string literal to screaming-snake-case (`FOO_BAR`). +- [`DelimiterCase`](source/delimiter-case.d.ts) - Convert a string literal to a custom string delimiter casing. +- [`DelimiterCasedProperties`](source/delimiter-cased-properties.d.ts) - Convert object properties to a custom string delimiter casing. +- [`DelimiterCasedPropertiesDeep`](source/delimiter-cased-properties-deep.d.ts) - Convert object properties to a custom string delimiter casing recursively. + +### Miscellaneous + +- [`PackageJson`](source/package-json.d.ts) - Type for [npm's `package.json` file](https://docs.npmjs.com/creating-a-package-json-file). It also includes support for [TypeScript Declaration Files](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html) and [Yarn Workspaces](https://classic.yarnpkg.com/lang/en/docs/workspaces/). +- [`TsConfigJson`](source/tsconfig-json.d.ts) - Type for [TypeScript's `tsconfig.json` file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) (TypeScript 4.4). + +## Declined types + +*If we decline a type addition, we will make sure to document the better solution here.* + +- [`Diff` and `Spread`](https://github.com/sindresorhus/type-fest/pull/7) - The pull request author didn't provide any real-world use-cases and the PR went stale. If you think this type is useful, provide some real-world use-cases and we might reconsider. +- [`Dictionary`](https://github.com/sindresorhus/type-fest/issues/33) - You only save a few characters (`Dictionary` vs `Record`) from [`Record`](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type), which is more flexible and well-known. Also, you shouldn't use an object as a dictionary. We have `Map` in JavaScript now. +- [`ExtractProperties` and `ExtractMethods`](https://github.com/sindresorhus/type-fest/pull/4) - The types violate the single responsibility principle. Instead, refine your types into more granular type hierarchies. +- [`Url2Json`](https://github.com/sindresorhus/type-fest/pull/262) - Inferring search parameters from a URL string is a cute idea, but not very useful in practice, since search parameters are usually dynamic and defined separately. +- [`Nullish`](https://github.com/sindresorhus/type-fest/pull/318) - The type only saves a couple of characters, not everyone knows what "nullish" means, and I'm also trying to [get away from `null`](https://github.com/sindresorhus/meta/discussions/7). +- [`TitleCase`](https://github.com/sindresorhus/type-fest/pull/303) - It's not solving a common need and is a better fit for a separate package. +- [`ExtendOr` and `ExtendAnd`](https://github.com/sindresorhus/type-fest/pull/247) - The benefits don't outweigh having to learn what they mean. +- [`PackageJsonExtras`](https://github.com/sindresorhus/type-fest/issues/371) - There are too many possible configurations that can be put into `package.json`. If you would like to extend `PackageJson` to support an additional configuration in your project, please see the *Extending existing types* section below. + +## Alternative type names + +*If you know one of our types by a different name, add it here for discovery.* + +- `PartialBy` - See [`SetOptional`](https://github.com/sindresorhus/type-fest/blob/main/source/set-optional.d.ts) +- `RecordDeep`- See [`Schema`](https://github.com/sindresorhus/type-fest/blob/main/source/schema.d.ts) + +## Tips + +### Extending existing types + +- [`PackageJson`](source/package-json.d.ts) - There are a lot of tools that place extra configurations inside the `package.json` file. You can extend `PackageJson` to support these additional configurations. +
+ + Example + + + [Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBDAnmApnA3gBQIYGMDW2A5igFIDOEAdnNuXAEJ0o4HFmVUC+cAZlBBBwA5ElQBaXinIxhAbgCwAKFCRYCZGnQAZYFRgooPfoJHSANntmKlysWlaESFanAC8jZo-YuaAMgwLKwBhal5gIgB+AC44XX1DADpQqnCiLhsgA) + + ```ts + import type {PackageJson as BasePackageJson} from 'type-fest'; + import type {Linter} from 'eslint'; + + type PackageJson = BasePackageJson & {eslintConfig?: Linter.Config}; + ``` +
+ +### Related + +- [typed-query-selector](https://github.com/g-plane/typed-query-selector) - Enhances `document.querySelector` and `document.querySelectorAll` with a template literal type that matches element types returned from an HTML element query selector. +- [`Linter.Config`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/eslint/index.d.ts) - Definitions for the [ESLint configuration schema](https://eslint.org/docs/user-guide/configuring/language-options). + +### Built-in types + +There are many advanced types most users don't know about. + +- [`Partial`](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype) - Make all properties in `T` optional. +
+ + Example + + + [Playground](https://www.typescriptlang.org/play/#code/JYOwLgpgTgZghgYwgAgHIHsAmEDC6QzADmyA3gLABQyycADnanALYQBcyAzmFKEQNxUaddFDAcQAV2YAjaIMoBfKlQQAbOJ05osEAIIMAQpOBrsUMkOR1eANziRkCfISKSoD4Pg4ZseAsTIALyW1DS0DEysHADkvvoMMQA0VsKi4sgAzAAMuVaKClY2wPaOknSYDrguADwA0sgQAB6QIJjaANYQAJ7oMDp+LsQAfAAUXd0cdUnI9mo+uv6uANp1ALoAlKHhyGAAFsCcAHTOAW4eYF4gyxNrwbNwago0ypRWp66jH8QcAApwYmAjxq8SWIy2FDCNDA3ToKFBQyIdR69wmfQG1TOhShyBgomQX3w3GQE2Q6IA8jIAFYQBBgI4TTiEs5bTQYsFInrLTbbHZOIlgZDlSqQABqj0kKBC3yINx6a2xfOQwH6o2FVXFaklwSCIUkbQghBAEEwENSfNOlykEGefNe5uhB2O6sgS3GPRmLogmslG1tLxUOKgEDA7hAuydtteryAA) + + ```ts + interface NodeConfig { + appName: string; + port: number; + } + + class NodeAppBuilder { + private configuration: NodeConfig = { + appName: 'NodeApp', + port: 3000 + }; + + private updateConfig(key: Key, value: NodeConfig[Key]) { + this.configuration[key] = value; + } + + config(config: Partial) { + type NodeConfigKey = keyof NodeConfig; + + for (const key of Object.keys(config) as NodeConfigKey[]) { + const updateValue = config[key]; + + if (updateValue === undefined) { + continue; + } + + this.updateConfig(key, updateValue); + } + + return this; + } + } + + // `Partial`` allows us to provide only a part of the + // NodeConfig interface. + new NodeAppBuilder().config({appName: 'ToDoApp'}); + ``` +
+ +- [`Required`](https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype) - Make all properties in `T` required. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgGED21VQGJZwC2wA3gFCjXAzFJgA2A-AFzADOUckA5gNxUaIYjA4ckvGG07c+g6gF8KQkAgCuEFFDA5O6gEbEwUbLm2ESwABQIixACJIoSdgCUYAR3Vg4MACYAPGYuFvYAfACU5Ko0APRxwADKMBD+wFAAFuh2Vv7OSBlYGdmc8ABu8LHKsRyGxqY4oQT21pTCIHQMjOwA5DAAHgACxAAOjDAAdChYxL0ANLHUouKSMH0AEmAAhJhY6ozpAJ77GTCMjMCiV0ToSAb7UJPPC9WRgrEJwAAqR6MwSRQPFGUFocDgRHYxnEfGAowh-zgUCOwF6KwkUl6tXqJhCeEsxDaS1AXSYfUGI3GUxmc0WSneQA) + + ```ts + interface ContactForm { + email?: string; + message?: string; + } + + function submitContactForm(formData: Required) { + // Send the form data to the server. + } + + submitContactForm({ + email: 'ex@mple.com', + message: 'Hi! Could you tell me more about…', + }); + + // TypeScript error: missing property 'message' + submitContactForm({ + email: 'ex@mple.com', + }); + ``` +
+ +- [`Readonly`](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype) - Make all properties in `T` readonly. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4UwOwVwW2AZA9gc3mAbmANsA3gKFCOAHkAzMgGkOJABEwAjKZa2kAUQCcvEu32AMQCGAF2FYBIAL4BufDRABLCKLBcywgMZgEKZOoDCiCGSXI8i4hGEwwALmABnUVxXJ57YFgzZHSVF8sT1BpBSItLGEnJz1kAy5LLy0TM2RHACUwYQATEywATwAeAITjU3MAPnkrCJMXLigtUT4AClxgGztKbyDgaX99I1TzAEokr1BRAAslJwA6FIqLAF48TtswHp9MHDla9hJGACswZvmyLjAwAC8wVpm5xZHkUZDaMKIwqyWXYCW0oN4sNlsA1h0ug5gAByACyBQAggAHJHQ7ZBIFoXbzBjMCz7OoQP5YIaJNYQMAAdziCVaALGNSIAHomcAACoFJFgADKWjcSNEwG4vC4ji0wggEEQguiTnMEGALWAV1yAFp8gVgEjeFyuKICvMrCTgVxnst5jtsGC4ljsPNhXxGaAWcAAOq6YRXYDCRg+RWIcA5JSC+kWdCepQ+v3RYCU3RInzRMCGwlpC19NYBW1Ye08R1AA) + + ```ts + enum LogLevel { + Off, + Debug, + Error, + Fatal + }; + + interface LoggerConfig { + name: string; + level: LogLevel; + } + + class Logger { + config: Readonly; + + constructor({name, level}: LoggerConfig) { + this.config = {name, level}; + Object.freeze(this.config); + } + } + + const config: LoggerConfig = { + name: 'MyApp', + level: LogLevel.Debug + }; + + const logger = new Logger(config); + + // TypeScript Error: cannot assign to read-only property. + logger.config.level = LogLevel.Error; + + // We are able to edit config variable as we please. + config.level = LogLevel.Error; + ``` +
+ +- [`Pick`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys) - From `T`, pick a set of properties whose keys are in the union `K`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgEE5TCgNugN4BQoZwOUBAXMAM5RyQDmA3KeSFABYCuAtgCMISMHloMmENh04oA9tBjQJjFuzIBfYrOAB6PcADCcGElh1gEGAHcKATwAO6ebyjB5CTNlwFwSxFR0BX5HeToYABNgBDh5fm8cfBg6AHIKG3ldA2BHOOcfFNpUygJ0pAhokr4hETFUgDpswywkggAFUwA3MFtgAF5gQgowKhhVKTYKGuFRcXo1aVZgbTIoJ3RW3xhOmB6+wfbcAGsAHi3kgBpgEtGy4AAfG54BWfqAPnZm4AAlZUj4MAkMA8GAGB4vEgfMlLLw6CwPBA8PYRmMgZVgAC6CgmI4cIommQELwICh8RBgKZKvALh1ur0bHQABR5PYMui0Wk7em2ADaAF0AJS0AASABUALIAGQAogR+Mp3CROCAFBBwVC2ikBpj5CgBIqGjizLA5TAFdAmalImAuqlBRoVQh5HBgEy1eDWfs7J5cjzGYKhroVfpDEhHM4MV6GRR5NN0JrtnRg6BVirTFBeHAKYmYY6QNpdB73LmCJZBlSAXAubtvczeSmQMNSuMbmKNgBlHFgPEUNwusBIPAAQlS1xetTmxT0SDoESgdD0C4aACtHMwxytLrohawgA) + + ```ts + interface Article { + title: string; + thumbnail: string; + content: string; + } + + // Creates new type out of the `Article` interface composed + // from the Articles' two properties: `title` and `thumbnail`. + // `ArticlePreview = {title: string; thumbnail: string}` + type ArticlePreview = Pick; + + // Render a list of articles using only title and description. + function renderArticlePreviews(previews: ArticlePreview[]): HTMLElement { + const articles = document.createElement('div'); + + for (const preview of previews) { + // Append preview to the articles. + } + + return articles; + } + + const articles = renderArticlePreviews([ + { + title: 'TypeScript tutorial!', + thumbnail: '/assets/ts.jpg' + } + ]); + ``` +
+ +- [`Record`](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) - Construct a type with a set of properties `K` of type `T`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4ejYAUHsGcCWAXBMB2dgwGbAKYC2ADgDYwCeeemCaWArgE7ADGMxAhmuQHQBQoYEnJE8wALKEARnkaxEKdMAC8wAOS0kstGuAAfdQBM8ANzxlRjXQbVaWACwC0JPB0NqA3HwGgIwAJJoWozYHCxixnAsjAhStADmwESMMJYo1Fi4HMCIaPEu+MRklHj8gpqyoeHAAKJFFFTAAN4+giDYCIxwSAByHAR4AFw5SDF5Xm2gJBzdfQPD3WPxE5PAlBxdAPLYNQAelgh4aOHDaPQEMowrIAC+3oJ+AMKMrlrAXFhSAFZ4LEhC9g4-0BmA4JBISXgiCkBQABpILrJ5MhUGhYcATGD6Bk4Hh-jNgABrPDkOBlXyQAAq9ngYmJpOAAHcEOCRjAXqwYODfoo6DhakUSph+Uh7GI4P0xER4Cj0OSQGwMP8tP1hgAlX7swwAHgRl2RvIANALSA08ABtAC6AD4VM1Wm0Kow0MMrYaHYJjGYLLJXZb3at1HYnC43Go-QHQDcvA6-JsmEJXARgCDgMYWAhjIYhDAU+YiMAAFIwex0ZmilMITCGF79TLAGRsAgJYAAZRwSEZGzEABFTOZUrJ5Yn+jwnWgeER6HB7AAKJrADpdXqS4ZqYultTG6azVfqHswPBbtauLY7fayQ7HIbAAAMwBuAEoYw9IBq2Ixs9h2eFMOQYPQObALQKJgggABeYhghCIpikkKRpOQRIknAsZUiIeCttECBEP8NSMCkjDDAARMGziuIYxHwYOjDCMBmDNnAuTxA6irdCOBB1Lh5Dqpqn66tISIykawBnOCtqqC0gbjqc9DgpGkxegOliyfJDrRkAA) + + ```ts + // Positions of employees in our company. + type MemberPosition = 'intern' | 'developer' | 'tech-lead'; + + // Interface describing properties of a single employee. + interface Employee { + firstName: string; + lastName: string; + yearsOfExperience: number; + } + + // Create an object that has all possible `MemberPosition` values set as keys. + // Those keys will store a collection of Employees of the same position. + const team: Record = { + intern: [], + developer: [], + 'tech-lead': [], + }; + + // Our team has decided to help John with his dream of becoming Software Developer. + team.intern.push({ + firstName: 'John', + lastName: 'Doe', + yearsOfExperience: 0 + }); + + // `Record` forces you to initialize all of the property keys. + // TypeScript Error: "tech-lead" property is missing + const teamEmpty: Record = { + intern: null, + developer: null, + }; + ``` +
+ +- [`Exclude`](https://www.typescriptlang.org/docs/handbook/utility-types.html#excludetype-excludedunion) - Exclude from `T` those types that are assignable to `U`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgMrQG7QMIHsQzADmyA3gFDLIAOuUYAXMiAK4A2byAPsgM5hRQJHqwC2AI2gBucgF9y5MAE9qKAEoQAjiwj8AEnBAATNtGQBeZAAooWphu26wAGmS3e93bRC8IASgsAPmRDJRlyAHoI5ABRAA8ENhYjFFYOZGVVZBgoXFFkAAM0zh5+QRBhZhYJaAKAOkjogEkQZAQ4X2QAdwALCFbaemRgXmQtFjhOMFwq9K6ULuB0lk6U+HYwZAxJnQaYFhAEMGB8ZCIIMAAFOjAANR2IK0HGWISklIAedCgsKDwCYgAbQA5M9gQBdVzFQJ+JhiSRQMiUYYwayZCC4VHPCzmSzAspCYEBWxgFhQAZwKC+FpgJ43VwARgADH4ZFQSWSBjcZPJyPtDsdTvxKWBvr8rD1DCZoJ5HPopaYoK4EPhCEQmGKcKriLCtrhgEYkVQVT5Nr4fmZLLZtMBbFZgT0wGBqES6ghbHBIJqoBKFdBWQpjfh+DQbhY2tqiHVsbjLMVkAB+ZAAZiZaeQTHOVxu9ySjxNaujNwDVHNvzqbBGkBAdPoAfkQA) + + ```ts + interface ServerConfig { + port: null | string | number; + } + + type RequestHandler = (request: Request, response: Response) => void; + + // Exclude `null` type from `null | string | number`. + // In case the port is equal to `null`, we will use default value. + function getPortValue(port: Exclude): number { + if (typeof port === 'string') { + return parseInt(port, 10); + } + + return port; + } + + function startServer(handler: RequestHandler, config: ServerConfig): void { + const server = require('http').createServer(handler); + + const port = config.port === null ? 3000 : getPortValue(config.port); + server.listen(port); + } + ``` +
+ +- [`Extract`](https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union) - Extract from `T` those types that are assignable to `U`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXzSwEdkQBJYACgEoAueVZAWwCMQYBuAKDDwGcM8MgBF4AXngBlAJ6scESgHIRi6ty5ZUGdoihgEABXZ888AN5d48ANoiAuvUat23K6ihMQ9ATE0BzV3goPy8GZjZOLgBfLi4Aejj4AEEICBwAdz54MAALKFQQ+BxEeAAHY1NgKAwoIKy0grr4DByEUpgccpgMaXgAaxBerCzi+B9-ZulygDouFHRsU1z8kKMYE1RhaqgAHkt4AHkWACt4EAAPbVRgLLWNgBp9gGlBs8uQa6yAUUuYPQwdgNpKM7nh7mMML4CgA+R5WABqUAgpDeVxuhxO1he0jsXGh8EoOBO9COx3BQPo2PBADckaR6IjkSA6PBqTgsMBzPsicdrEC7OJWXSQNwYvFEgAVTS9JLXODpeDpKBZFg4GCoWa8VACIJykAKiQWKy2YQOAioYikCg0OEMDyhRSy4DyxS24KhAAMjyi6gS8AAwjh5OD0iBFHAkJoEOksC1mnkMJq8gUQKDNttKPlnfrwYp3J5XfBHXqoKpfYkAOI4ansTxaeDADmoRSCCBYAbxhC6TDx6rwYHIRX5bScjA4bLJwoDmDwDkfbA9JMrVMVdM1TN69LgkTgwgkchUahqIA) + + ```ts + declare function uniqueId(): number; + + const ID = Symbol('ID'); + + interface Person { + [ID]: number; + name: string; + age: number; + } + + // Allows changing the person data as long as the property key is of string type. + function changePersonData< + Obj extends Person, + Key extends Extract, + Value extends Obj[Key] + > (obj: Obj, key: Key, value: Value): void { + obj[key] = value; + } + + // Tiny Andrew was born. + const andrew = { + [ID]: uniqueId(), + name: 'Andrew', + age: 0, + }; + + // Cool, we're fine with that. + changePersonData(andrew, 'name', 'Pony'); + + // Goverment didn't like the fact that you wanted to change your identity. + changePersonData(andrew, ID, uniqueId()); + ``` +
+ +- [`NonNullable`](https://www.typescriptlang.org/docs/handbook/utility-types.html#nonnullabletype) - Exclude `null` and `undefined` from `T`. +
+ + Example + + Works with strictNullChecks set to true. + + [Playground](https://typescript-play.js.org/?target=6#code/C4TwDgpgBACg9gJ2AOQK4FsBGEFQLxQDOwCAlgHYDmUAPlORtrnQwDasDcAUFwPQBU-WAEMkUOADMowqAGNWwwoSgATCBIqlgpOOSjAAFsOBRSy1IQgr9cKJlSlW1mZYQA3HFH68u8xcoBlHA8EACEHJ08Aby4oKDBUTFZSWXjEFEYcAEIALihkXTR2YSSIAB54JDQsHAA+blj4xOTUsHSACkMzPKD3HHDHNQQAGjSkPMqMmoQASh7g-oihqBi4uNIpdraxPAI2VhmVxrX9AzMAOm2ppnwoAA4ABifuE4BfKAhWSyOTuK7CS7pao3AhXF5rV48E4ICDAVAIPT-cGQyG+XTEIgLMJLTx7CAAdygvRCA0iCHaMwarhJOIQjUBSHaACJHk8mYdeLwxtdcVAAOSsh58+lXdr7Dlcq7A3n3J4PEUdADMcspUE53OluAIUGVTx46oAKuAIAFZGQwCYAKIIBCILjUxaDHAMnla+iodjcIA) + + ```ts + type PortNumber = string | number | null; + + /** Part of a class definition that is used to build a server */ + class ServerBuilder { + portNumber!: NonNullable; + + port(this: ServerBuilder, port: PortNumber): ServerBuilder { + if (port == null) { + this.portNumber = 8000; + } else { + this.portNumber = port; + } + + return this; + } + } + + const serverBuilder = new ServerBuilder(); + + serverBuilder + .port('8000') // portNumber = '8000' + .port(null) // portNumber = 8000 + .port(3000); // portNumber = 3000 + + // TypeScript error + serverBuilder.portNumber = null; + ``` +
+ +- [`Parameters`](https://www.typescriptlang.org/docs/handbook/utility-types.html#parameterstype) - Obtain the parameters of a function type in a tuple. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/GYVwdgxgLglg9mABAZwBYmMANgUwBQxgAOIUAXIgIZgCeA2gLoCUFAbnDACaIDeAUIkQB6IYgCypSlBxUATrMo1ECsJzgBbLEoipqAc0J7EMKMgDkiHLnU4wp46pwAPHMgB0fAL58+oSLARECEosLAA5ABUYG2QAHgAxJGdpVWREPDdMylk9ZApqemZEAF4APipacrw-CApEgBogkKwAYThwckQwEHUAIxxZJl4BYVEImiIZKF0oZRwiWVdbeygJmThgOYgcGFYcbhqApCJsyhtpWXcR1cnEePBoeDAABVPzgbTixFeFd8uEsClADcIxGiygIFkSEOT3SmTc2VydQeRx+ZxwF2QQ34gkEwDgsnSuFmMBKiAADEDjIhYk1Qm0OlSYABqZnYka4xA1DJZHJYkGc7yCbyeRA+CAIZCzNAYbA4CIAdxg2zJwVCkWirjwMswuEaACYmCCgA) + + ```ts + function shuffle(input: any[]): void { + // Mutate array randomly changing its' elements indexes. + } + + function callNTimes any> (func: Fn, callCount: number) { + // Type that represents the type of the received function parameters. + type FunctionParameters = Parameters; + + return function (...args: FunctionParameters) { + for (let i = 0; i < callCount; i++) { + func(...args); + } + } + } + + const shuffleTwice = callNTimes(shuffle, 2); + ``` +
+ +- [`ConstructorParameters`](https://www.typescriptlang.org/docs/handbook/utility-types.html#constructorparameterstype) - Obtain the parameters of a constructor function type in a tuple. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECCBOAXAlqApgWQPYBM0mgG8AoaaFRENALmgkXmQDsBzAblOmCycTV4D8teo1YdO3JiICuwRFngAKClWENmLAJRFOZRAAtkEAHQq00ALzlklNBzIBfYk+KhIMAJJTEYJsDQAwmDA+mgAPAAq0GgAHnxMODCKTGgA7tCKxllg8CwQtL4AngDaALraFgB80EWa1SRkAA6MAG5gfNAB4FABPDJyCrQR9tDNyG0dwMGhtBhgjWEiGgA00F70vv4RhY3hEZXVVinpc42KmuJkkv3y8Bly8EPaDWTkhiZd7r3e8LK3llwGCMXGQWGhEOsfH5zJlsrl8p0+gw-goAAo5MAAW3BaHgEEilU0tEhmzQ212BJ0ry4SOg+kg+gBBiMximIGA0nAfAQLGk2N4EAAEgzYcYcnkLsRdDTvNEYkYUKwSdCme9WdM0MYwYhFPSIPpJdTkAAzDKxBUaZX+aAAQgsVmkCTQxuYaBw2ng4Ok8CYcotSu8pMur09iG9vuObxZnx6SN+AyUWTF8MN0CcZE4Ywm5jZHK5aB5fP4iCFIqT4oRRTKRLo6lYVNeAHpG50wOzOe1zHr9NLQ+HoABybsD4HOKXXRA1JCoKhBELmI5pNaB6Fz0KKBAodDYPAgSUTmqYsAALx4m5nC6nW9nGq14KtaEUA9gR9PvuNCjQ9BgACNvcwNBtAcLiAA) + + ```ts + class ArticleModel { + title: string; + content?: string; + + constructor(title: string) { + this.title = title; + } + } + + class InstanceCache any)> { + private ClassConstructor: T; + private cache: Map> = new Map(); + + constructor (ctr: T) { + this.ClassConstructor = ctr; + } + + getInstance (...args: ConstructorParameters): InstanceType { + const hash = this.calculateArgumentsHash(...args); + + const existingInstance = this.cache.get(hash); + if (existingInstance !== undefined) { + return existingInstance; + } + + return new this.ClassConstructor(...args); + } + + private calculateArgumentsHash(...args: any[]): string { + // Calculate hash. + return 'hash'; + } + } + + const articleCache = new InstanceCache(ArticleModel); + const amazonArticle = articleCache.getInstance('Amazon forests burining!'); + ``` +
+ +- [`ReturnType`](https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype) - Obtain the return type of a function type. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA) + + ```ts + /** Provides every element of the iterable `iter` into the `callback` function and stores the results in an array. */ + function mapIter< + Elem, + Func extends (elem: Elem) => any, + Ret extends ReturnType + >(iter: Iterable, callback: Func): Ret[] { + const mapped: Ret[] = []; + + for (const elem of iter) { + mapped.push(callback(elem)); + } + + return mapped; + } + + const setObject: Set = new Set(); + const mapObject: Map = new Map(); + + mapIter(setObject, (value: string) => value.indexOf('Foo')); // number[] + + mapIter(mapObject, ([key, value]: [number, string]) => { + return key % 2 === 0 ? value : 'Odd'; + }); // string[] + ``` +
+ +- [`InstanceType`](https://www.typescriptlang.org/docs/handbook/utility-types.html#instancetypetype) - Obtain the instance type of a constructor function type. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA) + + ```ts + class IdleService { + doNothing (): void {} + } + + class News { + title: string; + content: string; + + constructor(title: string, content: string) { + this.title = title; + this.content = content; + } + } + + const instanceCounter: Map = new Map(); + + interface Constructor { + new(...args: any[]): any; + } + + // Keep track how many instances of `Constr` constructor have been created. + function getInstance< + Constr extends Constructor, + Args extends ConstructorParameters + >(constructor: Constr, ...args: Args): InstanceType { + let count = instanceCounter.get(constructor) || 0; + + const instance = new constructor(...args); + + instanceCounter.set(constructor, count + 1); + + console.log(`Created ${count + 1} instances of ${Constr.name} class`); + + return instance; + } + + + const idleService = getInstance(IdleService); + // Will log: `Created 1 instances of IdleService class` + const newsEntry = getInstance(News, 'New ECMAScript proposals!', 'Last month...'); + // Will log: `Created 1 instances of News class` + ``` +
+ +- [`Omit`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys) - Constructs a type by picking all properties from T and then removing K. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgIImAWzgG2QbwChlks4BzCAVShwC5kBnMKUcgbmKYAcIFgIjBs1YgOXMpSFMWbANoBdTiW5woFddwAW0kfKWEAvoUIB6U8gDCUCHEiNkICAHdkYAJ69kz4GC3JcPG4oAHteKDABBxCYNAxsPFBIWEQUCAAPJG4wZABySUFcgJAAEzMLXNV1ck0dIuCw6EjBADpy5AB1FAQ4EGQAV0YUP2AHDy8wEOQbUugmBLwtEIA3OcmQnEjuZBgQqE7gAGtgZAhwKHdkHFGwNvGUdDIcAGUliIBJEF3kAF5kAHlML4ADyPBIAGjyBUYRQAPnkqho4NoYQA+TiEGD9EAISIhPozErQMG4AASK2gn2+AApek9pCSXm8wFSQooAJQMUkAFQAsgAZACiOAgmDOOSIJAQ+OYyGl4DgoDmf2QJRCCH6YvALQQNjsEGFovF1NyJWAy1y7OUyHMyE+yRAuFImG4Iq1YDswHxbRINjA-SgfXlHqVUE4xiAA) + + ```ts + interface Animal { + imageUrl: string; + species: string; + images: string[]; + paragraphs: string[]; + } + + // Creates new type with all properties of the `Animal` interface + // except 'images' and 'paragraphs' properties. We can use this + // type to render small hover tooltip for a wiki entry list. + type AnimalShortInfo = Omit; + + function renderAnimalHoverInfo (animals: AnimalShortInfo[]): HTMLElement { + const container = document.createElement('div'); + // Internal implementation. + return container; + } + ``` +
+ +- [`Uppercase`](https://www.typescriptlang.org/docs/handbook/utility-types.html#uppercasestringtype) - Transforms every character in a string into uppercase. +
+ + Example + + + ```ts + type T = Uppercase<'hello'>; // 'HELLO' + + type T2 = Uppercase<'foo' | 'bar'>; // 'FOO' | 'BAR' + + type T3 = Uppercase<`aB${S}`>; + type T4 = T3<'xYz'>; // 'ABXYZ' + + type T5 = Uppercase; // string + type T6 = Uppercase; // any + type T7 = Uppercase; // never + type T8 = Uppercase<42>; // Error, type 'number' does not satisfy the constraint 'string' + ``` +
+ +- [`Lowercase`](https://www.typescriptlang.org/docs/handbook/utility-types.html#lowercasestringtype) - Transforms every character in a string into lowercase. +
+ + Example + + + ```ts + type T = Lowercase<'HELLO'>; // 'hello' + + type T2 = Lowercase<'FOO' | 'BAR'>; // 'foo' | 'bar' + + type T3 = Lowercase<`aB${S}`>; + type T4 = T3<'xYz'>; // 'abxyz' + + type T5 = Lowercase; // string + type T6 = Lowercase; // any + type T7 = Lowercase; // never + type T8 = Lowercase<42>; // Error, type 'number' does not satisfy the constraint 'string' + ``` +
+ +- [`Capitalize`](https://www.typescriptlang.org/docs/handbook/utility-types.html#capitalizestringtype) - Transforms the first character in a string into uppercase. +
+ + Example + + + ```ts + type T = Capitalize<'hello'>; // 'Hello' + + type T2 = Capitalize<'foo' | 'bar'>; // 'Foo' | 'Bar' + + type T3 = Capitalize<`aB${S}`>; + type T4 = T3<'xYz'>; // 'ABxYz' + + type T5 = Capitalize; // string + type T6 = Capitalize; // any + type T7 = Capitalize; // never + type T8 = Capitalize<42>; // Error, type 'number' does not satisfy the constraint 'string' + ``` +
+ +- [`Uncapitalize`](https://www.typescriptlang.org/docs/handbook/utility-types.html#uncapitalizestringtype) - Transforms the first character in a string into lowercase. +
+ + Example + + + ```ts + type T = Uncapitalize<'Hello'>; // 'hello' + + type T2 = Uncapitalize<'Foo' | 'Bar'>; // 'foo' | 'bar' + + type T3 = Uncapitalize<`AB${S}`>; + type T4 = T3<'xYz'>; // 'aBxYz' + + type T5 = Uncapitalize; // string + type T6 = Uncapitalize; // any + type T7 = Uncapitalize; // never + type T8 = Uncapitalize<42>; // Error, type 'number' does not satisfy the constraint 'string' + ``` +
+ +You can find some examples in the [TypeScript docs](https://www.typescriptlang.org/docs/handbook/utility-types.html). + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Jarek Radosz](https://github.com/CvX) +- [Dimitri Benin](https://github.com/BendingBender) +- [Pelle Wessman](https://github.com/voxpelli) + +## License + +SPDX-License-Identifier: (MIT OR CC0-1.0) + +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/website/node_modules/type-fest/source/async-return-type.d.ts b/website/node_modules/type-fest/source/async-return-type.d.ts new file mode 100644 index 00000000..57cc3631 --- /dev/null +++ b/website/node_modules/type-fest/source/async-return-type.d.ts @@ -0,0 +1,25 @@ +import type {PromiseValue} from './promise-value'; + +type AsyncFunction = (...args: any[]) => Promise; + +/** +Unwrap the return type of a function that returns a `Promise`. + +There has been [discussion](https://github.com/microsoft/TypeScript/pull/35998) about implementing this type in TypeScript. + +@example +```ts +import type {AsyncReturnType} from 'type-fest'; +import {asyncFunction} from 'api'; + +// This type resolves to the unwrapped return type of `asyncFunction`. +type Value = AsyncReturnType; + +async function doSomething(value: Value) {} + +asyncFunction().then(value => doSomething(value)); +``` + +@category Async +*/ +export type AsyncReturnType = PromiseValue>; diff --git a/website/node_modules/type-fest/source/asyncify.d.ts b/website/node_modules/type-fest/source/asyncify.d.ts new file mode 100644 index 00000000..6726b7e1 --- /dev/null +++ b/website/node_modules/type-fest/source/asyncify.d.ts @@ -0,0 +1,33 @@ +import type {PromiseValue} from './promise-value'; +import type {SetReturnType} from './set-return-type'; + +/** +Create an async version of the given function type, by boxing the return type in `Promise` while keeping the same parameter types. + +Use-case: You have two functions, one synchronous and one asynchronous that do the same thing. Instead of having to duplicate the type definition, you can use `Asyncify` to reuse the synchronous type. + +@example +``` +import type {Asyncify} from 'type-fest'; + +// Synchronous function. +function getFooSync(someArg: SomeType): Foo { + // … +} + +type AsyncifiedFooGetter = Asyncify; +//=> type AsyncifiedFooGetter = (someArg: SomeType) => Promise; + +// Same as `getFooSync` but asynchronous. +const getFooAsync: AsyncifiedFooGetter = (someArg) => { + // TypeScript now knows that `someArg` is `SomeType` automatically. + // It also knows that this function must return `Promise`. + // If you have `@typescript-eslint/promise-function-async` linter rule enabled, it will even report that "Functions that return promises must be async.". + + // … +} +``` + +@category Async +*/ +export type Asyncify any> = SetReturnType>>>; diff --git a/website/node_modules/type-fest/source/basic.d.ts b/website/node_modules/type-fest/source/basic.d.ts new file mode 100644 index 00000000..ac74fdc9 --- /dev/null +++ b/website/node_modules/type-fest/source/basic.d.ts @@ -0,0 +1,45 @@ +/** +Matches a [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). + +@category Class +*/ +export type Class = Constructor & {prototype: T}; + +/** +Matches a [`class` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). + +@category Class +*/ +export type Constructor = new(...arguments_: Arguments) => T; + +/** +Matches a JSON object. + +This type can be useful to enforce some input to be JSON-compatible or as a super-type to be extended from. Don't use this as a direct return type as the user would have to double-cast it: `jsonObject as unknown as CustomResponse`. Instead, you could extend your CustomResponse type from it to ensure your type only uses JSON-compatible types: `interface CustomResponse extends JsonObject { … }`. + +@category JSON +*/ +export type JsonObject = {[Key in string]?: JsonValue}; + +/** +Matches a JSON array. + +@category JSON +*/ +export type JsonArray = JsonValue[]; + +/** +Matches any valid JSON primitive value. + +@category JSON +*/ +export type JsonPrimitive = string | number | boolean | null; + +/** +Matches any valid JSON value. + +@see `Jsonify` if you need to transform a type to one that is assignable to `JsonValue`. + +@category JSON +*/ +export type JsonValue = JsonPrimitive | JsonObject | JsonArray; diff --git a/website/node_modules/type-fest/source/camel-case.d.ts b/website/node_modules/type-fest/source/camel-case.d.ts new file mode 100644 index 00000000..bcf54fc9 --- /dev/null +++ b/website/node_modules/type-fest/source/camel-case.d.ts @@ -0,0 +1,73 @@ +import type {WordSeparators} from '../source/internal'; +import type {Split} from './split'; + +/** +Step by step takes the first item in an array literal, formats it and adds it to a string literal, and then recursively appends the remainder. + +Only to be used by `CamelCaseStringArray<>`. + +@see CamelCaseStringArray +*/ +type InnerCamelCaseStringArray = + Parts extends [`${infer FirstPart}`, ...infer RemainingParts] + ? FirstPart extends undefined + ? '' + : FirstPart extends '' + ? InnerCamelCaseStringArray + : `${PreviousPart extends '' ? FirstPart : Capitalize}${InnerCamelCaseStringArray}` + : ''; + +/** +Starts fusing the output of `Split<>`, an array literal of strings, into a camel-cased string literal. + +It's separate from `InnerCamelCaseStringArray<>` to keep a clean API outwards to the rest of the code. + +@see Split +*/ +type CamelCaseStringArray = + Parts extends [`${infer FirstPart}`, ...infer RemainingParts] + ? Uncapitalize<`${FirstPart}${InnerCamelCaseStringArray}`> + : never; + +/** +Convert a string literal to camel-case. + +This can be useful when, for example, converting some kebab-cased command-line flags or a snake-cased database result. + +@example +``` +import type {CamelCase} from 'type-fest'; + +// Simple + +const someVariable: CamelCase<'foo-bar'> = 'fooBar'; + +// Advanced + +type CamelCasedProperties = { + [K in keyof T as CamelCase]: T[K] +}; + +interface RawOptions { + 'dry-run': boolean; + 'full_family_name': string; + foo: number; + BAR: string; + QUZ_QUX: number; + 'OTHER-FIELD': boolean; +} + +const dbResult: CamelCasedProperties = { + dryRun: true, + fullFamilyName: 'bar.js', + foo: 123, + bar: 'foo', + quzQux: 6, + otherField: false +}; +``` + +@category Change case +@category Template literal +*/ +export type CamelCase = K extends string ? CamelCaseStringArray ? Lowercase : K, WordSeparators>> : K; diff --git a/website/node_modules/type-fest/source/camel-cased-properties-deep.d.ts b/website/node_modules/type-fest/source/camel-cased-properties-deep.d.ts new file mode 100644 index 00000000..c821dc46 --- /dev/null +++ b/website/node_modules/type-fest/source/camel-cased-properties-deep.d.ts @@ -0,0 +1,54 @@ +import type {CamelCase} from './camel-case'; + +/** +Convert object properties to camel case recursively. + +This can be useful when, for example, converting some API types from a different style. + +@see CamelCasedProperties +@see CamelCase + +@example +``` +import type {CamelCasedPropertiesDeep} from 'type-fest'; + +interface User { + UserId: number; + UserName: string; +} + +interface UserWithFriends { + UserInfo: User; + UserFriends: User[]; +} + +const result: CamelCasedPropertiesDeep = { + userInfo: { + userId: 1, + userName: 'Tom', + }, + userFriends: [ + { + userId: 2, + userName: 'Jerry', + }, + { + userId: 3, + userName: 'Spike', + }, + ], +}; +``` + +@category Change case +@category Template literal +@category Object +*/ +export type CamelCasedPropertiesDeep = Value extends Function + ? Value + : Value extends Array + ? Array> + : Value extends Set + ? Set> : { + [K in keyof Value as CamelCase]: CamelCasedPropertiesDeep; + }; diff --git a/website/node_modules/type-fest/source/camel-cased-properties.d.ts b/website/node_modules/type-fest/source/camel-cased-properties.d.ts new file mode 100644 index 00000000..afe0629e --- /dev/null +++ b/website/node_modules/type-fest/source/camel-cased-properties.d.ts @@ -0,0 +1,36 @@ +import type {CamelCase} from './camel-case'; + +/** +Convert object properties to camel case but not recursively. + +This can be useful when, for example, converting some API types from a different style. + +@see CamelCasedPropertiesDeep +@see CamelCase + +@example +``` +import type {CamelCasedProperties} from 'type-fest'; + +interface User { + UserId: number; + UserName: string; +} + +const result: CamelCasedProperties = { + userId: 1, + userName: 'Tom', +}; +``` + +@category Change case +@category Template literal +@category Object +*/ +export type CamelCasedProperties = Value extends Function + ? Value + : Value extends Array + ? Value + : { + [K in keyof Value as CamelCase]: Value[K]; + }; diff --git a/website/node_modules/type-fest/source/conditional-except.d.ts b/website/node_modules/type-fest/source/conditional-except.d.ts new file mode 100644 index 00000000..d61792a9 --- /dev/null +++ b/website/node_modules/type-fest/source/conditional-except.d.ts @@ -0,0 +1,45 @@ +import type {Except} from './except'; +import type {ConditionalKeys} from './conditional-keys'; + +/** +Exclude keys from a shape that matches the given `Condition`. + +This is useful when you want to create a new type with a specific set of keys from a shape. For example, you might want to exclude all the primitive properties from a class and form a new shape containing everything but the primitive properties. + +@example +``` +import type {Primitive, ConditionalExcept} from 'type-fest'; + +class Awesome { + name: string; + successes: number; + failures: bigint; + + run() {} +} + +type ExceptPrimitivesFromAwesome = ConditionalExcept; +//=> {run: () => void} +``` + +@example +``` +import type {ConditionalExcept} from 'type-fest'; + +interface Example { + a: string; + b: string | number; + c: () => void; + d: {}; +} + +type NonStringKeysOnly = ConditionalExcept; +//=> {b: string | number; c: () => void; d: {}} +``` + +@category Object +*/ +export type ConditionalExcept = Except< + Base, + ConditionalKeys +>; diff --git a/website/node_modules/type-fest/source/conditional-keys.d.ts b/website/node_modules/type-fest/source/conditional-keys.d.ts new file mode 100644 index 00000000..c337dbb2 --- /dev/null +++ b/website/node_modules/type-fest/source/conditional-keys.d.ts @@ -0,0 +1,47 @@ +/** +Extract the keys from a type where the value type of the key extends the given `Condition`. + +Internally this is used for the `ConditionalPick` and `ConditionalExcept` types. + +@example +``` +import type {ConditionalKeys} from 'type-fest'; + +interface Example { + a: string; + b: string | number; + c?: string; + d: {}; +} + +type StringKeysOnly = ConditionalKeys; +//=> 'a' +``` + +To support partial types, make sure your `Condition` is a union of undefined (for example, `string | undefined`) as demonstrated below. + +@example +``` +import type {ConditionalKeys} from 'type-fest'; + +type StringKeysAndUndefined = ConditionalKeys; +//=> 'a' | 'c' +``` + +@category Object +*/ +export type ConditionalKeys = NonNullable< + // Wrap in `NonNullable` to strip away the `undefined` type from the produced union. + { + // Map through all the keys of the given base type. + [Key in keyof Base]: + // Pick only keys with types extending the given `Condition` type. + Base[Key] extends Condition + // Retain this key since the condition passes. + ? Key + // Discard this key since the condition fails. + : never; + + // Convert the produced object into a union type of the keys which passed the conditional test. + }[keyof Base] +>; diff --git a/website/node_modules/type-fest/source/conditional-pick.d.ts b/website/node_modules/type-fest/source/conditional-pick.d.ts new file mode 100644 index 00000000..f90e399b --- /dev/null +++ b/website/node_modules/type-fest/source/conditional-pick.d.ts @@ -0,0 +1,44 @@ +import type {ConditionalKeys} from './conditional-keys'; + +/** +Pick keys from the shape that matches the given `Condition`. + +This is useful when you want to create a new type from a specific subset of an existing type. For example, you might want to pick all the primitive properties from a class and form a new automatically derived type. + +@example +``` +import type {Primitive, ConditionalPick} from 'type-fest'; + +class Awesome { + name: string; + successes: number; + failures: bigint; + + run() {} +} + +type PickPrimitivesFromAwesome = ConditionalPick; +//=> {name: string; successes: number; failures: bigint} +``` + +@example +``` +import type {ConditionalPick} from 'type-fest'; + +interface Example { + a: string; + b: string | number; + c: () => void; + d: {}; +} + +type StringKeysOnly = ConditionalPick; +//=> {a: string} +``` + +@category Object +*/ +export type ConditionalPick = Pick< + Base, + ConditionalKeys +>; diff --git a/website/node_modules/type-fest/source/delimiter-case.d.ts b/website/node_modules/type-fest/source/delimiter-case.d.ts new file mode 100644 index 00000000..770707ef --- /dev/null +++ b/website/node_modules/type-fest/source/delimiter-case.d.ts @@ -0,0 +1,93 @@ +import type {UpperCaseCharacters, WordSeparators} from '../source/internal'; + +/** +Unlike a simpler split, this one includes the delimiter splitted on in the resulting array literal. This is to enable splitting on, for example, upper-case characters. + +@category Template literal +*/ +export type SplitIncludingDelimiters = + Source extends '' ? [] : + Source extends `${infer FirstPart}${Delimiter}${infer SecondPart}` ? + ( + Source extends `${FirstPart}${infer UsedDelimiter}${SecondPart}` + ? UsedDelimiter extends Delimiter + ? Source extends `${infer FirstPart}${UsedDelimiter}${infer SecondPart}` + ? [...SplitIncludingDelimiters, UsedDelimiter, ...SplitIncludingDelimiters] + : never + : never + : never + ) : + [Source]; + +/** +Format a specific part of the splitted string literal that `StringArrayToDelimiterCase<>` fuses together, ensuring desired casing. + +@see StringArrayToDelimiterCase +*/ +type StringPartToDelimiterCase = + StringPart extends UsedWordSeparators ? Delimiter : + Start extends true ? Lowercase : + StringPart extends UsedUpperCaseCharacters ? `${Delimiter}${Lowercase}` : + StringPart; + +/** +Takes the result of a splitted string literal and recursively concatenates it together into the desired casing. + +It receives `UsedWordSeparators` and `UsedUpperCaseCharacters` as input to ensure it's fully encapsulated. + +@see SplitIncludingDelimiters +*/ +type StringArrayToDelimiterCase = + Parts extends [`${infer FirstPart}`, ...infer RemainingParts] + ? `${StringPartToDelimiterCase}${StringArrayToDelimiterCase}` + : Parts extends [string] + ? string + : ''; + +/** +Convert a string literal to a custom string delimiter casing. + +This can be useful when, for example, converting a camel-cased object property to an oddly cased one. + +@see KebabCase +@see SnakeCase + +@example +``` +import type {DelimiterCase} from 'type-fest'; + +// Simple + +const someVariable: DelimiterCase<'fooBar', '#'> = 'foo#bar'; + +// Advanced + +type OddlyCasedProperties = { + [K in keyof T as DelimiterCase]: T[K] +}; + +interface SomeOptions { + dryRun: boolean; + includeFile: string; + foo: number; +} + +const rawCliOptions: OddlyCasedProperties = { + 'dry#run': true, + 'include#file': 'bar.js', + foo: 123 +}; +``` + +@category Change case +@category Template literal +*/ +export type DelimiterCase = Value extends string + ? StringArrayToDelimiterCase< + SplitIncludingDelimiters, + true, + WordSeparators, + UpperCaseCharacters, + Delimiter + > + : Value; diff --git a/website/node_modules/type-fest/source/delimiter-cased-properties-deep.d.ts b/website/node_modules/type-fest/source/delimiter-cased-properties-deep.d.ts new file mode 100644 index 00000000..36b4725f --- /dev/null +++ b/website/node_modules/type-fest/source/delimiter-cased-properties-deep.d.ts @@ -0,0 +1,60 @@ +import type {DelimiterCase} from './delimiter-case'; + +/** +Convert object properties to delimiter case recursively. + +This can be useful when, for example, converting some API types from a different style. + +@see DelimiterCase +@see DelimiterCasedProperties + +@example +``` +import type {DelimiterCasedPropertiesDeep} from 'type-fest'; + +interface User { + userId: number; + userName: string; +} + +interface UserWithFriends { + userInfo: User; + userFriends: User[]; +} + +const result: DelimiterCasedPropertiesDeep = { + 'user-info': { + 'user-id': 1, + 'user-name': 'Tom', + }, + 'user-friends': [ + { + 'user-id': 2, + 'user-name': 'Jerry', + }, + { + 'user-id': 3, + 'user-name': 'Spike', + }, + ], +}; +``` + +@category Change case +@category Template literal +@category Object +*/ +export type DelimiterCasedPropertiesDeep< + Value, + Delimiter extends string, +> = Value extends Function | Date | RegExp + ? Value + : Value extends Array + ? Array> + : Value extends Set + ? Set> : { + [K in keyof Value as DelimiterCase< + K, + Delimiter + >]: DelimiterCasedPropertiesDeep; + }; diff --git a/website/node_modules/type-fest/source/delimiter-cased-properties.d.ts b/website/node_modules/type-fest/source/delimiter-cased-properties.d.ts new file mode 100644 index 00000000..03b76af5 --- /dev/null +++ b/website/node_modules/type-fest/source/delimiter-cased-properties.d.ts @@ -0,0 +1,37 @@ +import type {DelimiterCase} from './delimiter-case'; + +/** +Convert object properties to delimiter case but not recursively. + +This can be useful when, for example, converting some API types from a different style. + +@see DelimiterCase +@see DelimiterCasedPropertiesDeep + +@example +``` +import type {DelimiterCasedProperties} from 'type-fest'; + +interface User { + userId: number; + userName: string; +} + +const result: DelimiterCasedProperties = { + 'user-id': 1, + 'user-name': 'Tom', +}; +``` + +@category Change case +@category Template literal +@category Object +*/ +export type DelimiterCasedProperties< + Value, + Delimiter extends string, +> = Value extends Function + ? Value + : Value extends Array + ? Value + : {[K in keyof Value as DelimiterCase]: Value[K]}; diff --git a/website/node_modules/type-fest/source/entries.d.ts b/website/node_modules/type-fest/source/entries.d.ts new file mode 100644 index 00000000..9fe2fd32 --- /dev/null +++ b/website/node_modules/type-fest/source/entries.d.ts @@ -0,0 +1,62 @@ +import type {ArrayEntry, MapEntry, ObjectEntry, SetEntry} from './entry'; + +type ArrayEntries = Array>; +type MapEntries = Array>; +type ObjectEntries = Array>; +type SetEntries> = Array>; + +/** +Many collections have an `entries` method which returns an array of a given object's own enumerable string-keyed property [key, value] pairs. The `Entries` type will return the type of that collection's entries. + +For example the {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries|`Object`}, {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries|`Map`}, {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries|`Array`}, and {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/entries|`Set`} collections all have this method. Note that `WeakMap` and `WeakSet` do not have this method since their entries are not enumerable. + +@see `Entry` if you want to just access the type of a single entry. + +@example +``` +import type {Entries} from 'type-fest'; + +interface Example { + someKey: number; +} + +const manipulatesEntries = (examples: Entries) => examples.map(example => [ + // Does some arbitrary processing on the key (with type information available) + example[0].toUpperCase(), + + // Does some arbitrary processing on the value (with type information available) + example[1].toFixed() +]); + +const example: Example = {someKey: 1}; +const entries = Object.entries(example) as Entries; +const output = manipulatesEntries(entries); + +// Objects +const objectExample = {a: 1}; +const objectEntries: Entries = [['a', 1]]; + +// Arrays +const arrayExample = ['a', 1]; +const arrayEntries: Entries = [[0, 'a'], [1, 1]]; + +// Maps +const mapExample = new Map([['a', 1]]); +const mapEntries: Entries = [['a', 1]]; + +// Sets +const setExample = new Set(['a', 1]); +const setEntries: Entries = [['a', 'a'], [1, 1]]; +``` + +@category Object +@category Map +@category Set +@category Array +*/ +export type Entries = + BaseType extends Map ? MapEntries + : BaseType extends Set ? SetEntries + : BaseType extends readonly unknown[] ? ArrayEntries + : BaseType extends object ? ObjectEntries + : never; diff --git a/website/node_modules/type-fest/source/entry.d.ts b/website/node_modules/type-fest/source/entry.d.ts new file mode 100644 index 00000000..678b67a1 --- /dev/null +++ b/website/node_modules/type-fest/source/entry.d.ts @@ -0,0 +1,65 @@ +type MapKey = BaseType extends Map ? KeyType : never; +type MapValue = BaseType extends Map ? ValueType : never; + +export type ArrayEntry = [number, BaseType[number]]; +export type MapEntry = [MapKey, MapValue]; +export type ObjectEntry = [keyof BaseType, BaseType[keyof BaseType]]; +export type SetEntry = BaseType extends Set ? [ItemType, ItemType] : never; + +/** +Many collections have an `entries` method which returns an array of a given object's own enumerable string-keyed property [key, value] pairs. The `Entry` type will return the type of that collection's entry. + +For example the {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries|`Object`}, {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries|`Map`}, {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries|`Array`}, and {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/entries|`Set`} collections all have this method. Note that `WeakMap` and `WeakSet` do not have this method since their entries are not enumerable. + +@see `Entries` if you want to just access the type of the array of entries (which is the return of the `.entries()` method). + +@example +``` +import type {Entry} from 'type-fest'; + +interface Example { + someKey: number; +} + +const manipulatesEntry = (example: Entry) => [ + // Does some arbitrary processing on the key (with type information available) + example[0].toUpperCase(), + + // Does some arbitrary processing on the value (with type information available) + example[1].toFixed(), +]; + +const example: Example = {someKey: 1}; +const entry = Object.entries(example)[0] as Entry; +const output = manipulatesEntry(entry); + +// Objects +const objectExample = {a: 1}; +const objectEntry: Entry = ['a', 1]; + +// Arrays +const arrayExample = ['a', 1]; +const arrayEntryString: Entry = [0, 'a']; +const arrayEntryNumber: Entry = [1, 1]; + +// Maps +const mapExample = new Map([['a', 1]]); +const mapEntry: Entry = ['a', 1]; + +// Sets +const setExample = new Set(['a', 1]); +const setEntryString: Entry = ['a', 'a']; +const setEntryNumber: Entry = [1, 1]; +``` + +@category Object +@category Map +@category Array +@category Set +*/ +export type Entry = + BaseType extends Map ? MapEntry + : BaseType extends Set ? SetEntry + : BaseType extends readonly unknown[] ? ArrayEntry + : BaseType extends object ? ObjectEntry + : never; diff --git a/website/node_modules/type-fest/source/exact.d.ts b/website/node_modules/type-fest/source/exact.d.ts new file mode 100644 index 00000000..0227f3d0 --- /dev/null +++ b/website/node_modules/type-fest/source/exact.d.ts @@ -0,0 +1,73 @@ +import type {KeysOfUnion} from './internal'; + +/** +Extract the element of an array that also works for array union. + +Returns `never` if T is not an array. + +It creates a type-safe way to access the element type of `unknown` type. +*/ +type ArrayElement = T extends readonly unknown[] ? T[0] : never; + +/** +Extract the object field type if T is an object and K is a key of T, return `never` otherwise. + +It creates a type-safe way to access the member type of `unknown` type. +*/ +type ObjectValue = K extends keyof T ? T[K] : never; + +/** +Create a type from `ParameterType` and `InputType` and change keys exclusive to `InputType` to `never`. +- Generate a list of keys that exists in `InputType` but not in `ParameterType`. +- Mark these excess keys as `never`. +*/ +type ExactObject = {[Key in keyof ParameterType]: Exact>} + & Record>, never>; + +/** +Create a type that does not allow extra properties, meaning it only allows properties that are explicitly declared. + +This is useful for function type-guarding to reject arguments with excess properties. Due to the nature of TypeScript, it does not complain if excess properties are provided unless the provided value is an object literal. + +*Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/12936) if you want to have this type as a built-in in TypeScript.* + +@example +``` +type OnlyAcceptName = {name: string}; + +function onlyAcceptName(args: OnlyAcceptName) {} + +// TypeScript complains about excess properties when an object literal is provided. +onlyAcceptName({name: 'name', id: 1}); +//=> `id` is excess + +// TypeScript does not complain about excess properties when the provided value is a variable (not an object literal). +const invalidInput = {name: 'name', id: 1}; +onlyAcceptName(invalidInput); // No errors +``` + +Having `Exact` allows TypeScript to reject excess properties. + +@example +``` +import {Exact} from 'type-fest'; + +type OnlyAcceptName = {name: string}; + +function onlyAcceptNameImproved>(args: T) {} + +const invalidInput = {name: 'name', id: 1}; +onlyAcceptNameImproved(invalidInput); // Compilation error +``` + +[Read more](https://stackoverflow.com/questions/49580725/is-it-possible-to-restrict-typescript-object-to-contain-only-properties-defined) + +@category Utilities +*/ +export type Exact = + // Convert union of array to array of union: A[] & B[] => (A & B)[] + ParameterType extends unknown[] ? Array, ArrayElement>> + // In TypeScript, Array is a subtype of ReadonlyArray, so always test Array before ReadonlyArray. + : ParameterType extends readonly unknown[] ? ReadonlyArray, ArrayElement>> + : ParameterType extends object ? ExactObject + : ParameterType; diff --git a/website/node_modules/type-fest/source/except.d.ts b/website/node_modules/type-fest/source/except.d.ts new file mode 100644 index 00000000..dab21d54 --- /dev/null +++ b/website/node_modules/type-fest/source/except.d.ts @@ -0,0 +1,57 @@ +import type {IsEqual} from './internal'; + +/** +Filter out keys from an object. + +Returns `never` if `Exclude` is strictly equal to `Key`. +Returns `never` if `Key` extends `Exclude`. +Returns `Key` otherwise. + +@example +``` +type Filtered = Filter<'foo', 'foo'>; +//=> never +``` + +@example +``` +type Filtered = Filter<'bar', string>; +//=> never +``` + +@example +``` +type Filtered = Filter<'bar', 'foo'>; +//=> 'bar' +``` + +@see {Except} +*/ +type Filter = IsEqual extends true ? never : (KeyType extends ExcludeType ? never : KeyType); + +/** +Create a type from an object type without certain keys. + +This type is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). The `Omit` type does not restrict the omitted keys to be keys present on the given type, while `Except` does. The benefits of a stricter type are avoiding typos and allowing the compiler to pick up on rename refactors automatically. + +This type was proposed to the TypeScript team, which declined it, saying they prefer that libraries implement stricter versions of the built-in types ([microsoft/TypeScript#30825](https://github.com/microsoft/TypeScript/issues/30825#issuecomment-523668235)). + +@example +``` +import type {Except} from 'type-fest'; + +type Foo = { + a: number; + b: string; + c: boolean; +}; + +type FooWithoutA = Except; +//=> {b: string}; +``` + +@category Object +*/ +export type Except = { + [KeyType in keyof ObjectType as Filter]: ObjectType[KeyType]; +}; diff --git a/website/node_modules/type-fest/source/fixed-length-array.d.ts b/website/node_modules/type-fest/source/fixed-length-array.d.ts new file mode 100644 index 00000000..9152e906 --- /dev/null +++ b/website/node_modules/type-fest/source/fixed-length-array.d.ts @@ -0,0 +1,43 @@ +/** +Methods to exclude. +*/ +type ArrayLengthMutationKeys = 'splice' | 'push' | 'pop' | 'shift' | 'unshift'; + +/** +Create a type that represents an array of the given type and length. The array's length and the `Array` prototype methods that manipulate its length are excluded in the resulting type. + +Please participate in [this issue](https://github.com/microsoft/TypeScript/issues/26223) if you want to have a similiar type built into TypeScript. + +Use-cases: +- Declaring fixed-length tuples or arrays with a large number of items. +- Creating a range union (for example, `0 | 1 | 2 | 3 | 4` from the keys of such a type) without having to resort to recursive types. +- Creating an array of coordinates with a static length, for example, length of 3 for a 3D vector. + +Note: This type does not prevent out-of-bounds access. Prefer `ReadonlyTuple` unless you need mutability. + +@example +``` +import type {FixedLengthArray} from 'type-fest'; + +type FencingTeam = FixedLengthArray; + +const guestFencingTeam: FencingTeam = ['Josh', 'Michael', 'Robert']; + +const homeFencingTeam: FencingTeam = ['George', 'John']; +//=> error TS2322: Type string[] is not assignable to type 'FencingTeam' + +guestFencingTeam.push('Sam'); +//=> error TS2339: Property 'push' does not exist on type 'FencingTeam' +``` + +@category Array +@see ReadonlyTuple +*/ +export type FixedLengthArray = Pick< + ArrayPrototype, + Exclude +> & { + [index: number]: Element; + [Symbol.iterator]: () => IterableIterator; + readonly length: Length; +}; diff --git a/website/node_modules/type-fest/source/get.d.ts b/website/node_modules/type-fest/source/get.d.ts new file mode 100644 index 00000000..a95394c0 --- /dev/null +++ b/website/node_modules/type-fest/source/get.d.ts @@ -0,0 +1,184 @@ +import type {StringDigit} from '../source/internal'; +import type {Split} from './split'; +import type {StringKeyOf} from './string-key-of'; + +type GetOptions = { + strict?: boolean; +}; + +/** +Like the `Get` type but receives an array of strings as a path parameter. +*/ +type GetWithPath = + Keys extends [] + ? BaseType + : Keys extends readonly [infer Head, ...infer Tail] + ? GetWithPath< + PropertyOf, Options>, + Extract, + Options + > + : never; + +/** +Adds `undefined` to `Type` if `strict` is enabled. +*/ +type Strictify = + Options['strict'] extends true ? Type | undefined : Type; + +/** +If `Options['strict']` is `true`, includes `undefined` in the returned type when accessing properties on `Record`. + +Known limitations: +- Does not include `undefined` in the type on object types with an index signature (for example, `{a: string; [key: string]: string}`). +*/ +type StrictPropertyOf = + Record extends BaseType + ? string extends keyof BaseType + ? Strictify // Record + : BaseType[Key] // Record<'a' | 'b', any> (Records with a string union as keys have required properties) + : BaseType[Key]; + +/** +Splits a dot-prop style path into a tuple comprised of the properties in the path. Handles square-bracket notation. + +@example +``` +ToPath<'foo.bar.baz'> +//=> ['foo', 'bar', 'baz'] + +ToPath<'foo[0].bar.baz'> +//=> ['foo', '0', 'bar', 'baz'] +``` +*/ +type ToPath = Split, '.'>; + +/** +Replaces square-bracketed dot notation with dots, for example, `foo[0].bar` -> `foo.0.bar`. +*/ +type FixPathSquareBrackets = + Path extends `[${infer Head}]${infer Tail}` + ? Tail extends `[${string}` + ? `${Head}.${FixPathSquareBrackets}` + : `${Head}${FixPathSquareBrackets}` + : Path extends `${infer Head}[${infer Middle}]${infer Tail}` + ? `${Head}.${FixPathSquareBrackets<`[${Middle}]${Tail}`>}` + : Path; + +/** +Returns true if `LongString` is made up out of `Substring` repeated 0 or more times. + +@example +``` +ConsistsOnlyOf<'aaa', 'a'> //=> true +ConsistsOnlyOf<'ababab', 'ab'> //=> true +ConsistsOnlyOf<'aBa', 'a'> //=> false +ConsistsOnlyOf<'', 'a'> //=> true +``` +*/ +type ConsistsOnlyOf = + LongString extends '' + ? true + : LongString extends `${Substring}${infer Tail}` + ? ConsistsOnlyOf + : false; + +/** +Convert a type which may have number keys to one with string keys, making it possible to index using strings retrieved from template types. + +@example +``` +type WithNumbers = {foo: string; 0: boolean}; +type WithStrings = WithStringKeys; + +type WithNumbersKeys = keyof WithNumbers; +//=> 'foo' | 0 +type WithStringsKeys = keyof WithStrings; +//=> 'foo' | '0' +``` +*/ +type WithStringKeys = { + [Key in StringKeyOf]: UncheckedIndex +}; + +/** +Perform a `T[U]` operation if `T` supports indexing. +*/ +type UncheckedIndex = [T] extends [Record] ? T[U] : never; + +/** +Get a property of an object or array. Works when indexing arrays using number-literal-strings, for example, `PropertyOf = number`, and when indexing objects with number keys. + +Note: +- Returns `unknown` if `Key` is not a property of `BaseType`, since TypeScript uses structural typing, and it cannot be guaranteed that extra properties unknown to the type system will exist at runtime. +- Returns `undefined` from nullish values, to match the behaviour of most deep-key libraries like `lodash`, `dot-prop`, etc. +*/ +type PropertyOf = + BaseType extends null | undefined + ? undefined + : Key extends keyof BaseType + ? StrictPropertyOf + : BaseType extends [] | [unknown, ...unknown[]] + ? unknown // It's a tuple, but `Key` did not extend `keyof BaseType`. So the index is out of bounds. + : BaseType extends { + [n: number]: infer Item; + length: number; // Note: This is needed to avoid being too lax with records types using number keys like `{0: string; 1: boolean}`. + } + ? ( + ConsistsOnlyOf extends true + ? Strictify + : unknown + ) + : Key extends keyof WithStringKeys + ? StrictPropertyOf, Key, Options> + : unknown; + +// This works by first splitting the path based on `.` and `[...]` characters into a tuple of string keys. Then it recursively uses the head key to get the next property of the current object, until there are no keys left. Number keys extract the item type from arrays, or are converted to strings to extract types from tuples and dictionaries with number keys. +/** +Get a deeply-nested property from an object using a key path, like Lodash's `.get()` function. + +Use-case: Retrieve a property from deep inside an API response or some other complex object. + +@example +``` +import type {Get} from 'type-fest'; +import * as lodash from 'lodash'; + +const get = (object: BaseType, path: Path): Get => + lodash.get(object, path); + +interface ApiResponse { + hits: { + hits: Array<{ + _id: string + _source: { + name: Array<{ + given: string[] + family: string + }> + birthDate: string + } + }> + } +} + +const getName = (apiResponse: ApiResponse) => + get(apiResponse, 'hits.hits[0]._source.name'); + //=> Array<{given: string[]; family: string}> + +// Path also supports a readonly array of strings +const getNameWithPathArray = (apiResponse: ApiResponse) => + get(apiResponse, ['hits','hits', '0', '_source', 'name'] as const); + //=> Array<{given: string[]; family: string}> + +// Strict mode: +Get //=> string | undefined +Get, 'foo', {strict: true}> // => string | undefined +``` + +@category Object +@category Array +@category Template literal +*/ +export type Get = + GetWithPath : Path, Options>; diff --git a/website/node_modules/type-fest/source/has-optional-keys.d.ts b/website/node_modules/type-fest/source/has-optional-keys.d.ts new file mode 100644 index 00000000..597e8aaa --- /dev/null +++ b/website/node_modules/type-fest/source/has-optional-keys.d.ts @@ -0,0 +1,21 @@ +import {OptionalKeysOf} from './optional-keys-of'; + +/** +Creates a type that represents `true` or `false` depending on whether the given type has any optional fields. + +This is useful when you want to create an API whose behavior depends on the presence or absence of optional fields. + +@example +``` +import type {HasOptionalKeys, OptionalKeysOf} from 'type-fest'; + +type UpdateService = { + removeField: HasOptionalKeys extends true + ? (field: OptionalKeysOf) => Promise + : never +} +``` + +@category Utilities +*/ +export type HasOptionalKeys = OptionalKeysOf extends never ? false : true; diff --git a/website/node_modules/type-fest/source/has-required-keys.d.ts b/website/node_modules/type-fest/source/has-required-keys.d.ts new file mode 100644 index 00000000..ec9daf36 --- /dev/null +++ b/website/node_modules/type-fest/source/has-required-keys.d.ts @@ -0,0 +1,59 @@ +import {RequiredKeysOf} from './required-keys-of'; + +/** +Creates a type that represents `true` or `false` depending on whether the given type has any required fields. + +This is useful when you want to create an API whose behavior depends on the presence or absence of required fields. + +@example +``` +import type {HasRequiredKeys} from 'type-fest'; + +type GeneratorOptions