diff --git a/.github/workflows/cargo-bench.yml b/.github/workflows/cargo-bench.yml index 29aa2002f..4847c27c5 100644 --- a/.github/workflows/cargo-bench.yml +++ b/.github/workflows/cargo-bench.yml @@ -26,12 +26,8 @@ name: cargo bench jobs: cargo-bench: name: cargo bench - runs-on: - - runs-on=${{ github.run_id }} - - runner=32cpu-linux-x64 - - extras=s3-cache + runs-on: ubuntu-latest steps: - - uses: runs-on/action@v1 - uses: actions/checkout@v4 - name: Use correct Rust toolchain shell: bash diff --git a/.github/workflows/cargo-test.yml b/.github/workflows/cargo-test.yml index c62d476f9..70826a539 100644 --- a/.github/workflows/cargo-test.yml +++ b/.github/workflows/cargo-test.yml @@ -89,7 +89,6 @@ jobs: env: KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}} RUST_BACKTRACE: full - RUST_MIN_STACK: 10485760000 - name: Commit differences if: steps.path-changes.outputs.outside-kcl-samples == 'false' && steps.cargo-test-kcl-samples.outcome == 'failure' shell: bash @@ -122,7 +121,6 @@ jobs: # Configure nextest when it's run by insta (via just). NEXTEST_PROFILE: ci RUST_BACKTRACE: full - RUST_MIN_STACK: 10485760000 - name: cargo test if: steps.path-changes.outputs.outside-kcl-samples == 'true' shell: bash @@ -131,7 +129,6 @@ jobs: cargo llvm-cov nextest --workspace --features artifact-graph --lcov --output-path lcov.info --retries=2 --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log env: KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}} - RUST_MIN_STACK: 10485760000 - name: Upload to codecov.io if: steps.path-changes.outputs.outside-kcl-samples == 'true' uses: codecov/codecov-action@v5 diff --git a/docs/kcl/clone.md b/docs/kcl/clone.md index 38fcb6376..c8273f268 100644 --- a/docs/kcl/clone.md +++ b/docs/kcl/clone.md @@ -6,7 +6,7 @@ layout: manual Clone a sketch or solid. -This works essentially like a copy-paste operation. +This works essentially like a copy-paste operation. It creates a perfect replica at that point in time that you can manipulate individually afterwards. This doesn't really have much utility unless you need the equivalent of a double instance pattern with zero transformations. diff --git a/docs/kcl/startSketchOn.md b/docs/kcl/startSketchOn.md index 9d9686ddd..39f9ec281 100644 --- a/docs/kcl/startSketchOn.md +++ b/docs/kcl/startSketchOn.md @@ -6,7 +6,7 @@ layout: manual Start a new 2-dimensional sketch on a specific plane or face. -## Sketch on Face Behavior +### Sketch on Face Behavior There are some important behaviors to understand when sketching on a face: diff --git a/docs/kcl/std.json b/docs/kcl/std.json index 71576b366..2ee6d6603 100644 --- a/docs/kcl/std.json +++ b/docs/kcl/std.json @@ -73770,7 +73770,7 @@ { "name": "clone", "summary": "Clone a sketch or solid.", - "description": "This works essentially like a copy-paste operation.\n\nThis doesn't really have much utility unless you need the equivalent of a double instance pattern with zero transformations.\n\nReally only use this function if YOU ARE SURE you need it. In most cases you do not need clone and using a pattern with `instance = 2` is more appropriate.", + "description": "This works essentially like a copy-paste operation. It creates a perfect replica at that point in time that you can manipulate individually afterwards.\n\nThis doesn't really have much utility unless you need the equivalent of a double instance pattern with zero transformations.\n\nReally only use this function if YOU ARE SURE you need it. In most cases you do not need clone and using a pattern with `instance = 2` is more appropriate.", "tags": [], "keywordArguments": true, "args": [ @@ -292719,7 +292719,7 @@ { "name": "startSketchOn", "summary": "Start a new 2-dimensional sketch on a specific plane or face.", - "description": "## Sketch on Face Behavior\n\nThere are some important behaviors to understand when sketching on a face:\n\nThe resulting sketch will _include_ the face and thus Solid that was sketched on. So say you were to export the resulting Sketch / Solid from a sketch on a face, you would get both the artifact of the sketch on the face and the parent face / Solid itself.\n\nThis is important to understand because if you were to then sketch on the resulting Solid, it would again include the face and parent Solid that was sketched on. This could go on indefinitely.\n\nThe point is if you want to export the result of a sketch on a face, you only need to export the final Solid that was created from the sketch on the face, since it will include all the parent faces and Solids.", + "description": "### Sketch on Face Behavior\n\nThere are some important behaviors to understand when sketching on a face:\n\nThe resulting sketch will _include_ the face and thus Solid that was sketched on. So say you were to export the resulting Sketch / Solid from a sketch on a face, you would get both the artifact of the sketch on the face and the parent face / Solid itself.\n\nThis is important to understand because if you were to then sketch on the resulting Solid, it would again include the face and parent Solid that was sketched on. This could go on indefinitely.\n\nThe point is if you want to export the result of a sketch on a face, you only need to export the final Solid that was created from the sketch on the face, since it will include all the parent faces and Solids.", "tags": [], "keywordArguments": true, "args": [ diff --git a/e2e/playwright/point-click-assemblies.spec.ts b/e2e/playwright/point-click-assemblies.spec.ts index 970e8259f..ed1c572dc 100644 --- a/e2e/playwright/point-click-assemblies.spec.ts +++ b/e2e/playwright/point-click-assemblies.spec.ts @@ -694,7 +694,7 @@ washer await toolbar.openPane('code') await editor.expectEditor.toContain( ` - washer + washer |> rotate(roll = 90, pitch = 0, yaw = 0) clone001 = clone(washer) `, diff --git a/e2e/playwright/point-click.spec.ts b/e2e/playwright/point-click.spec.ts index bb1ad62f6..c178ba2c4 100644 --- a/e2e/playwright/point-click.spec.ts +++ b/e2e/playwright/point-click.spec.ts @@ -3406,7 +3406,7 @@ profile001 = startProfile(sketch001, at = [-20, 20]) radius = 500 ) sketch002 = startSketchOn(XZ) - |> startProfile(at = [0, 0]) + |> startProfile(at = [0, 0]) |> xLine(length = -2000) sweep001 = sweep(sketch001, path = sketch002) ` diff --git a/example.bing b/example.bing deleted file mode 100644 index 63e3c44d4..000000000 --- a/example.bing +++ /dev/null @@ -1,14 +0,0 @@ -const totalHeight = 5 -const xVal = 3 + 2 - -sketch MySketch(myVar = 5) { - start(0, 0) - lineTo(5, myVar) - horzLineTo(10) - vertLineTo(0) - close() -} - -const mySketch = MySketch(totalHeight) - -print(`print worked: `, xVal) diff --git a/package-lock.json b/package-lock.json index 1d8bbae75..bf254901d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,7 @@ "vscode-languageserver-protocol": "^3.17.5", "vscode-uri": "^3.1.0", "web-vitals": "^3.5.2", + "why-is-node-running": "^3.2.2", "win-ca": "^3.5.1", "xstate": "^5.19.2", "yargs": "^17.7.2" @@ -105,7 +106,7 @@ "@types/wicg-file-system-access": "^2023.10.6", "@types/ws": "^8.18.1", "@vitejs/plugin-react": "^4.4.1", - "@vitest/web-worker": "^1.5.0", + "@vitest/web-worker": "^3.1.2", "@xstate/cli": "^0.5.17", "autoprefixer": "^10.4.21", "dpdm": "^3.14.0", @@ -121,7 +122,7 @@ "eslint-plugin-react-perf": "^3.3.3", "eslint-plugin-suggest-no-throw": "^1.0.0", "eslint-plugin-testing-library": "^7.1.1", - "happy-dom": "^16.3.0", + "happy-dom": "^17.4.4", "http-server": "^14.1.1", "husky": "^9.1.7", "kill-port": "^2.0.1", @@ -140,7 +141,7 @@ "vite-plugin-package-version": "^1.1.0", "vite-plugin-top-level-await": "^1.5.0", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^1.6.1", + "vitest": "^3.1.2", "vitest-webgl-canvas-mock": "^1.1.0", "ws": "^8.18.1" } @@ -5987,28 +5988,29 @@ } }, "node_modules/@vitest/expect": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", - "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.2.tgz", + "integrity": "sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "chai": "^4.3.10" + "@vitest/spy": "3.1.2", + "@vitest/utils": "3.1.2", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.2.tgz", + "integrity": "sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", + "@vitest/spy": "3.1.2", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -6028,33 +6030,10 @@ } } }, - "node_modules/@vitest/mocker/node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker/node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.2.tgz", + "integrity": "sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==", "dev": true, "license": "MIT", "dependencies": { @@ -6065,177 +6044,76 @@ } }, "node_modules/@vitest/runner": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", - "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.2.tgz", + "integrity": "sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "1.6.1", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" + "@vitest/utils": "3.1.2", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@vitest/snapshot": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", - "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.2.tgz", + "integrity": "sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==", "dev": true, "license": "MIT", "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "3.1.2", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/snapshot/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@vitest/snapshot/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@vitest/snapshot/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/@vitest/spy": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", - "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.2.tgz", + "integrity": "sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^2.2.0" + "tinyspy": "^3.0.2" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", - "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.2.tgz", + "integrity": "sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==", "dev": true, "license": "MIT", "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "3.1.2", + "loupe": "^3.1.3", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/utils/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@vitest/utils/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@vitest/utils/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/@vitest/web-worker": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/web-worker/-/web-worker-1.6.1.tgz", - "integrity": "sha512-T3zLS/sga4z8ZqgA+iDKyiKOcL7OGG3EJ8xqXEEDuvtQELxCITehwPbDe7cGKQ+YspFsLqdNC9td30uQ1uStFg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vitest/web-worker/-/web-worker-3.1.2.tgz", + "integrity": "sha512-4k+YlnfM0OPRv3UNURKFe98FYc1fzVsiZKVIhcwln/fwxHdagXirRPv56wu7AjVirXIhJQp3IjoCimQKvEviug==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.3.4" + "debug": "^4.4.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "^1.0.0" + "vitest": "3.1.2" } }, "node_modules/@vscode/test-electron": { @@ -7785,13 +7663,13 @@ } }, "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": ">=12" } }, "node_modules/ast-types": { @@ -8611,22 +8489,20 @@ "license": "CC-BY-4.0" }, "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", "dev": true, "license": "MIT", "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=12" } }, "node_modules/chalk": { @@ -8654,16 +8530,13 @@ "license": "MIT" }, "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, "engines": { - "node": "*" + "node": ">= 16" } }, "node_modules/cheerio": { @@ -8985,13 +8858,6 @@ "dev": true, "license": "MIT" }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, - "license": "MIT" - }, "node_modules/config-file-ts": { "version": "0.2.8-rc1", "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", @@ -9432,14 +9298,11 @@ } }, "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, "engines": { "node": ">=6" } @@ -10369,9 +10232,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -11433,6 +11296,21 @@ "pend": "~1.2.0" } }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -11947,16 +11825,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -12274,9 +12142,9 @@ "license": "MIT" }, "node_modules/happy-dom": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-16.8.1.tgz", - "integrity": "sha512-n0QrmT9lD81rbpKsyhnlz3DgnMZlaOkJPpgi746doA+HvaMC79bdWkwjrNnGJRvDrWTI8iOcJiVTJ5CdT/AZRw==", + "version": "17.4.4", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.4.4.tgz", + "integrity": "sha512-/Pb0ctk3HTZ5xEL3BZ0hK1AqDSAUuRQitOmROPHhfUYEWpmTImwfD8vFDGADmMAX0JYgbcgxWoLFKtsWhcpuVA==", "dev": true, "license": "MIT", "dependencies": { @@ -12592,16 +12460,6 @@ "node": ">= 14" } }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" - } - }, "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -14202,23 +14060,6 @@ "node": ">=4" } }, - "node_modules/local-pkg": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", - "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mlly": "^1.7.3", - "pkg-types": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -14460,14 +14301,11 @@ } }, "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } + "license": "MIT" }, "node_modules/lowercase-keys": { "version": "2.0.0", @@ -14713,13 +14551,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "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/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -14994,26 +14825,6 @@ "license": "MIT", "optional": true }, - "node_modules/mlly": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", - "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.14.0", - "pathe": "^2.0.1", - "pkg-types": "^1.3.0", - "ufo": "^1.5.4" - } - }, - "node_modules/mlly/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, "node_modules/mocha": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", @@ -16240,20 +16051,20 @@ } }, "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": ">= 14.16" } }, "node_modules/pe-library": { @@ -16341,25 +16152,6 @@ "node": ">=12.13.0" } }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" - } - }, - "node_modules/pkg-types/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, "node_modules/playwright": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", @@ -18772,19 +18564,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -18811,26 +18590,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-literal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", - "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, "node_modules/strip-outer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", @@ -19302,14 +19061,44 @@ "dev": true, "license": "MIT" }, - "node_modules/tinypool": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", - "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinypool": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" } }, "node_modules/tinyrainbow": { @@ -19323,9 +19112,9 @@ } }, "node_modules/tinyspy": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, "license": "MIT", "engines": { @@ -19582,16 +19371,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", @@ -19778,13 +19557,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ufo": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", - "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", - "dev": true, - "license": "MIT" - }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -20209,23 +19981,23 @@ } }, "node_modules/vite-node": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz", - "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.2.tgz", + "integrity": "sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "vite": "^5.0.0" + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0" }, "bin": { "vite-node": "vite-node.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -20306,47 +20078,49 @@ } }, "node_modules/vitest": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", - "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.2.tgz", + "integrity": "sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "1.6.1", - "@vitest/runner": "1.6.1", - "@vitest/snapshot": "1.6.1", - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.3", - "vite": "^5.0.0", - "vite-node": "1.6.1", - "why-is-node-running": "^2.2.2" + "@vitest/expect": "3.1.2", + "@vitest/mocker": "3.1.2", + "@vitest/pretty-format": "^3.1.2", + "@vitest/runner": "3.1.2", + "@vitest/snapshot": "3.1.2", + "@vitest/spy": "3.1.2", + "@vitest/utils": "3.1.2", + "chai": "^5.2.0", + "debug": "^4.4.0", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.13", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.1.2", + "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.1", - "@vitest/ui": "1.6.1", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.1.2", + "@vitest/ui": "3.1.2", "happy-dom": "*", "jsdom": "*" }, @@ -20354,6 +20128,9 @@ "@edge-runtime/vm": { "optional": true }, + "@types/debug": { + "optional": true + }, "@types/node": { "optional": true }, @@ -20382,125 +20159,21 @@ "parse-color": "^1.0.0" } }, - "node_modules/vitest/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "node_modules/vitest/node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" }, "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/vitest/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, "node_modules/vscode-jsonrpc": { @@ -20799,20 +20472,15 @@ } }, "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.2.tgz", + "integrity": "sha512-NKUzAelcoCXhXL4dJzKIwXeR8iEVqsA0Lq6Vnd0UXvgaKbzVo4ZTHROF2Jidrv+SgxOQ03fMinnNhzZATxOD3A==", "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, "bin": { "why-is-node-running": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=20.11" } }, "node_modules/win-ca": { @@ -21211,193 +20879,6 @@ "vitest": "^3.1.1" } }, - "packages/codemirror-lang-kcl/node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/codemirror-lang-kcl/node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.1.1", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/codemirror-lang-kcl/node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.1.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/codemirror-lang-kcl/node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/codemirror-lang-kcl/node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.1.1", - "loupe": "^3.1.3", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/codemirror-lang-kcl/node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "packages/codemirror-lang-kcl/node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/codemirror-lang-kcl/node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "packages/codemirror-lang-kcl/node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "packages/codemirror-lang-kcl/node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", - "dev": true, - "license": "MIT" - }, - "packages/codemirror-lang-kcl/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "packages/codemirror-lang-kcl/node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "packages/codemirror-lang-kcl/node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "packages/codemirror-lang-kcl/node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "packages/codemirror-lang-kcl/node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "packages/codemirror-lang-kcl/node_modules/vite-tsconfig-paths": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", @@ -21418,76 +20899,6 @@ } } }, - "packages/codemirror-lang-kcl/node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", - "chai": "^5.2.0", - "debug": "^4.4.0", - "expect-type": "^1.2.0", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "std-env": "^3.8.1", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinypool": "^1.0.2", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, "packages/codemirror-lsp-client": { "name": "@kittycad/codemirror-lsp-client", "version": "1.0.0", diff --git a/package.json b/package.json index 0b91b233f..19bd83d05 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "vscode-languageserver-protocol": "^3.17.5", "vscode-uri": "^3.1.0", "web-vitals": "^3.5.2", + "why-is-node-running": "^3.2.2", "win-ca": "^3.5.1", "xstate": "^5.19.2", "yargs": "^17.7.2" @@ -150,7 +151,11 @@ "test:unit:kcl-samples:local": "npm run simpleserver:bg && npm run test:unit:kcl-samples; kill-port 3000" }, "browserslist": { - "production": [">0.2%", "not dead", "not op_mini all"], + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], "development": [ "last 1 chrome version", "last 1 firefox version", @@ -189,7 +194,7 @@ "@types/wicg-file-system-access": "^2023.10.6", "@types/ws": "^8.18.1", "@vitejs/plugin-react": "^4.4.1", - "@vitest/web-worker": "^1.5.0", + "@vitest/web-worker": "^3.1.2", "@xstate/cli": "^0.5.17", "autoprefixer": "^10.4.21", "dpdm": "^3.14.0", @@ -205,7 +210,7 @@ "eslint-plugin-react-perf": "^3.3.3", "eslint-plugin-suggest-no-throw": "^1.0.0", "eslint-plugin-testing-library": "^7.1.1", - "happy-dom": "^16.3.0", + "happy-dom": "^17.4.4", "http-server": "^14.1.1", "husky": "^9.1.7", "kill-port": "^2.0.1", @@ -224,7 +229,7 @@ "vite-plugin-package-version": "^1.1.0", "vite-plugin-top-level-await": "^1.5.0", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^1.6.1", + "vitest": "^3.1.2", "vitest-webgl-canvas-mock": "^1.1.0", "ws": "^8.18.1" } diff --git a/rust/Cargo.lock b/rust/Cargo.lock index b4b22cd63..0284e7d50 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -758,7 +758,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.10", + "parking_lot_core", ] [[package]] @@ -772,7 +772,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.10", + "parking_lot_core", ] [[package]] @@ -847,7 +847,7 @@ dependencies = [ "backtrace", "lazy_static", "mintex", - "parking_lot 0.12.3", + "parking_lot", "rustc-hash 1.1.0", "serde", "serde_json", @@ -1091,6 +1091,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -1882,6 +1895,7 @@ dependencies = [ "bson", "chrono", "clap", + "console_error_panic_hook", "convert_case", "criterion", "dashmap 6.1.0", @@ -1890,6 +1904,7 @@ dependencies = [ "fnv", "form_urlencoded", "futures", + "futures-lite", "git_rev", "gltf-json", "handlebars", @@ -1934,10 +1949,10 @@ dependencies = [ "validator", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "web-sys", "web-time", "winnow 0.6.24", + "zduny-wasm-timer", "zip", ] @@ -2475,15 +2490,10 @@ dependencies = [ ] [[package]] -name = "parking_lot" -version = "0.11.2" +name = "parking" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" @@ -2492,21 +2502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -2517,7 +2513,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.10", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -3057,15 +3053,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.10" @@ -4013,7 +4000,7 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot 0.12.3", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -4610,21 +4597,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "web-sys" version = "0.3.77" @@ -4986,6 +4958,21 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zduny-wasm-timer" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52bd30296679f51dce4a4da2a5050d9401d09866d89b89d860da37bc3ec08df" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 2b676f451..f972d0794 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -31,6 +31,7 @@ async-trait = "0.1.88" anyhow = { version = "1" } bson = { version = "2.13.0", features = ["uuid-1", "chrono"] } clap = { version = "4.5.36", features = ["derive"] } +console_error_panic_hook = "0.1.7" dashmap = { version = "6.1.0" } http = "1" indexmap = "2.7.0" diff --git a/rust/justfile b/rust/justfile index 1b4628894..2cfb462f6 100644 --- a/rust/justfile +++ b/rust/justfile @@ -1,6 +1,6 @@ cnr := "cargo nextest run" cita := "cargo insta test --accept" -kcl_lib_flags := "-p kcl-lib --feature artifact-graph --no-fail-fast" +kcl_lib_flags := "-p kcl-lib --features artifact-graph" # Run the same lint checks we run in CI. lint: diff --git a/rust/kcl-lib/Cargo.toml b/rust/kcl-lib/Cargo.toml index 7b10a60e9..c10590776 100644 --- a/rust/kcl-lib/Cargo.toml +++ b/rust/kcl-lib/Cargo.toml @@ -89,13 +89,15 @@ winnow = "=0.6.24" zip = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] +console_error_panic_hook = { workspace = true } +futures-lite = "2.6.0" instant = { version = "0.1.13", features = ["wasm-bindgen", "inaccurate"] } js-sys = { version = "0.3.72" } tokio = { workspace = true, features = ["sync", "time"] } tower-lsp = { workspace = true, features = ["runtime-agnostic"] } wasm-bindgen = "0.2.99" wasm-bindgen-futures = "0.4.49" -wasm-timer = "0.2.5" +wasm-timer = { package = "zduny-wasm-timer", version = "0.2.5" } web-sys = { version = "0.3.76", features = ["console"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/rust/kcl-lib/src/engine/async_tasks/tasks_wasm.rs b/rust/kcl-lib/src/engine/async_tasks/tasks_wasm.rs index 433b08818..60e647e58 100644 --- a/rust/kcl-lib/src/engine/async_tasks/tasks_wasm.rs +++ b/rust/kcl-lib/src/engine/async_tasks/tasks_wasm.rs @@ -1,89 +1,116 @@ -//! This module contains the `AsyncTasks` struct, which is used to manage a set of asynchronous +//! This module contains the wasm-specific `AsyncTasks` struct, which is used to manage a set of asynchronous //! tasks. -use std::{ops::AddAssign, sync::Arc}; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; -use tokio::sync::RwLock; +use tokio::sync::{mpsc, Notify}; use crate::errors::KclError; #[derive(Debug, Clone)] pub struct AsyncTasks { - pub sender: Arc>>>, - pub receiver: Arc>>>, - pub sent: Arc>, + // Results arrive here (unbounded = never blocks the producer) + tx: mpsc::UnboundedSender>, + rx: Arc>>>, + + // How many tasks we started since last clear() + spawned: Arc, + + // Used to wake `join_all()` as soon as a task finishes. + notifier: Arc, } -impl AsyncTasks { - pub fn new() -> Self { - let (results_tx, results_rx) = tokio::sync::mpsc::channel(1); - Self { - sender: Arc::new(RwLock::new(results_tx)), - receiver: Arc::new(RwLock::new(results_rx)), - sent: Arc::new(RwLock::new(0)), - } - } - - pub async fn spawn(&mut self, task: F) - where - F: std::future::Future>, - F: Send + 'static, - { - // Add one to the sent counter. - self.sent.write().await.add_assign(1); - - // Spawn the task and send the result to the channel. - let sender_clone = self.sender.clone(); - wasm_bindgen_futures::spawn_local(async move { - let result = task.await; - let sender = sender_clone.read().await; - if let Err(_) = sender.send(result).await { - web_sys::console::error_1(&"Failed to send result".into()); - } - }); - } - - // Wait for all tasks to finish. - // Return an error if any of them failed. - pub async fn join_all(&mut self) -> anyhow::Result<(), KclError> { - if *self.sent.read().await == 0 { - return Ok(()); - } - - let mut results = Vec::new(); - let mut receiver = self.receiver.write().await; - - // Wait for all tasks to finish. - while let Some(result) = receiver.recv().await { - results.push(result); - - // Check if all tasks have finished. - if results.len() == *self.sent.read().await { - break; - } - } - - // Check if any of the tasks failed. - for result in results { - result?; - } - - Ok(()) - } - - pub async fn clear(&mut self) { - // Clear the sent counter. - *self.sent.write().await = 0; - - // Clear the channel. - let (results_tx, results_rx) = tokio::sync::mpsc::channel(1); - *self.sender.write().await = results_tx; - *self.receiver.write().await = results_rx; - } -} +// Safety: single-threaded wasm ⇒ these are sound. +unsafe impl Send for AsyncTasks {} +unsafe impl Sync for AsyncTasks {} impl Default for AsyncTasks { fn default() -> Self { Self::new() } } + +impl AsyncTasks { + pub fn new() -> Self { + console_error_panic_hook::set_once(); + + let (tx, rx) = mpsc::unbounded_channel(); + Self { + tx, + rx: Arc::new(tokio::sync::Mutex::new(rx)), + spawned: Arc::new(AtomicUsize::new(0)), + notifier: Arc::new(Notify::new()), + } + } + + pub async fn spawn(&mut self, fut: F) + where + F: std::future::Future> + Send + 'static, + { + self.spawned.fetch_add(1, Ordering::Relaxed); + let tx = self.tx.clone(); + let notify = self.notifier.clone(); + + wasm_bindgen_futures::spawn_local(async move { + console_error_panic_hook::set_once(); + let _ = tx.send(fut.await); // ignore if receiver disappeared + notify.notify_one(); // wake any join_all waiter + }); + } + + // Wait for all tasks to finish. + // Return an error if any of them failed. + pub async fn join_all(&mut self) -> anyhow::Result<(), KclError> { + let total = self.spawned.load(Ordering::Acquire); + if total == 0 { + return Ok(()); + } + + let mut done = 0; + while done < total { + // 1) Drain whatever is already in the channel + { + let mut rx = self.rx.lock().await; + while let Ok(res) = rx.try_recv() { + done += 1; + res?; // propagate first Err + } + } + if done >= total { + break; + } + // Yield to the event loop so that we don't block the UI thread. + // No seriously WE DO NOT WANT TO PAUSE THE WHOLE APP ON THE JS SIDE. + futures_lite::future::yield_now().await; + // Check again before waiting to avoid missing notifications + { + let mut rx = self.rx.lock().await; + while let Ok(res) = rx.try_recv() { + done += 1; + res?; // propagate first Err + if done >= total { + break; + } + } + } + // Only wait for notification if we still need more tasks to complete + if done < total { + // 2) Nothing ready yet → wait for a notifier poke + self.notifier.notified().await; + } + } + + Ok(()) + } + + pub async fn clear(&mut self) { + self.spawned.store(0, Ordering::Release); + + // Drain channel so old results don’t confuse the next join_all. + let mut rx = self.rx.lock().await; + while rx.try_recv().is_ok() {} + } +} diff --git a/rust/kcl-lib/src/engine/mod.rs b/rust/kcl-lib/src/engine/mod.rs index f03085372..921d2e4b4 100644 --- a/rust/kcl-lib/src/engine/mod.rs +++ b/rust/kcl-lib/src/engine/mod.rs @@ -229,18 +229,20 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static { while current_time.elapsed().as_secs() < 60 { let responses = self.responses().read().await.clone(); let Some(resp) = responses.get(&id) else { - // Sleep for a little so we don't hog the CPU. + // Yield to the event loop so that we don’t block the UI thread. // No seriously WE DO NOT WANT TO PAUSE THE WHOLE APP ON THE JS SIDE. - let duration = instant::Duration::from_millis(100); #[cfg(target_arch = "wasm32")] - wasm_timer::Delay::new(duration).await.map_err(|err| { - KclError::Internal(KclErrorDetails { - message: format!("Failed to sleep: {:?}", err), - source_ranges: vec![source_range], - }) - })?; + { + let duration = instant::Duration::from_millis(1); + wasm_timer::Delay::new(duration).await.map_err(|err| { + KclError::Internal(KclErrorDetails { + message: format!("Failed to sleep: {:?}", err), + source_ranges: vec![source_range], + }) + })?; + } #[cfg(not(target_arch = "wasm32"))] - tokio::time::sleep(duration).await; + tokio::task::yield_now().await; continue; }; diff --git a/rust/kcl-lib/src/simulation_tests.rs b/rust/kcl-lib/src/simulation_tests.rs index eb749de86..5f7eac534 100644 --- a/rust/kcl-lib/src/simulation_tests.rs +++ b/rust/kcl-lib/src/simulation_tests.rs @@ -2630,3 +2630,25 @@ mod multiple_foreign_imports_all_render { super::execute(TEST_NAME, true).await } } +mod import_mesh_clone { + const TEST_NAME: &str = "import_mesh_clone"; + + /// Test parsing KCL. + #[test] + fn parse() { + super::parse(TEST_NAME) + } + + /// Test that parsing and unparsing KCL produces the original KCL input. + #[tokio::test(flavor = "multi_thread")] + async fn unparse() { + super::unparse(TEST_NAME).await + } + + /// Test that KCL is executed correctly. + #[tokio::test(flavor = "multi_thread")] + #[ignore = "turn on when katie fixes the mesh import"] + async fn kcl_test_execute() { + super::execute(TEST_NAME, true).await + } +} diff --git a/rust/kcl-lib/src/std/clone.rs b/rust/kcl-lib/src/std/clone.rs index 98a39dae6..07ab7d9b9 100644 --- a/rust/kcl-lib/src/std/clone.rs +++ b/rust/kcl-lib/src/std/clone.rs @@ -43,7 +43,8 @@ pub async fn clone(exec_state: &mut ExecState, args: Args) -> Result( let get_all_edge_faces_next_uuid = exec_state.next_uuid(); #[cfg(test)] let single_threaded = exec_state.single_threaded; - #[cfg(not(test))] + #[cfg(all(not(test), not(target_arch = "wasm32")))] let single_threaded = false; + // When running in vitest, we need to run this in a single thread. + // Because their workers are complete shit. + #[cfg(all(target_arch = "wasm32", not(test)))] + let single_threaded = crate::wasm::vitest::running_in_vitest(); // Get faces for original edge // Since this one is batched we can just run it. diff --git a/rust/kcl-lib/src/std/sketch.rs b/rust/kcl-lib/src/std/sketch.rs index 56c9001a5..be5171963 100644 --- a/rust/kcl-lib/src/std/sketch.rs +++ b/rust/kcl-lib/src/std/sketch.rs @@ -982,7 +982,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result bool { + let global = js_sys::global(); + + // global.process + let process = Reflect::get(&global, &JsValue::from_str("process")) + .ok() + .unwrap_or_else(|| JsValue::NULL); + // process.env + let env = Reflect::get(&process, &JsValue::from_str("env")) + .ok() + .unwrap_or_else(|| JsValue::NULL); + // env.VITEST + let vitest = Reflect::get(&env, &JsValue::from_str("VITEST")) + .ok() + .unwrap_or_else(|| JsValue::NULL); + + // "true", "1", or a boolean + vitest + .as_bool() + .unwrap_or_else(|| vitest.as_string().map_or(false, |s| s == "true" || s == "1")) +} + +fn is_vitest_by_global() -> bool { + let global = js_sys::global(); + Reflect::has(&global, &JsValue::from_str("__vitest_worker__")).unwrap_or(false) +} + +pub fn running_in_vitest() -> bool { + let running_in_vitest = is_vitest_by_env() || is_vitest_by_global(); + + if running_in_vitest { + web_sys::console::log_1(&JsValue::from_str(&format!( + "running_in_vitest: {}, SOME BEHAVIOR MIGHT BE DIFFERENT THAN THE WASM IN THE APP", + running_in_vitest + ))); + } + + running_in_vitest +} diff --git a/rust/kcl-lib/tests/import_mesh_clone/artifact_commands.snap b/rust/kcl-lib/tests/import_mesh_clone/artifact_commands.snap new file mode 100644 index 000000000..d4f861666 --- /dev/null +++ b/rust/kcl-lib/tests/import_mesh_clone/artifact_commands.snap @@ -0,0 +1,1019 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact commands import_mesh_clone.kcl +--- +[ + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "edge_lines_visible", + "hidden": false + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "object_visible", + "object_id": "[uuid]", + "hidden": true + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "object_visible", + "object_id": "[uuid]", + "hidden": true + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "import_files", + "files": [ + { + "path": "cube.obj", + "data": [ + 35, + 32, + 66, + 108, + 101, + 110, + 100, + 101, + 114, + 32, + 118, + 51, + 46, + 50, + 46, + 50, + 32, + 79, + 66, + 74, + 32, + 70, + 105, + 108, + 101, + 58, + 32, + 39, + 39, + 10, + 35, + 32, + 119, + 119, + 119, + 46, + 98, + 108, + 101, + 110, + 100, + 101, + 114, + 46, + 111, + 114, + 103, + 10, + 109, + 116, + 108, + 108, + 105, + 98, + 32, + 99, + 117, + 98, + 101, + 46, + 109, + 116, + 108, + 10, + 111, + 32, + 67, + 117, + 98, + 101, + 10, + 118, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 54, + 50, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 53, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 56, + 55, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 53, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 56, + 55, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 55, + 53, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 54, + 50, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 55, + 53, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 51, + 55, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 55, + 53, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 54, + 50, + 53, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 51, + 55, + 53, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 51, + 55, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 54, + 50, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 54, + 50, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 50, + 53, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 51, + 55, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 50, + 53, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 49, + 50, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 53, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 51, + 55, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 53, + 48, + 48, + 48, + 48, + 48, + 10, + 118, + 116, + 32, + 48, + 46, + 49, + 50, + 53, + 48, + 48, + 48, + 32, + 48, + 46, + 55, + 53, + 48, + 48, + 48, + 48, + 10, + 118, + 110, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 10, + 118, + 110, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 10, + 118, + 110, + 32, + 49, + 46, + 48, + 48, + 48, + 48, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 10, + 118, + 110, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 10, + 118, + 110, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 10, + 118, + 110, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 32, + 45, + 49, + 46, + 48, + 48, + 48, + 48, + 32, + 48, + 46, + 48, + 48, + 48, + 48, + 10, + 117, + 115, + 101, + 109, + 116, + 108, + 32, + 77, + 97, + 116, + 101, + 114, + 105, + 97, + 108, + 10, + 115, + 32, + 111, + 102, + 102, + 10, + 102, + 32, + 49, + 47, + 49, + 47, + 49, + 32, + 53, + 47, + 50, + 47, + 49, + 32, + 55, + 47, + 51, + 47, + 49, + 32, + 51, + 47, + 52, + 47, + 49, + 10, + 102, + 32, + 52, + 47, + 53, + 47, + 50, + 32, + 51, + 47, + 52, + 47, + 50, + 32, + 55, + 47, + 54, + 47, + 50, + 32, + 56, + 47, + 55, + 47, + 50, + 10, + 102, + 32, + 56, + 47, + 56, + 47, + 51, + 32, + 55, + 47, + 57, + 47, + 51, + 32, + 53, + 47, + 49, + 48, + 47, + 51, + 32, + 54, + 47, + 49, + 49, + 47, + 51, + 10, + 102, + 32, + 54, + 47, + 49, + 50, + 47, + 52, + 32, + 50, + 47, + 49, + 51, + 47, + 52, + 32, + 52, + 47, + 53, + 47, + 52, + 32, + 56, + 47, + 49, + 52, + 47, + 52, + 10, + 102, + 32, + 50, + 47, + 49, + 51, + 47, + 53, + 32, + 49, + 47, + 49, + 47, + 53, + 32, + 51, + 47, + 52, + 47, + 53, + 32, + 52, + 47, + 53, + 47, + 53, + 10, + 102, + 32, + 54, + 47, + 49, + 49, + 47, + 54, + 32, + 53, + 47, + 49, + 48, + 47, + 54, + 32, + 49, + 47, + 49, + 47, + 54, + 32, + 50, + 47, + 49, + 51, + 47, + 54, + 10 + ] + } + ], + "format": { + "type": "obj", + "coords": { + "forward": { + "axis": "y", + "direction": "negative" + }, + "up": { + "axis": "z", + "direction": "positive" + } + }, + "units": "mm" + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "entity_clone", + "entity_id": "[uuid]" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "entity_get_all_child_uuids", + "entity_id": "[uuid]" + } + } +] diff --git a/rust/kcl-lib/tests/import_mesh_clone/artifact_graph_flowchart.snap b/rust/kcl-lib/tests/import_mesh_clone/artifact_graph_flowchart.snap new file mode 100644 index 000000000..153c2570e --- /dev/null +++ b/rust/kcl-lib/tests/import_mesh_clone/artifact_graph_flowchart.snap @@ -0,0 +1,6 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact graph flowchart import_mesh_clone.kcl +extension: md +snapshot_kind: binary +--- diff --git a/rust/kcl-lib/tests/import_mesh_clone/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/import_mesh_clone/artifact_graph_flowchart.snap.md new file mode 100644 index 000000000..13e533509 --- /dev/null +++ b/rust/kcl-lib/tests/import_mesh_clone/artifact_graph_flowchart.snap.md @@ -0,0 +1,3 @@ +```mermaid +flowchart LR +``` diff --git a/rust/kcl-lib/tests/import_mesh_clone/ast.snap b/rust/kcl-lib/tests/import_mesh_clone/ast.snap new file mode 100644 index 000000000..10057f4d9 --- /dev/null +++ b/rust/kcl-lib/tests/import_mesh_clone/ast.snap @@ -0,0 +1,303 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of parsing import_mesh_clone.kcl +--- +{ + "Ok": { + "body": [ + { + "commentStart": 0, + "end": 0, + "path": { + "type": "Foreign", + "path": "../inputs/cube.obj" + }, + "selector": { + "type": "None", + "alias": { + "commentStart": 0, + "end": 0, + "name": "cube", + "start": 0, + "type": "Identifier" + } + }, + "start": 0, + "type": "ImportStatement", + "type": "ImportStatement" + }, + { + "commentStart": 0, + "declaration": { + "commentStart": 0, + "end": 0, + "id": { + "commentStart": 0, + "end": 0, + "name": "model", + "start": 0, + "type": "Identifier" + }, + "init": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "name": { + "commentStart": 0, + "end": 0, + "name": "cube", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + }, + "start": 0, + "type": "VariableDeclarator" + }, + "end": 0, + "kind": "const", + "start": 0, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + }, + { + "commentStart": 0, + "declaration": { + "commentStart": 0, + "end": 0, + "id": { + "commentStart": 0, + "end": 0, + "name": "model2", + "start": 0, + "type": "Identifier" + }, + "init": { + "body": [ + { + "arguments": [ + { + "abs_path": false, + "commentStart": 0, + "end": 0, + "name": { + "commentStart": 0, + "end": 0, + "name": "model", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "name": { + "commentStart": 0, + "end": 0, + "name": "clone", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "start": 0, + "type": "CallExpression", + "type": "CallExpression" + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "name": "x", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "raw": "1020", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 1020.0, + "suffix": "None" + } + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "name": { + "commentStart": 0, + "end": 0, + "name": "translate", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "name": "color", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "raw": "\"#ff0000\"", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": "#ff0000" + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "name": "metalness", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "raw": "50", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 50.0, + "suffix": "None" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "name": "roughness", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "raw": "50", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 50.0, + "suffix": "None" + } + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "name": { + "commentStart": 0, + "end": 0, + "name": "appearance", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + } + ], + "commentStart": 0, + "end": 0, + "start": 0, + "type": "PipeExpression", + "type": "PipeExpression" + }, + "start": 0, + "type": "VariableDeclarator" + }, + "end": 0, + "kind": "const", + "start": 0, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + } + ], + "commentStart": 0, + "end": 0, + "nonCodeMeta": { + "nonCodeNodes": { + "0": [ + { + "commentStart": 0, + "end": 0, + "start": 0, + "type": "NonCodeNode", + "value": { + "type": "newLine" + } + } + ], + "1": [ + { + "commentStart": 0, + "end": 0, + "start": 0, + "type": "NonCodeNode", + "value": { + "type": "newLine" + } + } + ] + }, + "startNodes": [] + }, + "start": 0 + } +} diff --git a/rust/kcl-lib/tests/import_mesh_clone/execution_error.snap b/rust/kcl-lib/tests/import_mesh_clone/execution_error.snap new file mode 100644 index 000000000..9fb21c895 --- /dev/null +++ b/rust/kcl-lib/tests/import_mesh_clone/execution_error.snap @@ -0,0 +1,19 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Error from executing import_mesh_clone.kcl +--- +KCL Internal error + + × internal: failed to fix tags and references: engine: KclErrorDetails + │ { source_ranges: [SourceRange([60, 72, 0])], message: "Modeling command + │ failed: [ApiError { error_code: BadRequest, message: \"Entity type does + │ not currently support transform patterns.\" }, ApiError { error_code: + │ InternalEngine, message: \"Failed to clone entity.\" }, ApiError + │ { error_code: InternalEngine, message: \"Failed to clone entity\" }]" } + ╭─[5:10] + 4 │ + 5 │ model2 = clone(model) + · ──────┬───── + · ╰── tests/import_mesh_clone/input.kcl + 6 │ |> translate( + ╰──── diff --git a/rust/kcl-lib/tests/import_mesh_clone/input.kcl b/rust/kcl-lib/tests/import_mesh_clone/input.kcl new file mode 100644 index 000000000..4643458ca --- /dev/null +++ b/rust/kcl-lib/tests/import_mesh_clone/input.kcl @@ -0,0 +1,13 @@ +import "../inputs/cube.obj" as cube + +model = cube + +model2 = clone(model) + |> translate( + x = 1020, + ) + |> appearance( + color = "#ff0000", + metalness = 50, + roughness = 50 + ) diff --git a/rust/kcl-lib/tests/import_mesh_clone/ops.snap b/rust/kcl-lib/tests/import_mesh_clone/ops.snap new file mode 100644 index 000000000..d3825b53a --- /dev/null +++ b/rust/kcl-lib/tests/import_mesh_clone/ops.snap @@ -0,0 +1,34 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Operations executed import_mesh_clone.kcl +--- +[ + { + "type": "GroupBegin", + "group": { + "type": "ModuleInstance", + "name": "cube", + "moduleId": 6 + }, + "sourceRange": [] + }, + { + "type": "GroupEnd" + }, + { + "isError": true, + "labeledArgs": { + "geometry": { + "value": { + "type": "ImportedGeometry", + "artifact_id": "[uuid]" + }, + "sourceRange": [] + } + }, + "name": "clone", + "sourceRange": [], + "type": "StdLibCall", + "unlabeledArg": null + } +] diff --git a/rust/kcl-lib/tests/import_mesh_clone/unparsed.snap b/rust/kcl-lib/tests/import_mesh_clone/unparsed.snap new file mode 100644 index 000000000..c1771d1ee --- /dev/null +++ b/rust/kcl-lib/tests/import_mesh_clone/unparsed.snap @@ -0,0 +1,11 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of unparsing import_mesh_clone.kcl +--- +import "../inputs/cube.obj" as cube + +model = cube + +model2 = clone(model) + |> translate(x = 1020) + |> appearance(color = "#ff0000", metalness = 50, roughness = 50) diff --git a/rust/kcl-wasm-lib/Cargo.toml b/rust/kcl-wasm-lib/Cargo.toml index b796218dd..b69ef99ee 100644 --- a/rust/kcl-wasm-lib/Cargo.toml +++ b/rust/kcl-wasm-lib/Cargo.toml @@ -12,7 +12,7 @@ bench = false [target.'cfg(target_arch = "wasm32")'.dependencies] bson = { workspace = true, features = ["uuid-1", "chrono"] } -console_error_panic_hook = "0.1.7" +console_error_panic_hook = { workspace = true } data-encoding = "2.6.0" futures = "0.3.31" # Enable the feature in a transitive dependency. diff --git a/simplest.bing b/simplest.bing deleted file mode 100644 index 939368619..000000000 --- a/simplest.bing +++ /dev/null @@ -1,6 +0,0 @@ - -face myRadius = radius of 5 at 0, 5 - -solid extrude myRadius by 5 - -showPart(solid) \ No newline at end of file diff --git a/src/clientSideScene/sceneEntities.ts b/src/clientSideScene/sceneEntities.ts index 08cb9bfc7..e99d1b61a 100644 --- a/src/clientSideScene/sceneEntities.ts +++ b/src/clientSideScene/sceneEntities.ts @@ -3598,11 +3598,20 @@ export class SceneEntities { entity_id: entityId, }, }) - const resp = await this.engineCommandManager.sendSceneCommand({ + let resp = await this.engineCommandManager.sendSceneCommand({ type: 'modeling_cmd_req', cmd_id: uuidv4(), cmd: { type: 'get_sketch_mode_plane' }, }) + + if (!resp) { + return Promise.reject('no response') + } + + if (isArray(resp)) { + resp = resp[0] + } + const faceInfo = resp?.success && resp?.resp.type === 'modeling' && diff --git a/src/components/UpdaterRestartModal.test.tsx b/src/components/UpdaterRestartModal.test.tsx index a97b9cc7d..d75de4dcc 100644 --- a/src/components/UpdaterRestartModal.test.tsx +++ b/src/components/UpdaterRestartModal.test.tsx @@ -30,12 +30,16 @@ describe('UpdaterRestartModal tests', () => { expect(updateButton).toBeEnabled() fireEvent.click(updateButton) expect(callback.mock.calls).toHaveLength(1) - expect(callback.mock.lastCall[0]).toEqual({ wantRestart: true }) + expect(callback.mock?.lastCall ? callback.mock?.lastCall[0] : null).toEqual( + { wantRestart: true } + ) const cancelButton = screen.getByTestId('update-restrart-button-cancel') expect(cancelButton).toBeEnabled() fireEvent.click(cancelButton) expect(callback.mock.calls).toHaveLength(2) - expect(callback.mock.lastCall[0]).toEqual({ wantRestart: false }) + expect(callback.mock?.lastCall ? callback.mock?.lastCall[0] : null).toEqual( + { wantRestart: false } + ) }) }) diff --git a/src/lang/executor.test.ts b/src/lang/executor.test.ts index 2bb7e2efa..26ad9b34e 100644 --- a/src/lang/executor.test.ts +++ b/src/lang/executor.test.ts @@ -1,7 +1,6 @@ import fs from 'node:fs' import { KCLError } from '@src/lang/errors' -import { defaultArtifactGraph } from '@src/lang/std/artifactGraph' import { topLevelRange } from '@src/lang/util' import type { Sketch } from '@src/lang/wasm' import { assertParse, sketchFromKclValue } from '@src/lang/wasm' @@ -463,11 +462,11 @@ const theExtrude = startSketchOn(XY) new KCLError( 'undefined_value', '`myVarZ` is not defined', - topLevelRange(129, 135), - [], - [], - defaultArtifactGraph(), - {}, + topLevelRange(127, 133), + expect.any(Object), + expect.any(Object), + expect.any(Object), + expect.any(Object), null ) ) diff --git a/src/lang/modifyAst/addEdgeTreatment.test.ts b/src/lang/modifyAst/addEdgeTreatment.test.ts index 865138e39..d8936aaf7 100644 --- a/src/lang/modifyAst/addEdgeTreatment.test.ts +++ b/src/lang/modifyAst/addEdgeTreatment.test.ts @@ -70,7 +70,8 @@ const dependencies = { const runGetPathToExtrudeForSegmentSelectionTest = async ( code: string, selectedSegmentSnippet: string, - expectedExtrudeSnippet: string + expectedExtrudeSnippet: string, + expectError?: boolean ) => { // helpers function getExtrudeExpression( @@ -142,6 +143,8 @@ const runGetPathToExtrudeForSegmentSelectionTest = async ( await kclManager.executeAst({ ast }) const artifactGraph = kclManager.artifactGraph + expect(kclManager.errors).toEqual([]) + // find artifact const maybeArtifact = [...artifactGraph].find(([, artifact]) => { if (!('codeRef' in artifact && artifact.codeRef)) return false @@ -160,7 +163,12 @@ const runGetPathToExtrudeForSegmentSelectionTest = async ( selection, artifactGraph ) - if (err(pathResult)) return pathResult + if (err(pathResult)) { + if (!expectError) { + expect(pathResult).toBeUndefined() + } + return pathResult + } const { pathToExtrudeNode } = pathResult const extrudeExpression = getExtrudeExpression(ast, pathToExtrudeNode) @@ -194,7 +202,7 @@ extrude001 = extrude(sketch001, length = -15)` selectedSegmentSnippet, expectedExtrudeSnippet ) - }, 5_000) + }, 10_000) it('should return the correct paths when extrusion occurs within the sketch pipe', async () => { const code = `sketch001 = startSketchOn(XY) |> startProfile(at = [-10, 10]) @@ -324,9 +332,10 @@ extrude003 = extrude(sketch003, length = -15)` await runGetPathToExtrudeForSegmentSelectionTest( code, selectedSegmentSnippet, - expectedExtrudeSnippet + expectedExtrudeSnippet, + true ) - }, 5_000) + }, 10_000) }) const runModifyAstCloneWithEdgeTreatmentAndTag = async ( @@ -351,6 +360,8 @@ const runModifyAstCloneWithEdgeTreatmentAndTag = async ( await kclManager.executeAst({ ast }) const artifactGraph = kclManager.artifactGraph + expect(kclManager.errors).toEqual([]) + const selection: Selections = { graphSelections: segmentRanges.map((segmentRange) => { const maybeArtifact = [...artifactGraph].find(([, a]) => { @@ -373,6 +384,7 @@ const runModifyAstCloneWithEdgeTreatmentAndTag = async ( dependencies ) if (err(result)) { + expect(result).toContain(expectedCode) return result } const { modifiedAst } = result @@ -393,6 +405,8 @@ const runDeleteEdgeTreatmentTest = async ( await kclManager.executeAst({ ast }) const artifactGraph = kclManager.artifactGraph + expect(kclManager.errors).toEqual([]) + // define snippet range const edgeTreatmentRange = topLevelRange( code.indexOf(edgeTreatmentSnippet), @@ -414,6 +428,7 @@ const runDeleteEdgeTreatmentTest = async ( // delete edge treatment const result = await deleteEdgeTreatment(ast, selection) if (err(result)) { + expect(result).toContain(expectedCode) return result } @@ -486,7 +501,7 @@ extrude001 = extrude(sketch001, length = -15, tagEnd = $capEnd001) parameters, expectedCode ) - }) + }, 10_000) it(`should add a ${edgeTreatmentType} to the sketch pipe`, async () => { const code = `sketch001 = startSketchOn(XY) |> startProfile(at = [-10, 10]) @@ -518,7 +533,7 @@ extrude001 = extrude(sketch001, length = -15, tagEnd = $capEnd001) parameters, expectedCode ) - }) + }, 10_000) it(`should add a ${edgeTreatmentType} to an already tagged segment`, async () => { const code = `sketch001 = startSketchOn(XY) |> startProfile(at = [-10, 10]) @@ -550,7 +565,7 @@ extrude001 = extrude(sketch001, length = -15, tagEnd = $capEnd001) parameters, expectedCode ) - }) + }, 10_000) it(`should add a ${edgeTreatmentType} with existing tag on other segment`, async () => { const code = `sketch001 = startSketchOn(XY) |> startProfile(at = [-10, 10]) @@ -582,7 +597,7 @@ extrude001 = extrude(sketch001, length = -15, tagEnd = $capEnd001) parameters, expectedCode ) - }) + }, 10_000) it(`should add a ${edgeTreatmentType} with existing fillet on other segment`, async () => { const code = `sketch001 = startSketchOn(XY) |> startProfile(at = [-10, 10]) @@ -703,7 +718,7 @@ extrude001 = extrude(sketch001, length = -15, tagEnd = $capEnd001) parameters, expectedCode ) - }) + }, 10_000) it(`should add ${edgeTreatmentType}s to two bodies`, async () => { const code = `sketch001 = startSketchOn(XY) |> startProfile(at = [-10, 10]) @@ -762,10 +777,9 @@ extrude002 = extrude(sketch002, length = -25, tagEnd = $capEnd002) parameters, expectedCode ) - }) + }, 10_000) }) - // Skipping since something about the vite worker is suss. - describe.skip(`Testing deleteEdgeTreatment with ${edgeTreatmentType}s`, () => { + describe(`Testing deleteEdgeTreatment with ${edgeTreatmentType}s`, () => { // simple cases it(`should delete a piped ${edgeTreatmentType} from a single segment`, async () => { const code = `sketch001 = startSketchOn(XY) @@ -818,7 +832,7 @@ extrude001 = extrude(sketch001, length = -15)` edgeTreatmentSnippet, expectedCode ) - }) + }, 10_000) // getOppositeEdge and getNextAdjacentEdge cases it(`should delete a piped ${edgeTreatmentType} tagged with getOppositeEdge`, async () => { const code = `sketch001 = startSketchOn(XY) @@ -845,7 +859,7 @@ extrude001 = extrude(sketch001, length = -15)` edgeTreatmentSnippet, expectedCode ) - }) + }, 10_000) it(`should delete a non-piped ${edgeTreatmentType} tagged with getNextAdjacentEdge`, async () => { const code = `sketch001 = startSketchOn(XY) |> startProfile(at = [-10, 10]) @@ -871,7 +885,7 @@ extrude001 = extrude(sketch001, length = -15)` edgeTreatmentSnippet, expectedCode ) - }) + }, 10_000) // cases with several edge treatments it(`should delete a piped ${edgeTreatmentType} from a body with multiple treatments`, async () => { const code = `sketch001 = startSketchOn(XY) @@ -904,7 +918,7 @@ chamfer001 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)])` edgeTreatmentSnippet, expectedCode ) - }) + }, 10_000) it(`should delete a non-piped ${edgeTreatmentType} from a body with multiple treatments`, async () => { const code = `sketch001 = startSketchOn(XY) |> startProfile(at = [-10, 10]) @@ -936,7 +950,7 @@ chamfer001 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)])` edgeTreatmentSnippet, expectedCode ) - }) + }, 10_000) }) } ) @@ -995,19 +1009,19 @@ describe('Testing button states', () => { // body is missing it('should return false when body is missing and nothing is selected', async () => { await runButtonStateTest(codeWithoutBodies, '', false) - }) + }, 10_000) it('should return false when body is missing and segment is selected', async () => { await runButtonStateTest(codeWithoutBodies, `line(end = [10, 0])`, false) - }) + }, 10_000) // body exists it('should return true when body exists and nothing is selected', async () => { await runButtonStateTest(codeWithBody, '', true) - }) + }, 10_000) it('should return true when body exists and segment is selected', async () => { await runButtonStateTest(codeWithBody, `line(end = [10, 0])`, true) - }) + }, 10_000) it('should return false when body exists and not a segment is selected', async () => { await runButtonStateTest(codeWithBody, `close()`, false) - }) + }, 10_000) }) diff --git a/src/lang/std/engineConnection.ts b/src/lang/std/engineConnection.ts index fae26db72..2d7d639d1 100644 --- a/src/lang/std/engineConnection.ts +++ b/src/lang/std/engineConnection.ts @@ -309,16 +309,10 @@ class EngineConnection extends EventTarget { private engineCommandManager: EngineCommandManager private pingPongSpan: { ping?: number; pong?: number } - private pingIntervalId: ReturnType = setInterval( - () => {}, - 60_000 - ) + private pingIntervalId: ReturnType | null = null isUsingConnectionLite: boolean = false - timeoutToForceConnectId: ReturnType = setTimeout( - () => {}, - 3000 - ) + timeoutToForceConnectId: ReturnType | null = null constructor({ engineCommandManager, @@ -391,8 +385,6 @@ class EngineConnection extends EventTarget { this.websocket?.addEventListener('message', ((event: MessageEvent) => { const message: Models['WebSocketResponse_type'] = JSON.parse(event.data) - const pending = - this.engineCommandManager.pendingCommands[message.request_id || ''] if (!('resp' in message)) return let resp = message.resp @@ -412,54 +404,7 @@ class EngineConnection extends EventTarget { return } - if ( - !( - pending && - message.success && - (message.resp.type === 'modeling' || - message.resp.type === 'modeling_batch') - ) - ) - return - - if ( - message.resp.type === 'modeling' && - pending.command.type === 'modeling_cmd_req' && - message.request_id - ) { - this.engineCommandManager.responseMap[message.request_id] = message.resp - } else if ( - message.resp.type === 'modeling_batch' && - pending.command.type === 'modeling_cmd_batch_req' - ) { - let individualPendingResponses: { - [key: string]: Models['WebSocketRequest_type'] - } = {} - pending.command.requests.forEach(({ cmd, cmd_id }) => { - individualPendingResponses[cmd_id] = { - type: 'modeling_cmd_req', - cmd, - cmd_id, - } - }) - Object.entries(message.resp.data.responses).forEach( - ([commandId, response]) => { - if (!('response' in response)) return - const command = individualPendingResponses[commandId] - if (!command) return - if (command.type === 'modeling_cmd_req') - this.engineCommandManager.responseMap[commandId] = { - type: 'modeling', - data: { - modeling_response: response.response, - }, - } - } - ) - } - - pending.resolve([message]) - delete this.engineCommandManager.pendingCommands[message.request_id || ''] + this.engineCommandManager.handleMessage(event) }) as EventListener) } @@ -473,7 +418,12 @@ class EngineConnection extends EventTarget { tearDown(opts?: { idleMode: boolean }) { this.idleMode = opts?.idleMode ?? false - clearInterval(this.pingIntervalId) + if (this.pingIntervalId) { + clearInterval(this.pingIntervalId) + } + if (this.timeoutToForceConnectId) { + clearTimeout(this.timeoutToForceConnectId) + } this.disconnectAll() @@ -1230,9 +1180,7 @@ class EngineConnection extends EventTarget { ) } disconnectAll() { - clearTimeout(this.timeoutToForceConnectId) - - if (this.websocket?.readyState === 1) { + if (this.websocket && this.websocket?.readyState < 3) { this.websocket?.close() } if (this.unreliableDataChannel?.readyState === 'open') { @@ -1322,7 +1270,10 @@ interface PendingMessage { range: SourceRange idToRangeMap: { [key: string]: SourceRange } resolve: (data: [Models['WebSocketResponse_type']]) => void - reject: (reason: string) => void + // BOTH resolve and reject get passed back to the rust side which + // assumes it is this type! Do not change it! + // Format your errors as this type! + reject: (reason: [Models['WebSocketResponse_type']]) => void promise: Promise<[Models['WebSocketResponse_type']]> isSceneCommand: boolean } @@ -1595,117 +1546,7 @@ export class EngineCommandManager extends EventTarget { engineConnection.websocket?.addEventListener('message', (( event: MessageEvent ) => { - let message: Models['WebSocketResponse_type'] | null = null - - if (event.data instanceof ArrayBuffer) { - // BSON deserialize the command. - message = BSON.deserialize( - new Uint8Array(event.data) - ) as Models['WebSocketResponse_type'] - // The request id comes back as binary and we want to get the uuid - // string from that. - if (message.request_id) { - message.request_id = binaryToUuid(message.request_id) - } - } else { - message = JSON.parse(event.data) - } - - if (message === null) { - // We should never get here. - console.error('Received a null message from the engine', event) - return - } - - // In either case we want to send the response back over the wire to - // the rust side. - this.rustContext?.sendResponse(message).catch((err) => { - console.error('Error sending response to rust', err) - }) - - const pending = this.pendingCommands[message.request_id || ''] - - if (pending && !message.success) { - // handle bad case - pending.reject(JSON.stringify(message)) - delete this.pendingCommands[message.request_id || ''] - } - if ( - !( - pending && - message.success && - (message.resp.type === 'modeling' || - message.resp.type === 'modeling_batch' || - message.resp.type === 'export') - ) - ) - return - - if (message.resp.type === 'export' && message.request_id) { - this.responseMap[message.request_id] = message.resp - } else if ( - message.resp.type === 'modeling' && - pending.command.type === 'modeling_cmd_req' && - message.request_id - ) { - this.addCommandLog({ - type: CommandLogType.ReceiveReliable, - data: message.resp, - id: message?.request_id || '', - cmd_type: pending?.command?.cmd?.type, - }) - - const modelingResponse = message.resp.data.modeling_response - - Object.values( - this.subscriptions[modelingResponse.type] || {} - ).forEach((callback) => callback(modelingResponse)) - - this.responseMap[message.request_id] = message.resp - } else if ( - message.resp.type === 'modeling_batch' && - pending.command.type === 'modeling_cmd_batch_req' - ) { - let individualPendingResponses: { - [key: string]: Models['WebSocketRequest_type'] - } = {} - pending.command.requests.forEach(({ cmd, cmd_id }) => { - individualPendingResponses[cmd_id] = { - type: 'modeling_cmd_req', - cmd, - cmd_id, - } - }) - Object.entries(message.resp.data.responses).forEach( - ([commandId, response]) => { - if (!('response' in response)) return - const command = individualPendingResponses[commandId] - if (!command) return - if (command.type === 'modeling_cmd_req') - this.addCommandLog({ - type: CommandLogType.ReceiveReliable, - data: { - type: 'modeling', - data: { - modeling_response: response.response, - }, - }, - id: commandId, - cmd_type: command?.cmd?.type, - }) - - this.responseMap[commandId] = { - type: 'modeling', - data: { - modeling_response: response.response, - }, - } - } - ) - } - - pending.resolve([message]) - delete this.pendingCommands[message.request_id || ''] + this.handleMessage(event) }) as EventListener) this.onVideoTrackMute = () => { @@ -1737,6 +1578,132 @@ export class EngineCommandManager extends EventTarget { return } + handleMessage(event: MessageEvent) { + let message: Models['WebSocketResponse_type'] | null = null + + if (event.data instanceof ArrayBuffer) { + // BSON deserialize the command. + message = BSON.deserialize( + new Uint8Array(event.data) + ) as Models['WebSocketResponse_type'] + // The request id comes back as binary and we want to get the uuid + // string from that. + if (message.request_id) { + message.request_id = binaryToUuid(message.request_id) + } + } else { + message = JSON.parse(event.data) + } + + if (message === null) { + // We should never get here. + console.error('Received a null message from the engine', event) + return + } + + if (message.request_id === undefined || message.request_id === null) { + // We only care about messages that have a request id, so we can + // ignore the rest. + return + } + + // In either case (success / fail) we want to send the response back over the wire to + // the rust side. + this.rustContext?.sendResponse(message).catch((err) => { + console.error('Error sending response to rust', err) + }) + + const pending = this.pendingCommands[message.request_id || ''] + + if (pending && !message.success) { + // handle bad case + pending.reject([message]) + delete this.pendingCommands[message.request_id || ''] + } + + if ( + !( + pending && + message.success && + (message.resp.type === 'modeling' || + message.resp.type === 'modeling_batch' || + message.resp.type === 'export') + ) + ) { + if (pending) { + pending.reject([message]) + delete this.pendingCommands[message.request_id || ''] + } + return + } + + pending.resolve([message]) + delete this.pendingCommands[message.request_id || ''] + + if (message.resp.type === 'export' && message.request_id) { + this.responseMap[message.request_id] = message.resp + } else if ( + message.resp.type === 'modeling' && + pending.command.type === 'modeling_cmd_req' && + message.request_id + ) { + this.addCommandLog({ + type: CommandLogType.ReceiveReliable, + data: message.resp, + id: message?.request_id || '', + cmd_type: pending?.command?.cmd?.type, + }) + + const modelingResponse = message.resp.data.modeling_response + + Object.values(this.subscriptions[modelingResponse.type] || {}).forEach( + (callback) => callback(modelingResponse) + ) + + this.responseMap[message.request_id] = message.resp + } else if ( + message.resp.type === 'modeling_batch' && + pending.command.type === 'modeling_cmd_batch_req' + ) { + let individualPendingResponses: { + [key: string]: Models['WebSocketRequest_type'] + } = {} + pending.command.requests.forEach(({ cmd, cmd_id }) => { + individualPendingResponses[cmd_id] = { + type: 'modeling_cmd_req', + cmd, + cmd_id, + } + }) + Object.entries(message.resp.data.responses).forEach( + ([commandId, response]) => { + if (!('response' in response)) return + const command = individualPendingResponses[commandId] + if (!command) return + if (command.type === 'modeling_cmd_req') + this.addCommandLog({ + type: CommandLogType.ReceiveReliable, + data: { + type: 'modeling', + data: { + modeling_response: response.response, + }, + }, + id: commandId, + cmd_type: command?.cmd?.type, + }) + + this.responseMap[commandId] = { + type: 'modeling', + data: { + modeling_response: response.response, + }, + } + } + ) + } + } + handleResize({ width, height }: { width: number; height: number }) { if (!this.engineConnection?.isReady()) { return @@ -1761,8 +1728,19 @@ export class EngineCommandManager extends EventTarget { tearDown(opts?: { idleMode: boolean }) { if (this.engineConnection) { - for (const pending of Object.values(this.pendingCommands)) { - pending.reject('no connection to send on') + for (const [cmdId, pending] of Object.entries(this.pendingCommands)) { + pending.reject([ + { + success: false, + errors: [ + { + error_code: 'connection_problem', + message: 'no connection to send on, tearing down', + }, + ], + }, + ]) + delete this.pendingCommands[cmdId] } this.engineConnection?.removeEventListener?.( @@ -1852,7 +1830,9 @@ export class EngineCommandManager extends EventTarget { async sendSceneCommand( command: EngineCommand, forceWebsocket = false - ): Promise { + ): Promise< + Models['WebSocketResponse_type'] | [Models['WebSocketResponse_type']] | null + > { if (this.engineConnection === undefined) { return Promise.resolve(null) } @@ -2059,10 +2039,17 @@ export class EngineCommandManager extends EventTarget { * to the engine */ rejectAllModelingCommands(rejectionMessage: string) { - Object.values(this.pendingCommands).forEach( - ({ reject, isSceneCommand }) => - !isSceneCommand && reject(rejectionMessage) - ) + for (const [cmdId, pending] of Object.entries(this.pendingCommands)) { + if (!pending.isSceneCommand) { + pending.reject([ + { + success: false, + errors: [{ error_code: 'internal_api', message: rejectionMessage }], + }, + ]) + delete this.pendingCommands[cmdId] + } + } } async setPlaneHidden(id: string, hidden: boolean) { diff --git a/src/lib/commandBarConfigs/validators.test.ts b/src/lib/commandBarConfigs/validators.test.ts index 977a3aca4..e037588b6 100644 --- a/src/lib/commandBarConfigs/validators.test.ts +++ b/src/lib/commandBarConfigs/validators.test.ts @@ -3,17 +3,11 @@ import { parseEngineErrorMessage } from '@src/lib/commandBarConfigs/validators' describe('parseEngineErrorMessage', () => { it('takes an engine error string and parses its json message', () => { const engineError = - 'engine error: [{"error_code":"internal_engine","message":"Trajectory curve must be G1 continuous (with continuous tangents)"}]' - const message = parseEngineErrorMessage(engineError) + '[{"success": false,"request_id": "e6c0104b-ec60-4779-8e98-722f0a5019ec","errors": [{"error_code": "internal_engine","message": "Trajectory curve must be G1 continuous (with continuous tangents)"}]}]' + const parsedEngineError = JSON.parse(engineError) + const message = parseEngineErrorMessage(parsedEngineError) expect(message).toEqual( 'Trajectory curve must be G1 continuous (with continuous tangents)' ) }) - - it('retuns undefined on strings with different formats', () => { - const s1 = 'engine error: []' - const s2 = 'blabla' - expect(parseEngineErrorMessage(s1)).toBeUndefined() - expect(parseEngineErrorMessage(s2)).toBeUndefined() - }) }) diff --git a/src/lib/commandBarConfigs/validators.ts b/src/lib/commandBarConfigs/validators.ts index 87be89757..ccb395031 100644 --- a/src/lib/commandBarConfigs/validators.ts +++ b/src/lib/commandBarConfigs/validators.ts @@ -1,9 +1,8 @@ import type { Models } from '@kittycad/lib' -import type { ApiError_type } from '@kittycad/lib/dist/types/src/models' import type { Selections } from '@src/lib/selections' import { engineCommandManager, kclManager } from '@src/lib/singletons' -import { uuidv4 } from '@src/lib/utils' +import { isArray, uuidv4 } from '@src/lib/utils' import type { CommandBarContext } from '@src/machines/commandBarMachine' export const disableDryRunWithRetry = async (numberOfRetries = 3) => { @@ -24,7 +23,14 @@ export const disableDryRunWithRetry = async (numberOfRetries = 3) => { } // Takes a callback function and wraps it around enable_dry_run and disable_dry_run -export const dryRunWrapper = async (callback: () => Promise) => { +export const dryRunWrapper = async ( + callback: () => Promise< + | Models['WebSocketResponse_type'] + | [Models['WebSocketResponse_type']] + | undefined + | null + > +): Promise<[Models['WebSocketResponse_type']] | undefined> => { // Gotcha: What about race conditions? try { await engineCommandManager.sendSceneCommand({ @@ -33,7 +39,15 @@ export const dryRunWrapper = async (callback: () => Promise) => { cmd: { type: 'enable_dry_run' }, }) const result = await callback() - return result + if (!result) { + return undefined + } + + if (isArray(result)) { + return result + } + + return [result] } catch (e) { console.error(e) } finally { @@ -48,13 +62,24 @@ function isSelections(selections: unknown): selections is Selections { ) } -export function parseEngineErrorMessage(engineError: string) { - const parts = engineError.split('engine error: ') - if (parts.length < 2) { +export function parseEngineErrorMessage( + engineErrors?: [Models['WebSocketResponse_type']] +): string | undefined { + if (!engineErrors) { return undefined } - const errors = JSON.parse(parts[1]) as ApiError_type[] + if (!engineErrors[0]) { + return undefined + } + + const engineError = engineErrors[0] + + if (engineError.success) { + return undefined + } + + const errors = engineError.errors if (!errors[0]) { return undefined } @@ -114,7 +139,7 @@ export const revolveAxisValidator = async ({ }) } const result = await dryRunWrapper(command) - if (result?.success) { + if (result && result[0] && result[0].success) { return true } @@ -163,7 +188,7 @@ export const loftValidator = async ({ }) } const result = await dryRunWrapper(command) - if (result?.success) { + if (result && result[0] && result[0].success) { return true } @@ -218,7 +243,7 @@ export const shellValidator = async ({ } const result = await dryRunWrapper(command) - if (result?.success) { + if (result && result[0] && result[0].success) { return true } @@ -280,7 +305,7 @@ export const sweepValidator = async ({ } const result = await dryRunWrapper(command) - if (result?.success) { + if (result && result[0] && result[0].success) { return true } diff --git a/src/lib/selections.ts b/src/lib/selections.ts index 2de46904d..d4c28a7f0 100644 --- a/src/lib/selections.ts +++ b/src/lib/selections.ts @@ -39,6 +39,7 @@ import { import { err } from '@src/lib/trap' import { getNormalisedCoordinates, + isArray, isNonNullable, isOverlap, uuidv4, @@ -672,7 +673,7 @@ export async function sendSelectEventToEngine( engineStreamState.videoRef.current, engineCommandManager.streamDimensions ) - const res = await engineCommandManager.sendSceneCommand({ + let res = await engineCommandManager.sendSceneCommand({ type: 'modeling_cmd_req', cmd: { type: 'select_with_point', @@ -681,6 +682,13 @@ export async function sendSelectEventToEngine( }, cmd_id: uuidv4(), }) + if (!res) { + return Promise.reject('no response') + } + + if (isArray(res)) { + res = res[0] + } if ( res?.success && res?.resp?.type === 'modeling' && diff --git a/src/setupTests.ts b/src/setupTests.ts index bccb50d2d..665684dc4 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -62,3 +62,69 @@ vi.mock('three', async () => { // Mock other 'three' exports if necessary } }) + +/// Cleanup the engine connection if we had one. +/* +import { afterAll } from 'vitest' +import { engineCommandManager } from '@src/lib/singletons' + +afterEach(async () => { + const ws = engineCommandManager.engineConnection?.websocket as any + if (ws) { + await finishWebSocket(ws) + } + engineCommandManager.tearDown() +}) + +// 1️⃣ make timers fake +beforeAll(() => vi.useFakeTimers()) + +// 2️⃣ restore real timers when the suite is done +afterAll(() => vi.useRealTimers()) + +/// Cleanup fake timers +afterEach(() => { + vi.runOnlyPendingTimers() + vi.clearAllTimers() +}) + +async function finishWebSocket(ws: WebSocket) { + if (!ws) return + + // Drop ws’s own 30-s watchdog *first* + clearTimeout((ws as any)._closeTimer) + + // Politely ask to close + if (ws.readyState < 2) ws.close(1000) + + // Wait up to 100 ms (real time) for the close to complete, + // then hard-kill synchronously. + await new Promise((res) => { + const realTimer = setTimeout(() => { + ws.terminate() // nukes socket & DNS/TLS handles + res() + }, 100) // real timer, not affected by vi.useFakeTimers() + + ws.once('close', () => { + clearTimeout(realTimer) // handshake succeeded + res() + }) + }) +} + +/// Cleanup happyDOM +afterEach(() => (globalThis as any).happyDOM?.cancelAsync?.()) + +afterAll(() => { + for (const h of (process as any)._getActiveHandles()) { + if (h.constructor?.name === 'FSWatcher') { + console.log('FSWatcher:', h) + } + } +}) + +import why from 'why-is-node-running' +afterAll(() => { + console.error('\n----- LIVE HANDLES -----') + why() // prints sockets, timers, file-watchers with stacks +})*/ diff --git a/vitest.workspace.ts b/vitest.workspace.ts index c9821f56f..3749477a7 100644 --- a/vitest.workspace.ts +++ b/vitest.workspace.ts @@ -1,10 +1,6 @@ import { defineWorkspace } from 'vitest/config' export default defineWorkspace([ - './vite.main.config.ts', - './vite.base.config.ts', './vite.config.ts', - './vite.preload.config.ts', - './vite.renderer.config.ts', './packages/codemirror-lang-kcl/vitest.main.config.ts', ])