diff --git a/.changeset/eleven-laws-care.md b/.changeset/eleven-laws-care.md new file mode 100644 index 0000000..33a4418 --- /dev/null +++ b/.changeset/eleven-laws-care.md @@ -0,0 +1,5 @@ +--- +"google-workspace-developer-tools": minor +--- + +Implement code completion for OAuth2 scopes. diff --git a/.github/actions/setup-xvfb/action.yml b/.github/actions/setup-xvfb/action.yml new file mode 100644 index 0000000..a2d5d43 --- /dev/null +++ b/.github/actions/setup-xvfb/action.yml @@ -0,0 +1,29 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: "Setup Xvfb" +description: "Install and start Xvfb virtual display server for headless testing" +runs: + using: "composite" + steps: + - name: Install Xvfb + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y xvfb + - name: Start Xvfb + shell: bash + run: | + Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + echo "DISPLAY=:99.0" >> $GITHUB_ENV diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0f2134..f5e541c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,7 @@ jobs: - run: pnpm build - run: pnpm lint - run: pnpm check + - uses: ./.github/actions/setup-xvfb - run: pnpm test - run: pnpm ci:package - run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index baea4b0..748ab91 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,5 +31,6 @@ jobs: - run: pnpm build - run: pnpm lint - run: pnpm check + - uses: ./.github/actions/setup-xvfb - run: pnpm test - run: pnpm ci:package diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 5556712..02cc4d5 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -31,6 +31,7 @@ jobs: - run: pnpm ci:update - run: pnpm lint - run: pnpm check + - uses: ./.github/actions/setup-xvfb - run: pnpm test - name: Check for changes id: changes diff --git a/packages/vscode-extension/.vscode-test.mjs b/packages/vscode-extension/.vscode-test.mjs index 29da3d2..4ed5568 100644 --- a/packages/vscode-extension/.vscode-test.mjs +++ b/packages/vscode-extension/.vscode-test.mjs @@ -18,4 +18,6 @@ import { defineConfig } from "@vscode/test-cli"; export default defineConfig({ files: "out/test/**/*.test.cjs", + launchArgs: ["--user-data-dir=/tmp/vscode-test-user-data"], + version: "insiders", }); diff --git a/packages/vscode-extension/README.md b/packages/vscode-extension/README.md index 0cbffa5..eacd050 100644 --- a/packages/vscode-extension/README.md +++ b/packages/vscode-extension/README.md @@ -17,7 +17,9 @@ Automatically validate and document Google Workspace OAuth2 scopes in your code: - **Hover Documentation**: See scope descriptions, associated APIs, and documentation links on hover - **Multi-API Support**: Coverage for all Google Workspace APIs (Gmail, Drive, Calendar, Chat, Admin, and more) -![OAuth2 Scope Linting](https://raw.githubusercontent.com/googleworkspace/vscode-extension/main/packages/vscode-extension/assets/scope-diagnostics.png) +Get code completions for all Google OAuth2 scopes: + +![OAuth2 Scope Linting & Completions](https://raw.githubusercontent.com/googleworkspace/vscode-extension/main/packages/vscode-extension/assets/scope-completion.gif) **Scope Classifications:** diff --git a/packages/vscode-extension/assets/scope-completion.gif b/packages/vscode-extension/assets/scope-completion.gif new file mode 100644 index 0000000..b29bd8f Binary files /dev/null and b/packages/vscode-extension/assets/scope-completion.gif differ diff --git a/packages/vscode-extension/package.json b/packages/vscode-extension/package.json index 73d7511..d2c761e 100644 --- a/packages/vscode-extension/package.json +++ b/packages/vscode-extension/package.json @@ -2,7 +2,7 @@ "name": "google-workspace-developer-tools", "displayName": "Google Workspace Developer Tools", "description": "Lint Google Workspace OAuth2 scopes and more.", - "version": "0.5.4", + "version": "0.5.6", "publisher": "google-workspace", "license": "Apache-2.0", "private": true, @@ -54,13 +54,13 @@ "scripts": { "build": "tsup", "check": "tsc --noEmit", - "ci:package": "vsce package", + "ci:package": "vsce package --no-dependencies", "ci:publish": "ovsx publish --skip-duplicate && vsce publish --skip-duplicate", "ci:version": "changeset version && pnpm i --lockfile-only --engine-strict=false", "ci:update": "tsx scripts/fetch-apis.ts", "dev": "tsup --watch", "pretest": "pnpm build", - "test": "xvfb-run -a vscode-test", + "test": "vscode-test", "vscode:prepublish": "tsup" }, "devDependencies": { @@ -76,5 +76,8 @@ "tsup": "catalog:", "tsx": "catalog:", "typescript": "catalog:" + }, + "dependencies": { + "vitest": "^4.0.4" } } diff --git a/packages/vscode-extension/src/extension.ts b/packages/vscode-extension/src/extension.ts index 2397241..0dd1eab 100644 --- a/packages/vscode-extension/src/extension.ts +++ b/packages/vscode-extension/src/extension.ts @@ -17,6 +17,35 @@ import * as vscode from "vscode"; import { SCOPES, ScopeClassification, getScopeMarkdown } from "./scopes.js"; +export function scopeCompletion( + document: vscode.TextDocument, + position: vscode.Position, +): vscode.CompletionItem[] { + const completionItems: vscode.CompletionItem[] = []; + + const lineText = document.lineAt(position.line).text; + const textBeforeCursor = lineText.substring(0, position.character); + + const match = textBeforeCursor.match( + /(https:\/\/www\.googleapis\.com\/auth\/[a-zA-Z._-]*)$/, + ); + + if (!match) { + return completionItems; + } + + for (const [scope] of SCOPES.entries()) { + if (scope.startsWith(match[1])) { + const item = new vscode.CompletionItem(scope.split("/").pop() ?? scope); + item.insertText = scope.replace(match[1], ""); + completionItems.push(item); + } + } + return completionItems.sort((a, b) => + String(a.label).localeCompare(String(b.label)), + ); +} + export function activate(context: vscode.ExtensionContext) { if (vscode.lm.registerMcpServerDefinitionProvider) { context.subscriptions.push( @@ -77,6 +106,20 @@ export function activate(context: vscode.ExtensionContext) { vscode.languages.createDiagnosticCollection("scopes"); context.subscriptions.push(scopeDiagnostics); + const scopeCompletionProvider = + vscode.languages.registerCompletionItemProvider( + { scheme: "file" }, + { + provideCompletionItems(document, position) { + console.log(position); + return scopeCompletion(document, position); + }, + }, + "/", + ".", + ); + context.subscriptions.push(scopeCompletionProvider); + function updateDiagnostics( document: vscode.TextDocument, collection: vscode.DiagnosticCollection, diff --git a/packages/vscode-extension/src/scopes.ts b/packages/vscode-extension/src/scopes.ts index 2f0571c..2ed37ec 100644 --- a/packages/vscode-extension/src/scopes.ts +++ b/packages/vscode-extension/src/scopes.ts @@ -43,10 +43,6 @@ export const SCOPES = new Map(); for (const { title, version, documentationLink, scopes } of GOOGLE_APIS || []) { for (const { id, description } of scopes || []) { - console.log( - `Processing scope: ${id} - ${description} (${title} v${version})`, - ); - if (!SCOPES.has(id)) { SCOPES.set(id, { description: description, diff --git a/packages/vscode-extension/src/test/completion.test.ts b/packages/vscode-extension/src/test/completion.test.ts new file mode 100644 index 0000000..4083ddf --- /dev/null +++ b/packages/vscode-extension/src/test/completion.test.ts @@ -0,0 +1,36 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from "node:assert"; +import * as vscode from "vscode"; +import { scopeCompletion } from "../extension.js"; + +suite("Completion Provider", () => { + test("it should provide completion for scopes", async () => { + const doc = await vscode.workspace.openTextDocument({ + language: "javascript", + content: "const scope = 'https://www.googleapis.com/auth/drive.f", + }); + + scopeCompletion(doc, doc.lineAt(0).range.end); + + const completionList = scopeCompletion(doc, doc.lineAt(0).range.end); + + assert.ok(completionList.length > 0, "Completion list should not be empty"); + assert.equal(completionList[0].label, "drive.file"); + assert.equal(completionList[0].insertText, "ile"); + }); +}); diff --git a/packages/vscode-extension/tsup.config.mjs b/packages/vscode-extension/tsup.config.mjs index cb456c9..1f97d5f 100644 --- a/packages/vscode-extension/tsup.config.mjs +++ b/packages/vscode-extension/tsup.config.mjs @@ -1,7 +1,11 @@ import { defineConfig } from "tsup"; export default defineConfig({ - entry: ["src/extension.ts", "src/test/**/*.ts"], + entry: [ + "src/extension.ts", + "src/test/scopes.test.ts", + "src/test/completion.test.ts", + ], outDir: "out", clean: true, external: ["vscode"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6a92e79..482dfee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,6 +88,10 @@ importers: version: 5.9.3 packages/vscode-extension: + dependencies: + vitest: + specifier: ^4.0.4 + version: 4.0.4(@types/node@20.19.19)(tsx@4.20.6)(yaml@2.8.1) devDependencies: '@types/mocha': specifier: 'catalog:' @@ -118,7 +122,7 @@ importers: version: 0.10.6 tsup: specifier: 'catalog:' - version: 8.5.0(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + version: 8.5.0(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) tsx: specifier: 'catalog:' version: 4.20.6 @@ -775,6 +779,9 @@ packages: resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@textlint/ast-node-types@15.2.2': resolution: {integrity: sha512-9ByYNzWV8tpz6BFaRzeRzIov8dkbSZu9q7IWqEIfmRuLWb2qbI/5gTvKcoWT1HYs4XM7IZ8TKSXcuPvMb6eorA==} @@ -793,6 +800,12 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -821,6 +834,35 @@ packages: resolution: {integrity: sha512-SnbaqayTVFEA6/tYumdF0UmybY0KHyKwGPBXnyckFlrrKdhWFrL3a2HIPXHjht5ZOElKGcXfD2D63P36btb+ww==} engines: {node: '>=20.0.0'} + '@vitest/expect@4.0.4': + resolution: {integrity: sha512-0ioMscWJtfpyH7+P82sGpAi3Si30OVV73jD+tEqXm5+rIx9LgnfdaOn45uaFkKOncABi/PHL00Yn0oW/wK4cXw==} + + '@vitest/mocker@4.0.4': + resolution: {integrity: sha512-UTtKgpjWj+pvn3lUM55nSg34098obGhSHH+KlJcXesky8b5wCUgg7s60epxrS6yAG8slZ9W8T9jGWg4PisMf5Q==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.4': + resolution: {integrity: sha512-lHI2rbyrLVSd1TiHGJYyEtbOBo2SDndIsN3qY4o4xe2pBxoJLD6IICghNCvD7P+BFin6jeyHXiUICXqgl6vEaQ==} + + '@vitest/runner@4.0.4': + resolution: {integrity: sha512-99EDqiCkncCmvIZj3qJXBZbyoQ35ghOwVWNnQ5nj0Hnsv4Qm40HmrMJrceewjLVvsxV/JSU4qyx2CGcfMBmXJw==} + + '@vitest/snapshot@4.0.4': + resolution: {integrity: sha512-XICqf5Gi4648FGoBIeRgnHWSNDp+7R5tpclGosFaUUFzY6SfcpsfHNMnC7oDu/iOLBxYfxVzaQpylEvpgii3zw==} + + '@vitest/spy@4.0.4': + resolution: {integrity: sha512-G9L13AFyYECo40QG7E07EdYnZZYCKMTSp83p9W8Vwed0IyCG1GnpDLxObkx8uOGPXfDpdeVf24P1Yka8/q1s9g==} + + '@vitest/utils@4.0.4': + resolution: {integrity: sha512-4bJLmSvZLyVbNsYFRpPYdJViG9jZyRvMZ35IF4ymXbRZoS+ycYghmwTGiscTXduUg2lgKK7POWIyXJNute1hjw==} + '@vscode/test-cli@0.0.10': resolution: {integrity: sha512-B0mMH4ia+MOOtwNiLi79XhA+MLmUItIC8FckEuKrVAVriIuSWjt7vv4+bF8qVFiNFe4QRfzPaIZk39FZGWEwHA==} engines: {node: '>=18'} @@ -936,6 +978,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -1029,6 +1075,10 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} + chai@6.2.0: + resolution: {integrity: sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1280,6 +1330,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -1306,6 +1359,9 @@ packages: engines: {node: '>=4'} hasBin: true + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} @@ -1313,6 +1369,10 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -1950,6 +2010,11 @@ packages: resolution: {integrity: sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA==} engines: {node: '>=20.17'} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} @@ -2162,6 +2227,10 @@ packages: yaml: optional: true + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} @@ -2332,6 +2401,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -2362,6 +2434,10 @@ packages: resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} @@ -2385,6 +2461,12 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} @@ -2500,6 +2582,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} @@ -2507,6 +2592,10 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + tmp@0.2.5: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} @@ -2661,6 +2750,80 @@ packages: resolution: {integrity: sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==} engines: {node: '>=4'} + vite@7.1.12: + resolution: {integrity: sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.0.4: + resolution: {integrity: sha512-hV31h0/bGbtmDQc0KqaxsTO1v4ZQeF8ojDFuy4sZhFadwAqqvJA0LDw68QUocctI5EDpFMql/jVWKuPYHIf2Ew==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.4 + '@vitest/browser-preview': 4.0.4 + '@vitest/browser-webdriverio': 4.0.4 + '@vitest/ui': 4.0.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -2684,6 +2847,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + workerpool@6.5.1: resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} @@ -3415,6 +3583,8 @@ snapshots: '@sindresorhus/merge-streams@2.3.0': {} + '@standard-schema/spec@1.0.0': {} + '@textlint/ast-node-types@15.2.2': {} '@textlint/linter-formatter@15.2.2': @@ -3449,6 +3619,13 @@ snapshots: tslib: 2.8.1 optional: true + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.8': {} '@types/istanbul-lib-coverage@2.0.6': {} @@ -3475,6 +3652,45 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/expect@4.0.4': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.4 + '@vitest/utils': 4.0.4 + chai: 6.2.0 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.4(vite@7.1.12(@types/node@20.19.19)(tsx@4.20.6)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 4.0.4 + estree-walker: 3.0.3 + magic-string: 0.30.19 + optionalDependencies: + vite: 7.1.12(@types/node@20.19.19)(tsx@4.20.6)(yaml@2.8.1) + + '@vitest/pretty-format@4.0.4': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.4': + dependencies: + '@vitest/utils': 4.0.4 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.4': + dependencies: + '@vitest/pretty-format': 4.0.4 + magic-string: 0.30.19 + pathe: 2.0.3 + + '@vitest/spy@4.0.4': {} + + '@vitest/utils@4.0.4': + dependencies: + '@vitest/pretty-format': 4.0.4 + tinyrainbow: 3.0.3 + '@vscode/test-cli@0.0.10': dependencies: '@types/mocha': 10.0.10 @@ -3614,6 +3830,8 @@ snapshots: array-union@2.1.0: {} + assertion-error@2.0.1: {} + astral-regex@2.0.0: {} asynckit@0.4.0: {} @@ -3712,6 +3930,8 @@ snapshots: camelcase@6.3.0: {} + chai@6.2.0: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -3960,6 +4180,8 @@ snapshots: es-errors@1.3.0: {} + es-module-lexer@1.7.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -4006,11 +4228,17 @@ snapshots: esprima@4.0.1: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + eventemitter3@5.0.1: {} expand-template@2.0.3: optional: true + expect-type@1.2.2: {} + extend@3.0.2: {} extendable-error@0.1.7: {} @@ -4712,6 +4940,8 @@ snapshots: nano-spawn@1.0.3: {} + nanoid@3.3.11: {} + napi-build-utils@2.0.0: optional: true @@ -4900,13 +5130,20 @@ snapshots: pluralize@8.0.0: {} - postcss-load-config@6.0.1(tsx@4.20.6)(yaml@2.8.1): + postcss-load-config@6.0.1(postcss@8.5.6)(tsx@4.20.6)(yaml@2.8.1): dependencies: lilconfig: 3.1.3 optionalDependencies: + postcss: 8.5.6 tsx: 4.20.6 yaml: 2.8.1 + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prebuild-install@7.1.3: dependencies: detect-libc: 2.1.2 @@ -5125,6 +5362,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@4.1.0: {} simple-concat@1.0.1: @@ -5154,6 +5393,8 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 + source-map-js@1.2.1: {} + source-map@0.8.0-beta.0: dependencies: whatwg-url: 7.1.0 @@ -5179,6 +5420,10 @@ snapshots: sprintf-js@1.0.3: {} + stackback@0.0.2: {} + + std-env@3.10.0: {} + stdin-discarder@0.2.2: {} string-argv@0.3.2: {} @@ -5313,6 +5558,8 @@ snapshots: dependencies: any-promise: 1.3.0 + tinybench@2.9.0: {} + tinyexec@0.3.2: {} tinyglobby@0.2.15: @@ -5320,6 +5567,8 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinyrainbow@3.0.3: {} + tmp@0.2.5: {} to-regex-range@5.0.1: @@ -5336,7 +5585,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.0(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): + tsup@8.5.0(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): dependencies: bundle-require: 5.1.0(esbuild@0.25.10) cac: 6.7.14 @@ -5347,7 +5596,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(tsx@4.20.6)(yaml@2.8.1) + postcss-load-config: 6.0.1(postcss@8.5.6)(tsx@4.20.6)(yaml@2.8.1) resolve-from: 5.0.0 rollup: 4.52.4 source-map: 0.8.0-beta.0 @@ -5356,6 +5605,7 @@ snapshots: tinyglobby: 0.2.15 tree-kill: 1.2.2 optionalDependencies: + postcss: 8.5.6 typescript: 5.9.3 transitivePeerDependencies: - jiti @@ -5453,6 +5703,58 @@ snapshots: version-range@4.15.0: {} + vite@7.1.12(@types/node@20.19.19)(tsx@4.20.6)(yaml@2.8.1): + dependencies: + esbuild: 0.25.10 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.4 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.19 + fsevents: 2.3.3 + tsx: 4.20.6 + yaml: 2.8.1 + + vitest@4.0.4(@types/node@20.19.19)(tsx@4.20.6)(yaml@2.8.1): + dependencies: + '@vitest/expect': 4.0.4 + '@vitest/mocker': 4.0.4(vite@7.1.12(@types/node@20.19.19)(tsx@4.20.6)(yaml@2.8.1)) + '@vitest/pretty-format': 4.0.4 + '@vitest/runner': 4.0.4 + '@vitest/snapshot': 4.0.4 + '@vitest/spy': 4.0.4 + '@vitest/utils': 4.0.4 + debug: 4.4.3(supports-color@8.1.1) + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.19 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.1.12(@types/node@20.19.19)(tsx@4.20.6)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.19.19 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + web-streams-polyfill@3.3.3: {} webidl-conversions@4.0.2: {} @@ -5473,6 +5775,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + workerpool@6.5.1: {} wrap-ansi@7.0.0: