From 8d4e5293ce9cfb5c853c6037bae09efad71d68d4 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 29 Jul 2026 20:25:53 -0600 Subject: [PATCH 01/18] Set up `outFiles` for extension --- .vscode/launch.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index 11d562d2b..6e8daf2bf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,6 +8,9 @@ "args": [ "--extensionDevelopmentPath=${workspaceFolder}/ggsql-vscode" ], + "outFiles": [ + "${workspaceFolder}/ggsql-vscode/out/**/*.js" + ], "preLaunchTask": "build-ggsql-vscode" } ] From b6fe457b4df3e620eee69889ad7034f1eaae0117 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 29 Jul 2026 20:26:20 -0600 Subject: [PATCH 02/18] Fix broken language icon --- ggsql-vscode/CLAUDE.md | 4 +++- ggsql-vscode/package.json | 4 ++-- ggsql-vscode/resources/ggsql-lang-icon.svg | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 ggsql-vscode/resources/ggsql-lang-icon.svg diff --git a/ggsql-vscode/CLAUDE.md b/ggsql-vscode/CLAUDE.md index 01ef6f4a6..69a7ed178 100644 --- a/ggsql-vscode/CLAUDE.md +++ b/ggsql-vscode/CLAUDE.md @@ -13,7 +13,7 @@ ggsql-vscode/ ├── esbuild.js Bundler config (builds out/extension.js) ├── eslint.config.mjs ├── language-configuration.json Bracket pairs, comment markers -├── logo.png, icon.png +├── logo.png Marketplace icon ├── src/ │ ├── extension.ts activate(): registers commands, manager, code lenses │ ├── manager.ts Kernel discovery + Positron language-runtime registration @@ -27,6 +27,8 @@ ggsql-vscode/ │ └── ggsql.tmLanguage.json TextMate grammar (used for tokenization in VS Code) ├── examples/ Sample .ggsql files ├── resources/ Static assets bundled with the extension +│ ├── ggsql-icon.svg Full-colour logo; read by manager.ts for base64EncodedIconSvg +│ └── ggsql-lang-icon.svg contributes.languages[].icon; tuned for the 16px file-icon slot └── ggsql-0.1.0.vsix Packaged extension (build artifact, may be stale) ``` diff --git a/ggsql-vscode/package.json b/ggsql-vscode/package.json index 64533ed33..aaf1d66db 100644 --- a/ggsql-vscode/package.json +++ b/ggsql-vscode/package.json @@ -38,8 +38,8 @@ ], "configuration": "./language-configuration.json", "icon": { - "light": "./icon.png", - "dark": "./icon.png" + "light": "./resources/ggsql-lang-icon.svg", + "dark": "./resources/ggsql-lang-icon.svg" } } ], diff --git a/ggsql-vscode/resources/ggsql-lang-icon.svg b/ggsql-vscode/resources/ggsql-lang-icon.svg new file mode 100644 index 000000000..1766e7c71 --- /dev/null +++ b/ggsql-vscode/resources/ggsql-lang-icon.svg @@ -0,0 +1,17 @@ + + + + From ab6b0f6538fac16b17fa0b1f74361f265eefff8c Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 29 Jul 2026 20:36:22 -0600 Subject: [PATCH 03/18] Command for new `.gsql` file --- ggsql-vscode/CLAUDE.md | 7 ++++++- ggsql-vscode/package.json | 17 +++++++++++++++-- ggsql-vscode/src/extension.ts | 8 ++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/ggsql-vscode/CLAUDE.md b/ggsql-vscode/CLAUDE.md index 69a7ed178..0b5009516 100644 --- a/ggsql-vscode/CLAUDE.md +++ b/ggsql-vscode/CLAUDE.md @@ -34,7 +34,9 @@ ggsql-vscode/ ## File extensions and language ID -`package.json` registers `id: ggsql` for `.ggsql`, `.ggsql.sql`, and `.gsql`. The TextMate grammar at `syntaxes/ggsql.tmLanguage.json` provides tokenization. Tree-sitter highlights — used by editors that prefer the grammar package directly — live in [`/tree-sitter-ggsql/queries/highlights.scm`](../tree-sitter-ggsql/queries/highlights.scm). +`package.json` registers `id: ggsql` for `.gsql`, `.ggsql`, and `.ggsql.sql`. **Order matters:** the first entry is the primary extension, and it is what the workbench suggests when saving an untitled ggsql document (`textFileService.suggestFilename` uses `extensions.at(0)`). `.gsql` is first to match the documented extension in [`/doc/get_started/tooling/positron-vscode.qmd`](../doc/get_started/tooling/positron-vscode.qmd). + +The TextMate grammar at `syntaxes/ggsql.tmLanguage.json` provides tokenization. Tree-sitter highlights — used by editors that prefer the grammar package directly — live in [`/tree-sitter-ggsql/queries/highlights.scm`](../tree-sitter-ggsql/queries/highlights.scm). ## Commands and keybindings @@ -47,9 +49,12 @@ Declared in `package.json` and wired up in `extension.ts`: | `ggsql.runNextCell` | — | Run the next cell | | `ggsql.runCellsAbove` | — | Run all cells above the cursor | | `ggsql.sourceCurrentFile` | — | Run the entire file (also exposed as the editor "Run" button) | +| `ggsql.createNewFile` | — | Open a new untitled ggsql document (also in the New File dialog) | Cells are detected by `cellParser.ts`; `codelens.ts` puts a CodeLens above each cell. +`ggsql.createNewFile` is contributed to the `file/newFile` menu under its own `ggsql` group, so the New File dialog lists it beneath the built-in File and Notebook sections rather than alongside them. It is registered before the Positron API check in `extension.ts`, since opening an untitled document does not need Positron. + ## Positron integration The extension declares `contributes.languageRuntimes` for `ggsql` (see `package.json`) and depends on `@posit-dev/positron`. When activated under Positron, `manager.ts`: diff --git a/ggsql-vscode/package.json b/ggsql-vscode/package.json index aaf1d66db..fbf785319 100644 --- a/ggsql-vscode/package.json +++ b/ggsql-vscode/package.json @@ -32,9 +32,9 @@ { "id": "ggsql", "extensions": [ + ".gsql", ".ggsql", - ".ggsql.sql", - ".gsql" + ".ggsql.sql" ], "configuration": "./language-configuration.json", "icon": { @@ -81,6 +81,12 @@ "command": "ggsql.runCurrentAdvance", "category": "ggsql", "title": "Run Query and Advance" + }, + { + "command": "ggsql.createNewFile", + "category": "ggsql", + "title": "New ggsql File", + "shortTitle": "ggsql File" } ], "keybindings": [ @@ -113,6 +119,13 @@ "command": "workbench.action.positronConsole.executeCode", "when": "resourceLangId == ggsql && !isInDiffEditor" } + ], + "file/newFile": [ + { + "command": "ggsql.createNewFile", + "group": "ggsql", + "when": "!virtualWorkspace" + } ] }, "configuration": { diff --git a/ggsql-vscode/src/extension.ts b/ggsql-vscode/src/extension.ts index 75e27d01d..f06d618d2 100644 --- a/ggsql-vscode/src/extension.ts +++ b/ggsql-vscode/src/extension.ts @@ -29,6 +29,14 @@ export function log(message: string): void { export function activate(context: vscode.ExtensionContext): void { log('ggsql extension activating...'); + // Register "New ggsql File" for the New File dialog + context.subscriptions.push( + vscode.commands.registerCommand('ggsql.createNewFile', async () => { + const document = await vscode.workspace.openTextDocument({ language: 'ggsql' }); + await vscode.window.showTextDocument(document); + }) + ); + // Try to acquire the Positron API const positronApi = tryAcquirePositronApi(); From ade7d668c89078ec8b0ea23b939442faecf7cd52 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 29 Jul 2026 21:19:21 -0600 Subject: [PATCH 04/18] Make more discoverable on `.sql` files --- ggsql-vscode/CLAUDE.md | 25 +++++ ggsql-vscode/package.json | 14 ++- ggsql-vscode/src/codelens.ts | 31 +++++- ggsql-vscode/src/context.ts | 3 +- ggsql-vscode/src/decorations.ts | 3 +- ggsql-vscode/src/extension.ts | 12 ++- ggsql-vscode/src/languages.ts | 44 ++++++++ ggsql-vscode/src/sqlAssociation.ts | 163 +++++++++++++++++++++++++++++ 8 files changed, 285 insertions(+), 10 deletions(-) create mode 100644 ggsql-vscode/src/languages.ts create mode 100644 ggsql-vscode/src/sqlAssociation.ts diff --git a/ggsql-vscode/CLAUDE.md b/ggsql-vscode/CLAUDE.md index 0b5009516..fc1338133 100644 --- a/ggsql-vscode/CLAUDE.md +++ b/ggsql-vscode/CLAUDE.md @@ -55,6 +55,31 @@ Cells are detected by `cellParser.ts`; `codelens.ts` puts a CodeLens above each `ggsql.createNewFile` is contributed to the `file/newFile` menu under its own `ggsql` group, so the New File dialog lists it beneath the built-in File and Notebook sections rather than alongside them. It is registered before the Positron API check in `extension.ts`, since opening an untitled document does not need Positron. +## Attaching to `.sql` files + +ggsql is a SQL superset, so the kernel can execute a plain `.sql` file. Rather than claiming the `.sql` file association, the extension attaches its run affordances to documents already tokenized as `sql`: + +- [`src/languages.ts`](src/languages.ts) is the single source of truth. `isGgsqlDocument()` decides whether ggsql will act on a document; every command guard, decoration and context key routes through it. Add new language-scoped behaviour there rather than comparing `languageId` inline. +- `CELL_LANGUAGE_IDS` registers the CodeLens provider for both ids. The provider gates on `isGgsqlDocument()` at request time and fires `onDidChangeCodeLenses` when `ggsql.enableSqlFiles` changes, so the setting takes effect without re-registering. +- The `editor/title/run` `when` clauses gate on `config.ggsql.enableSqlFiles` directly, so no context key is needed for the buttons. + +Why not claim `.sql` in `contributes.languages`: extension-contributed associations are resolved last-registered-wins (`languagesAssociations.ts`), which is not a contract and is fragile against other SQL extensions. It would also replace the richer built-in `source.sql` grammar and break language-scoped features from other SQL tooling. + +[`src/sqlAssociation.ts`](src/sqlAssociation.ts) shows a one-time notice pointing at `files.associations`, which is the right mechanism for users who want ggsql highlighting in `.sql` files because user-configured associations sit in the highest precedence tier and so win deterministically. + +**It does not write the setting.** Mapping `*.sql` rewrites how every `.sql` file in every workspace is treated, which is not an extension's call, and running SQL in the ggsql console does not need it. An earlier version did write it on a button press, and that turned out to need a reset command, an offer to un-write, and care over which config level to base the update on. None of that is needed now. + +Three constraints worth knowing before editing the notice: + +- **Notification text is not markdown.** It is parsed by `parseLinkedText`, which matches markdown links and nothing else, so code spans and emphasis render literally. Hrefs are limited to `https:`, `command:` and `file:`, and may contain no spaces or `)`. The setting name is therefore a `command:` link, and the mapping is spelled out in prose rather than backticked. +- **Any button press dismisses the notification**, so every button has to be a complete answer. The documentation link lives on the `ggsql.enableSqlFiles` setting rather than being a button here. +- **The notice fires on every `.sql` open, and only "Don't show again" persists.** An Info notification with buttons is *not* sticky (`notifications.ts` makes actions sticky only at Error severity), so it auto-hides after 10 seconds and extensions cannot opt out. A show-once notice is therefore trivially missed, which would defeat the point. A module-level `noticeVisible` guard stops that becoming a pile-up when many `.sql` files open at once. `ggsql: Reset .sql File Association Prompt` clears the persisted flag, the only route back from "Don't show again" short of editing the global storage database. + +The `ggsql.enableSqlFiles` description uses `markdownDescription` rather than `description`, which matters for two reasons: only the markdown path runs `fixSettingLinks`, and only it renders links at all. It uses both link forms available there: + +- `` `#files.associations#` `` renders as a link to that setting. The backticks (or single quotes) are required; a bare `#files.associations#` is left as literal text. +- A plain markdown link to [`/doc/get_started/tooling/positron-vscode.qmd`](../doc/get_started/tooling/positron-vscode.qmd), whose `## Using ggsql with .sql files` heading carries an explicit `{#sql-files}` anchor so the URL survives a rewording. Keep the anchor if you edit that heading. + ## Positron integration The extension declares `contributes.languageRuntimes` for `ggsql` (see `package.json`) and depends on `@posit-dev/positron`. When activated under Positron, `manager.ts`: diff --git a/ggsql-vscode/package.json b/ggsql-vscode/package.json index fbf785319..b09c5c976 100644 --- a/ggsql-vscode/package.json +++ b/ggsql-vscode/package.json @@ -87,6 +87,11 @@ "category": "ggsql", "title": "New ggsql File", "shortTitle": "ggsql File" + }, + { + "command": "ggsql.resetSqlAssociationPrompt", + "category": "ggsql", + "title": "Reset .sql File Association Prompt" } ], "keybindings": [ @@ -113,11 +118,11 @@ { "command": "ggsql.sourceCurrentFile", "group": "navigation@0", - "when": "resourceLangId == ggsql && !isInDiffEditor" + "when": "(resourceLangId == ggsql || (resourceLangId == sql && config.ggsql.enableSqlFiles)) && !isInDiffEditor" }, { "command": "workbench.action.positronConsole.executeCode", - "when": "resourceLangId == ggsql && !isInDiffEditor" + "when": "(resourceLangId == ggsql || (resourceLangId == sql && config.ggsql.enableSqlFiles)) && !isInDiffEditor" } ], "file/newFile": [ @@ -136,6 +141,11 @@ "type": "string", "default": "", "description": "Path to the ggsql-jupyter executable. If empty, uses 'ggsql-jupyter' from PATH." + }, + "ggsql.enableSqlFiles": { + "type": "boolean", + "default": true, + "markdownDescription": "Offer ggsql run buttons and code cells in plain `.sql` files. ggsql is a superset of SQL, so the ggsql console can execute them. Turn this off if you use `.sql` files with other database tooling.\n\nTo also give `.sql` files ggsql syntax highlighting, map them to the ggsql language with `#files.associations#`. See [Using ggsql with `.sql` files](https://ggsql.org/get_started/tooling/positron-vscode.html#sql-files) for details." } } } diff --git a/ggsql-vscode/src/codelens.ts b/ggsql-vscode/src/codelens.ts index 9934c8ab4..404454369 100644 --- a/ggsql-vscode/src/codelens.ts +++ b/ggsql-vscode/src/codelens.ts @@ -1,8 +1,31 @@ import * as vscode from 'vscode'; import { parseCells, type Cell } from './cellParser'; +import { isGgsqlDocument } from './languages'; export class GgsqlCodeLensProvider implements vscode.CodeLensProvider { + private readonly changed = new vscode.EventEmitter(); + + /** Fired when `ggsql.enableSqlFiles` changes, so `.sql` lenses appear or clear. */ + readonly onDidChangeCodeLenses = this.changed.event; + + constructor(disposables: vscode.Disposable[]) { + disposables.push( + this.changed, + vscode.workspace.onDidChangeConfiguration(event => { + if (event.affectsConfiguration('ggsql.enableSqlFiles')) { + this.changed.fire(); + } + }), + ); + } + provideCodeLenses(document: vscode.TextDocument): vscode.CodeLens[] { + // Registered for `sql` as well as `ggsql`, so gate here rather than at + // registration time; that way the setting takes effect immediately. + if (!isGgsqlDocument(document)) { + return []; + } + const cells = parseCells(document); const lenses: vscode.CodeLens[] = []; @@ -54,7 +77,7 @@ export function registerCellCommands( context.subscriptions.push( vscode.commands.registerCommand('ggsql.runQuery', (line?: number) => { const editor = vscode.window.activeTextEditor; - if (!editor || editor.document.languageId !== 'ggsql') { return; } + if (!editor || !isGgsqlDocument(editor.document)) { return; } const cells = parseCells(editor.document); const cell = line !== undefined ? findCellAtLine(cells, line) @@ -66,7 +89,7 @@ export function registerCellCommands( vscode.commands.registerCommand('ggsql.runCurrentAdvance', (line?: number) => { const editor = vscode.window.activeTextEditor; - if (!editor || editor.document.languageId !== 'ggsql') { return; } + if (!editor || !isGgsqlDocument(editor.document)) { return; } const cells = parseCells(editor.document); const targetLine = line ?? editor.selection.active.line; const cell = findCellAtLine(cells, targetLine); @@ -84,7 +107,7 @@ export function registerCellCommands( vscode.commands.registerCommand('ggsql.runCellsAbove', (line?: number) => { const editor = vscode.window.activeTextEditor; - if (!editor || editor.document.languageId !== 'ggsql') { return; } + if (!editor || !isGgsqlDocument(editor.document)) { return; } const cells = parseCells(editor.document); const cursor = new vscode.Position(line ?? editor.selection.active.line, 0); cells @@ -98,7 +121,7 @@ export function registerCellCommands( vscode.commands.registerCommand('ggsql.runNextCell', (line?: number) => { const editor = vscode.window.activeTextEditor; - if (!editor || editor.document.languageId !== 'ggsql') { return; } + if (!editor || !isGgsqlDocument(editor.document)) { return; } const cells = parseCells(editor.document); const targetLine = line ?? editor.selection.active.line; const next = findNextCell(cells, targetLine); diff --git a/ggsql-vscode/src/context.ts b/ggsql-vscode/src/context.ts index f2cf7468d..f95dd501a 100644 --- a/ggsql-vscode/src/context.ts +++ b/ggsql-vscode/src/context.ts @@ -1,9 +1,10 @@ import * as vscode from 'vscode'; import { parseCells } from './cellParser'; +import { isGgsqlDocument } from './languages'; function setHasCodeCells(editor: vscode.TextEditor | undefined): void { let value = false; - if (editor && editor.document.languageId === 'ggsql') { + if (editor && isGgsqlDocument(editor.document)) { value = parseCells(editor.document).length > 0; } vscode.commands.executeCommand('setContext', 'ggsql.hasCodeCells', value); diff --git a/ggsql-vscode/src/decorations.ts b/ggsql-vscode/src/decorations.ts index 916bd0981..53571f429 100644 --- a/ggsql-vscode/src/decorations.ts +++ b/ggsql-vscode/src/decorations.ts @@ -1,5 +1,6 @@ import * as vscode from 'vscode'; import { parseCells } from './cellParser'; +import { isGgsqlDocument } from './languages'; const activeCellBackground = new vscode.ThemeColor('notebook.selectedCellBackground'); @@ -13,7 +14,7 @@ export function activateDecorations(disposables: vscode.Disposable[]): void { let activeEditor = vscode.window.activeTextEditor; function updateDecorations() { - if (!activeEditor || activeEditor.document.languageId !== 'ggsql') { + if (!activeEditor || !isGgsqlDocument(activeEditor.document)) { return; } diff --git a/ggsql-vscode/src/extension.ts b/ggsql-vscode/src/extension.ts index f06d618d2..beefcacf4 100644 --- a/ggsql-vscode/src/extension.ts +++ b/ggsql-vscode/src/extension.ts @@ -13,6 +13,8 @@ import { GgsqlCodeLensProvider, registerCellCommands } from './codelens'; import { activateDecorations } from './decorations'; import { activateContextKeys } from './context'; import { parseCells } from './cellParser'; +import { CELL_LANGUAGE_IDS, isGgsqlDocument } from './languages'; +import { activateSqlAssociationPrompt } from './sqlAssociation'; // Output channel for logging const outputChannel = vscode.window.createOutputChannel('ggsql'); @@ -37,6 +39,9 @@ export function activate(context: vscode.ExtensionContext): void { }) ); + // Offer to associate .sql files with ggsql + activateSqlAssociationPrompt(context); + // Try to acquire the Positron API const positronApi = tryAcquirePositronApi(); @@ -69,7 +74,7 @@ export function activate(context: vscode.ExtensionContext): void { context.subscriptions.push( vscode.commands.registerCommand('ggsql.sourceCurrentFile', async () => { const editor = vscode.window.activeTextEditor; - if (!editor || editor.document.languageId !== 'ggsql') { + if (!editor || !isGgsqlDocument(editor.document)) { return; } const cells = parseCells(editor.document); @@ -91,7 +96,10 @@ export function activate(context: vscode.ExtensionContext): void { // Register code lens provider and cell commands context.subscriptions.push( - vscode.languages.registerCodeLensProvider('ggsql', new GgsqlCodeLensProvider()), + vscode.languages.registerCodeLensProvider( + CELL_LANGUAGE_IDS, + new GgsqlCodeLensProvider(context.subscriptions), + ), ); registerCellCommands(context, (code) => { positronApi.runtime.executeCode('ggsql', code, false, true); diff --git a/ggsql-vscode/src/languages.ts b/ggsql-vscode/src/languages.ts new file mode 100644 index 000000000..bdc028346 --- /dev/null +++ b/ggsql-vscode/src/languages.ts @@ -0,0 +1,44 @@ +/* + * Which documents ggsql attaches to. + * + * ggsql is a superset of SQL, so the kernel can execute a plain `.sql` file. + * The extension therefore offers its run affordances on documents tokenized as + * `sql` as well as `ggsql`, without claiming the `.sql` file association. That + * keeps the richer built-in SQL grammar in place for plain SQL, and leaves + * language-scoped features from other SQL extensions working. + * + * Users who want the full ggsql treatment for `.sql` files (ggsql syntax + * highlighting, the ggsql file icon) set `files.associations` instead, which + * outranks every extension-contributed association. `sqlAssociation.ts` offers + * to do that for them. + */ + +import * as vscode from 'vscode'; + +export const GGSQL_LANGUAGE_ID = 'ggsql'; +export const SQL_LANGUAGE_ID = 'sql'; + +/** + * Language ids the CodeLens provider registers against. Both are registered + * unconditionally; `isGgsqlDocument` gates `sql` at request time so that + * toggling the setting takes effect without re-registering providers. + */ +export const CELL_LANGUAGE_IDS = [GGSQL_LANGUAGE_ID, SQL_LANGUAGE_ID]; + +/** Whether ggsql offers its run affordances in plain `.sql` files. */ +export function sqlFilesEnabled(): boolean { + return vscode.workspace + .getConfiguration('ggsql') + .get('enableSqlFiles', true); +} + +/** True when ggsql is willing to execute the contents of `document`. */ +export function isGgsqlDocument(document: vscode.TextDocument | undefined): boolean { + if (!document) { + return false; + } + if (document.languageId === GGSQL_LANGUAGE_ID) { + return true; + } + return document.languageId === SQL_LANGUAGE_ID && sqlFilesEnabled(); +} diff --git a/ggsql-vscode/src/sqlAssociation.ts b/ggsql-vscode/src/sqlAssociation.ts new file mode 100644 index 000000000..cfa736a65 --- /dev/null +++ b/ggsql-vscode/src/sqlAssociation.ts @@ -0,0 +1,163 @@ +/* + * Points users at `files.associations` when they open a plain `.sql` file. + * + * Running SQL through the ggsql kernel already works without any association + * (see `languages.ts`); mapping the files additionally gives them ggsql syntax + * highlighting and the ggsql file icon. That is a preference, not a + * prerequisite, so the extension only surfaces the option and lets the user + * make the change. It deliberately does not write `files.associations` itself: + * mapping `*.sql` globally rewrites how every `.sql` file in every workspace is + * treated, which is not a decision an extension should take on someone's behalf. + * + * `files.associations` is the right mechanism for users who do want it, because + * user-configured associations outrank every extension-contributed one and so + * win deterministically even with other SQL extensions installed. + * + * The notice appears on each `.sql` file opened until dismissed for good, and + * never when `*.sql` is already mapped. + */ + +import * as vscode from 'vscode'; +import { SQL_LANGUAGE_ID, sqlFilesEnabled } from './languages'; +import { log } from './extension'; + +/** Set once the user has actively dismissed the notice for good. */ +const PROMPTED_KEY = 'ggsql.sqlAssociationPrompted'; + +const SQL_GLOB = '*.sql'; + +const SETTINGS_QUERY = 'files.associations'; + +/** + * True while a notice is on screen awaiting an answer. + * + * The notice is shown every time a `.sql` file is opened, because an Info + * notification with buttons is not sticky and auto-hides after 10 seconds + * (`notificationsToasts.ts`), and extensions cannot opt out of that: a + * show-once notice is trivially missed. Letting it age out is therefore not + * treated as an answer, only "Don't show again" is. + * + * This guard keeps that from becoming a pile-up. Opening a folder full of + * `.sql` files, or another extension opening them in the background, would + * otherwise queue one notice per file; instead the first is shown and the rest + * are skipped while it is still pending. + */ +let noticeVisible = false; + +/** + * True when `*.sql` is already mapped at any level. + * + * Reads the *effective* value on purpose: if a default, a workspace or the user + * maps `*.sql` to anything, there is nothing to tell them about. + */ +function hasSqlAssociation(): boolean { + const associations = vscode.workspace + .getConfiguration('files') + .get>('associations', {}); + return SQL_GLOB in associations; +} + +/* + * Notification text is parsed by `parseLinkedText`, which understands markdown + * links and nothing else: no code spans, no emphasis. Backticks would render + * literally, so the mapping is spelled out in prose and the setting name is a + * command link. Only https, command and file hrefs are permitted, and the href + * may contain no spaces or `)`. + * + * `workbench.action.openSettings` takes its query as a plain string argument, + * and the opener JSON-parses the URI query then wraps a non-array in an array, + * so an encoded bare string is enough. + */ +const SETTINGS_LINK = + `[Files: Associations](command:workbench.action.openSettings?%22files.associations%22)`; + +const MESSAGE = + 'You can run this .sql file in the ggsql console. For ggsql syntax highlighting in ' + + `.sql files as well, map "${SQL_GLOB}" to "ggsql" in ${SETTINGS_LINK}.`; + +async function showNotice(context: vscode.ExtensionContext): Promise { + noticeVisible = true; + const showSetting = 'Show Setting'; + const dontShow = 'Don\'t show again'; + let choice: string | undefined; + try { + choice = await vscode.window.showInformationMessage(MESSAGE, showSetting, dontShow); + } finally { + noticeVisible = false; + } + + if (choice === showSetting) { + await vscode.commands.executeCommand('workbench.action.openSettings', SETTINGS_QUERY); + } + // Only an explicit "don't show again" is persisted. Anything else, including + // the toast ageing out unseen, leaves the notice free to appear again. + if (choice === dontShow) { + await context.globalState.update(PROMPTED_KEY, true); + } +} + +/** + * Clears the persisted dismissal so the notice can appear again. + * + * Only reachable state to undo is "Don't show again", which otherwise has no + * route back short of editing the global storage database. + */ +async function resetNotice(context: vscode.ExtensionContext): Promise { + await context.globalState.update(PROMPTED_KEY, undefined); + log('Reset the .sql association notice'); + + if (hasSqlAssociation()) { + // An existing mapping makes `.sql` files open as `ggsql`, so the notice + // still will not fire. Say so rather than appearing to have done nothing. + void vscode.window.showInformationMessage( + `Notice reset, but "${SQL_GLOB}" is still mapped in ${SETTINGS_QUERY}, so .sql ` + + 'files open as ggsql and the notice stays hidden. Remove that mapping to see it.', + ); + return; + } + + void vscode.window.showInformationMessage( + 'ggsql will mention the .sql association again next time you open a .sql file.', + ); +} + +/** + * Mentions the association whenever a plain `.sql` document is opened. + * + * Registered regardless of whether Positron is present: the association affects + * syntax highlighting, which is useful even where the kernel cannot run. + */ +export function activateSqlAssociationPrompt(context: vscode.ExtensionContext): void { + context.subscriptions.push( + vscode.commands.registerCommand( + 'ggsql.resetSqlAssociationPrompt', + () => resetNotice(context), + ), + ); + + const maybeNotify = (document: vscode.TextDocument | undefined): void => { + if (!document || document.languageId !== SQL_LANGUAGE_ID) { + return; + } + // Only real files on disk; skip untitled buffers, diffs and output panes. + if (document.uri.scheme !== 'file') { + return; + } + if (noticeVisible || context.globalState.get(PROMPTED_KEY)) { + return; + } + if (hasSqlAssociation()) { + return; + } + if (!sqlFilesEnabled()) { + return; + } + void showNotice(context); + }; + + maybeNotify(vscode.window.activeTextEditor?.document); + + context.subscriptions.push( + vscode.workspace.onDidOpenTextDocument(maybeNotify), + ); +} From c291fd182f5a03d9bb5457c7bf95061ca58efca9 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 29 Jul 2026 21:19:31 -0600 Subject: [PATCH 05/18] Update docs --- doc/get_started/tooling/positron-vscode.qmd | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/get_started/tooling/positron-vscode.qmd b/doc/get_started/tooling/positron-vscode.qmd index 9eecc7112..de213c349 100644 --- a/doc/get_started/tooling/positron-vscode.qmd +++ b/doc/get_started/tooling/positron-vscode.qmd @@ -41,6 +41,26 @@ To create a cell, simply add a line with `-- %%` between blocks of code in your ![](./screenshots/positron-cells.png) +## Using ggsql with `.sql` files {#sql-files} + +ggsql is a superset of SQL, so the ggsql console can execute a plain SQL query. The extension offers the "Source Current File" button and code cells in `.sql` files as well as `.gsql` files, which means you can run SQL you already have against a ggsql kernel without renaming anything. + +By default `.sql` files keep their usual SQL syntax highlighting, so any other database tooling you have installed continues to work as before. If you would rather give `.sql` files the full ggsql treatment, including highlighting for `VISUALISE` and the other ggsql clauses, map them to the `ggsql` language type: + +```json +"files.associations": { + "*.sql": "ggsql" +} +``` + +The extension points you at this setting when you open a `.sql` file, but it never changes the setting for you. Mapping `*.sql` affects every `.sql` file you open, so the choice is left to you. Add it to a single workspace rather than your user settings if you only want it for one project, and use "Don't show again" on the notification if you would rather not be reminded. + +If you use `.sql` files with other database tooling and would prefer the extension leave them alone entirely, turn the run buttons and cells off: + +```json +"ggsql.enableSqlFiles": false +``` + ## Database connections By default, ggsql starts in the Positron console with an empty in-memory duckdb database connection. A "magic" comment can be used to initiate a different database connection after the session has launched, which can be either a comment in your source file or invoked directly in the console. From d02050565e048dcfd25cb85a3539d60e2dbe3869 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 29 Jul 2026 21:19:38 -0600 Subject: [PATCH 06/18] Update CHANGELOG --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9eb1a45c..21365ee92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ ## [Unreleased] +### Added + +- The VS Code / Positron extension now offers its "Source Current File" button + and code cells in plain `.sql` files, so existing SQL can be run against a + ggsql kernel without renaming it. `.sql` files keep their usual SQL syntax + highlighting; to get ggsql highlighting as well, map them to the `ggsql` + language type with `files.associations`, which the extension points out the + first time a `.sql` file is opened. The new `ggsql.enableSqlFiles` setting + turns the whole behaviour off. +- The VS Code / Positron extension contributes a "ggsql File" entry to the + New File dialog. + +### Fixed + +- The VS Code / Positron extension now ships a language icon that renders in the + session picker, editor tabs and the Explorer. It previously pointed at a file + that did not exist, which left the icon blank. + ## 0.4.1 - 2026-06-22 ### Changed From 51073d0d1c73c2e2240622705e88087e8c52d943 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 29 Jul 2026 21:34:16 -0600 Subject: [PATCH 07/18] Fix when execution is offered --- CHANGELOG.md | 3 +++ ggsql-vscode/CLAUDE.md | 10 +++++++++- ggsql-vscode/package.json | 32 +++++++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21365ee92..92f8a03f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ - The VS Code / Positron extension now ships a language icon that renders in the session picker, editor tabs and the Explorer. It previously pointed at a file that did not exist, which left the icon blank. +- In plain VS Code, the extension no longer offers run buttons, keybindings or + Command Palette entries for commands that need the Positron runtime and so + had no handler there. ## 0.4.1 - 2026-06-22 diff --git a/ggsql-vscode/CLAUDE.md b/ggsql-vscode/CLAUDE.md index fc1338133..1337ab7b9 100644 --- a/ggsql-vscode/CLAUDE.md +++ b/ggsql-vscode/CLAUDE.md @@ -88,7 +88,15 @@ The extension declares `contributes.languageRuntimes` for `ggsql` (see `package. 2. Registers it as a Positron language runtime so `▶ Run` and the Console route to the kernel. 3. Routes plot output to Positron's Plot pane via metadata coming back from the kernel (`output_location: "plot"`). -Outside Positron, the same commands fall back to writing query output to the active terminal. +Outside Positron there is no way to execute a query: `activate()` returns early, so every command that runs code stays unregistered. To avoid offering actions that cannot work, everything execution-related gates on Positron's built-in **`isPositron`** context key ([extension development docs](https://positron.posit.co/extension-development.html#option-1-context-keys)): + +- the two `editor/title/run` buttons +- the three keybindings +- the five run commands, hidden from the Command Palette via a `commandPalette` menu entry + +`isPositron` is declared with a default of `true`, so it is correct in Positron from startup with no code and no activation-order window. In VS Code the key is never declared, so it evaluates falsy. Prefer it over a hand-rolled `setContext` key for anything purely declarative; the Positron API (`tryAcquirePositronApi`) is the right check when the code needs the API object itself. + +Anything that does *not* need the runtime (`ggsql.createNewFile`, `ggsql.resetSqlAssociationPrompt`, syntax highlighting) is registered before the early return and works in plain VS Code. Add new commands on the correct side of that line, and gate them if they execute code. ## Settings diff --git a/ggsql-vscode/package.json b/ggsql-vscode/package.json index b09c5c976..53adb4725 100644 --- a/ggsql-vscode/package.json +++ b/ggsql-vscode/package.json @@ -99,18 +99,18 @@ "command": "ggsql.runCurrentAdvance", "key": "ctrl+enter", "mac": "cmd+enter", - "when": "editorTextFocus && editorLangId == ggsql && ggsql.hasCodeCells && !findInputFocussed && !replaceInputFocussed" + "when": "isPositron && editorTextFocus && editorLangId == ggsql && ggsql.hasCodeCells && !findInputFocussed && !replaceInputFocussed" }, { "command": "ggsql.runCurrentAdvance", "key": "shift+enter", - "when": "editorTextFocus && editorLangId == ggsql && ggsql.hasCodeCells && !findInputFocussed && !replaceInputFocussed" + "when": "isPositron && editorTextFocus && editorLangId == ggsql && ggsql.hasCodeCells && !findInputFocussed && !replaceInputFocussed" }, { "command": "ggsql.runQuery", "key": "ctrl+shift+enter", "mac": "cmd+shift+enter", - "when": "editorTextFocus && editorLangId == ggsql && ggsql.hasCodeCells && !findInputFocussed && !replaceInputFocussed" + "when": "isPositron && editorTextFocus && editorLangId == ggsql && ggsql.hasCodeCells && !findInputFocussed && !replaceInputFocussed" } ], "menus": { @@ -118,11 +118,11 @@ { "command": "ggsql.sourceCurrentFile", "group": "navigation@0", - "when": "(resourceLangId == ggsql || (resourceLangId == sql && config.ggsql.enableSqlFiles)) && !isInDiffEditor" + "when": "isPositron && (resourceLangId == ggsql || (resourceLangId == sql && config.ggsql.enableSqlFiles)) && !isInDiffEditor" }, { "command": "workbench.action.positronConsole.executeCode", - "when": "(resourceLangId == ggsql || (resourceLangId == sql && config.ggsql.enableSqlFiles)) && !isInDiffEditor" + "when": "isPositron && (resourceLangId == ggsql || (resourceLangId == sql && config.ggsql.enableSqlFiles)) && !isInDiffEditor" } ], "file/newFile": [ @@ -131,6 +131,28 @@ "group": "ggsql", "when": "!virtualWorkspace" } + ], + "commandPalette": [ + { + "command": "ggsql.sourceCurrentFile", + "when": "isPositron" + }, + { + "command": "ggsql.runQuery", + "when": "isPositron" + }, + { + "command": "ggsql.runCellsAbove", + "when": "isPositron" + }, + { + "command": "ggsql.runNextCell", + "when": "isPositron" + }, + { + "command": "ggsql.runCurrentAdvance", + "when": "isPositron" + } ] }, "configuration": { From 312d269d49a0db069b728ce0db14689c7550540e Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 11:48:00 -0600 Subject: [PATCH 08/18] Scaffold out some initial testing --- ggsql-vscode/.gitignore | 2 + ggsql-vscode/.vscode-test.mjs | 5 + ggsql-vscode/.vscodeignore | 1 + ggsql-vscode/package-lock.json | 2295 +++++++++++++++++- ggsql-vscode/package.json | 14 +- ggsql-vscode/src/test/grammar/highlight.gsql | 3 + ggsql-vscode/src/test/harness.test.ts | 8 + ggsql-vscode/tsconfig.test.json | 14 + 8 files changed, 2274 insertions(+), 68 deletions(-) create mode 100644 ggsql-vscode/.vscode-test.mjs create mode 100644 ggsql-vscode/src/test/grammar/highlight.gsql create mode 100644 ggsql-vscode/src/test/harness.test.ts create mode 100644 ggsql-vscode/tsconfig.test.json diff --git a/ggsql-vscode/.gitignore b/ggsql-vscode/.gitignore index 1fcb1529f..7eaa3d359 100644 --- a/ggsql-vscode/.gitignore +++ b/ggsql-vscode/.gitignore @@ -1 +1,3 @@ out +out-test +.vscode-test/ diff --git a/ggsql-vscode/.vscode-test.mjs b/ggsql-vscode/.vscode-test.mjs new file mode 100644 index 000000000..d24b721c6 --- /dev/null +++ b/ggsql-vscode/.vscode-test.mjs @@ -0,0 +1,5 @@ +import { defineConfig } from '@vscode/test-cli'; + +export default defineConfig({ + files: 'out-test/test/**/*.test.js', +}); diff --git a/ggsql-vscode/.vscodeignore b/ggsql-vscode/.vscodeignore index 4a7a2ca42..04dc50a84 100644 --- a/ggsql-vscode/.vscodeignore +++ b/ggsql-vscode/.vscodeignore @@ -15,3 +15,4 @@ esbuild.js out/**/*.map package-lock.json *.vsix +out-test/** diff --git a/ggsql-vscode/package-lock.json b/ggsql-vscode/package-lock.json index 1c55f9498..dc5829a63 100644 --- a/ggsql-vscode/package-lock.json +++ b/ggsql-vscode/package-lock.json @@ -13,19 +13,34 @@ }, "devDependencies": { "@posit-dev/positron": "^0.2.2", + "@types/mocha": "^10.0.10", "@types/node": "^18.x", "@types/vscode": "^1.75.0", "@typescript-eslint/eslint-plugin": "^8.57.1", "@typescript-eslint/parser": "^8.57.1", + "@vscode/test-cli": "^0.0.15", + "@vscode/test-electron": "^3.1.0", "esbuild": "^0.27.4", "eslint": "^10.0.3", + "mocha": "^11.7.6", "npm-run-all": "^4.1.5", - "typescript": "^5.0.0" + "typescript": "^5.0.0", + "vscode-tmgrammar-test": "^0.1.3" }, "engines": { "vscode": "^1.75.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.4", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", @@ -614,6 +629,129 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@posit-dev/positron": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@posit-dev/positron/-/positron-0.2.2.tgz", @@ -641,6 +779,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -648,6 +793,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "18.19.130", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", @@ -898,6 +1050,89 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@vscode/test-cli": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.15.tgz", + "integrity": "sha512-nAxk2X79wuXS7aOhyFFhFcCqd7EBUoMesu7ZgsYE/4eFjyBMuyIweVE94BxdKH1RieN8eOz2SIrljrZt6Lk9fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mocha": "^10.0.10", + "c8": "^11.0.0", + "chokidar": "^5.0.0", + "enhanced-resolve": "^5.24.0", + "glob": "^13.0.6", + "minimatch": "^10.2.5", + "mocha": "^11.7.6", + "supports-color": "^10.2.2", + "yargs": "^18.0.0" + }, + "bin": { + "vscode-test": "out/bin.mjs" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@vscode/test-cli/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@vscode/test-cli/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-cli/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@vscode/test-electron": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-3.1.0.tgz", + "integrity": "sha512-CRqv5u+YYoseuNVJ6Tyo4k0sF0mx4qnKMihRB0PjsUF8Dc0WKtCXo6CNL6nWWm5esfFQsQA/pejMj4ZbpJVLTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^8.1.0", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=22" + } + }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -921,6 +1156,16 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", @@ -938,6 +1183,19 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -951,6 +1209,13 @@ "node": ">=4" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -1026,6 +1291,13 @@ "node": "18 || 20 || >=22" } }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true, + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", @@ -1039,6 +1311,180 @@ "node": "18 || 20 || >=22" } }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/c8": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-11.0.0.tgz", + "integrity": "sha512-e/uRViGHSVIJv7zsaDKM7VRn2390TgHXqUSvYwPHBQaU6L7E9L0n9JbdkwdYPvshDT0KymBmmlwSpms3yBaMNg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^8.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": "20 || >=22" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/c8/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/c8/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/c8/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/c8/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/c8/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/c8/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/c8/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/c8/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/c8/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/c8/node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -1089,6 +1535,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -1114,23 +1573,93 @@ "node": ">=0.8.0" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true, "license": "MIT" }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1138,6 +1667,20 @@ "dev": true, "license": "MIT" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1225,6 +1768,19 @@ } } }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1268,6 +1824,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1283,6 +1849,34 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -1471,6 +2065,16 @@ "@esbuild/win32-x64": "0.27.4" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1741,6 +2345,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -1778,6 +2392,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1829,6 +2467,29 @@ "node": ">= 0.4" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -1886,6 +2547,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2030,6 +2709,16 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -2037,6 +2726,41 @@ "dev": true, "license": "ISC" }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -2047,6 +2771,13 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2057,6 +2788,25 @@ "node": ">=0.8.19" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -2240,6 +2990,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", @@ -2273,6 +3033,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -2316,6 +3089,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -2415,6 +3208,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -2475,36 +3281,150 @@ "dev": true, "license": "ISC" }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "license": "MIT" + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "license": "MIT" + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keyv": { + "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, @@ -2527,6 +3447,16 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -2550,48 +3480,495 @@ "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mocha": { + "version": "11.7.6", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.6.tgz", + "integrity": "sha512-nS9xOGbw2I3cjCpxwZAEJ9xK9lmJ08vEkQvLtz4du9ZrF9UrjRpeJGiIgl2Z+Qs++pmB4ecDe48Fwsh+j+j7xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/mocha/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mocha/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/mocha/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">= 0.4" - } - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "node_modules/mocha/node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "brace-expansion": "^5.0.2" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=12" } }, "node_modules/ms": { @@ -2812,6 +4189,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -2830,6 +4233,86 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -2880,6 +4363,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -2904,6 +4401,16 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2921,6 +4428,23 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -2934,6 +4458,13 @@ "node": ">=4" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", @@ -2990,6 +4521,23 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -3005,6 +4553,43 @@ "node": ">=4" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -3049,6 +4634,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -3070,6 +4665,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -3090,6 +4702,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -3138,6 +4757,16 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -3187,6 +4816,13 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3299,6 +4935,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -3333,20 +4982,107 @@ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", "dev": true, - "license": "CC0-1.0" + "license": "CC0-1.0" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, "node_modules/string.prototype.padend": { @@ -3427,6 +5163,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -3437,6 +5213,19 @@ "node": ">=4" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -3463,6 +5252,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/test-exclude": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-8.0.0.tgz", + "integrity": "sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^13.0.6", + "minimatch": "^10.2.2" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -3638,6 +5456,28 @@ "dev": true, "license": "MIT" }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -3649,6 +5489,103 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-textmate": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-7.0.4.tgz", + "integrity": "sha512-9hJp0xL7HW1Q5OgGe03NACo7yiCTMEk3WU/rtKXUbncLtdg6rVVNJnHwD88UhbIYU2KoxY0Dih0x+kIsmUKn2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-tmgrammar-test": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/vscode-tmgrammar-test/-/vscode-tmgrammar-test-0.1.3.tgz", + "integrity": "sha512-Wg6Pz+ePAT1O+F/A1Fc4wS5vY2X+HNtgN4qMdL+65NLQYd1/zdDWH4fhwsLjX8wTzeXkMy49Cr4ZqWTJ7VnVxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bottleneck": "^2.19.5", + "chalk": "^2.4.2", + "commander": "^9.2.0", + "diff": "^4.0.2", + "glob": "^7.1.6", + "vscode-oniguruma": "^1.5.1", + "vscode-textmate": "^7.0.1" + }, + "bin": { + "vscode-tmgrammar-snap": "dist/snapshot.js", + "vscode-tmgrammar-test": "dist/unit.js" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-tmgrammar-test/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3764,6 +5701,232 @@ "node": ">=0.10.0" } }, + "node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", + "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/ggsql-vscode/package.json b/ggsql-vscode/package.json index 53adb4725..3282491fd 100644 --- a/ggsql-vscode/package.json +++ b/ggsql-vscode/package.json @@ -180,21 +180,31 @@ "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", "package": "npm run check-types && node esbuild.js --production", "check-types": "tsc --noEmit", - "lint": "eslint src --ext ts" + "lint": "eslint src --ext ts", + "compile-tests": "tsc -p tsconfig.test.json", + "pretest": "npm run compile-tests && npm run package", + "test:grammar": "vscode-tmgrammar-test -g syntaxes/ggsql.tmLanguage.json \"src/test/grammar/*.gsql\"", + "test:extension": "vscode-test", + "test": "npm run test:grammar && npm run test:extension" }, "dependencies": { "toml": "^3.0.0" }, "devDependencies": { "@posit-dev/positron": "^0.2.2", + "@types/mocha": "^10.0.10", "@types/node": "^18.x", "@types/vscode": "^1.75.0", "@typescript-eslint/eslint-plugin": "^8.57.1", "@typescript-eslint/parser": "^8.57.1", + "@vscode/test-cli": "^0.0.15", + "@vscode/test-electron": "^3.1.0", "esbuild": "^0.27.4", "eslint": "^10.0.3", + "mocha": "^11.7.6", "npm-run-all": "^4.1.5", - "typescript": "^5.0.0" + "typescript": "^5.0.0", + "vscode-tmgrammar-test": "^0.1.3" }, "overrides": { "uri-js": "npm:uri-js-replace" diff --git a/ggsql-vscode/src/test/grammar/highlight.gsql b/ggsql-vscode/src/test/grammar/highlight.gsql new file mode 100644 index 000000000..d3353e167 --- /dev/null +++ b/ggsql-vscode/src/test/grammar/highlight.gsql @@ -0,0 +1,3 @@ +-- SYNTAX TEST "source.ggsql" +SELECT +-- <----- keyword.other.ggsql diff --git a/ggsql-vscode/src/test/harness.test.ts b/ggsql-vscode/src/test/harness.test.ts new file mode 100644 index 000000000..422532f24 --- /dev/null +++ b/ggsql-vscode/src/test/harness.test.ts @@ -0,0 +1,8 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; + +suite('harness', () => { + test('the vscode API is reachable from tests', () => { + assert.ok(vscode.version.length > 0); + }); +}); diff --git a/ggsql-vscode/tsconfig.test.json b/ggsql-vscode/tsconfig.test.json new file mode 100644 index 000000000..a27844103 --- /dev/null +++ b/ggsql-vscode/tsconfig.test.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + // Output goes to out-test, not out, so it never overwrites the esbuild bundle at out/extension.js. + "outDir": "out-test", + "rootDir": "src", + "types": ["node", "mocha"] + }, + // Deliberately all of src/, not just src/test/: unit tests import the extension's own + // modules (e.g. `import { parseCells } from '../cellParser'`), which must also be compiled + // into out-test/ for those imports to resolve. + "include": ["src/**/*"] +} From 461df8a5da6a006396d6699ea124ce526acc8ef3 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 12:24:25 -0600 Subject: [PATCH 09/18] Test activation and language resolution --- ggsql-vscode/src/test/activation.test.ts | 60 ++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 ggsql-vscode/src/test/activation.test.ts diff --git a/ggsql-vscode/src/test/activation.test.ts b/ggsql-vscode/src/test/activation.test.ts new file mode 100644 index 000000000..b0b85dafa --- /dev/null +++ b/ggsql-vscode/src/test/activation.test.ts @@ -0,0 +1,60 @@ +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import * as vscode from 'vscode'; + +const EXTENSION_ID = 'ggsql.ggsql'; + +/** + * Opens a real file on disk so the workbench resolves its language from the + * contributed extensions. `openTextDocument({content})` would let the test pick + * the language itself, which is the thing under test here. + */ +async function openFileNamed(name: string, content: string): Promise { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ggsql-test-')); + const file = path.join(dir, name); + fs.writeFileSync(file, content); + return vscode.workspace.openTextDocument(file); +} + +suite('activation', () => { + suiteSetup(async () => { + const extension = vscode.extensions.getExtension(EXTENSION_ID); + assert.ok(extension, `extension ${EXTENSION_ID} not found`); + await extension.activate(); + }); + + test('the extension activates', () => { + const extension = vscode.extensions.getExtension(EXTENSION_ID); + assert.strictEqual(extension?.isActive, true); + }); + + test('ggsql file extensions resolve to the ggsql language', async () => { + for (const name of ['q.gsql', 'q.ggsql', 'q.ggsql.sql']) { + const document = await openFileNamed(name, 'SELECT 1\n'); + assert.strictEqual(document.languageId, 'ggsql', `${name} resolved wrongly`); + } + }); + + test('createNewFile opens an untitled ggsql document', async () => { + await vscode.commands.executeCommand('ggsql.createNewFile'); + const editor = vscode.window.activeTextEditor; + assert.ok(editor, 'no active editor after createNewFile'); + assert.strictEqual(editor.document.languageId, 'ggsql'); + assert.strictEqual(editor.document.isUntitled, true); + }); + + test('run commands are unavailable outside Positron', async () => { + // Every command that executes code is registered after activate() returns + // early when the Positron API is absent, so invoking one here must reject. + await assert.rejects( + () => Promise.resolve(vscode.commands.executeCommand('ggsql.runQuery')), + /not found/i, + ); + }); + + teardown(async () => { + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); +}); From d80d014e09cdf32a49f0fbace16b076f0d2c9e15 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 13:27:45 -0600 Subject: [PATCH 10/18] Test cell parsing --- ggsql-vscode/src/test/cellParser.test.ts | 74 ++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 ggsql-vscode/src/test/cellParser.test.ts diff --git a/ggsql-vscode/src/test/cellParser.test.ts b/ggsql-vscode/src/test/cellParser.test.ts new file mode 100644 index 000000000..4d73bcd79 --- /dev/null +++ b/ggsql-vscode/src/test/cellParser.test.ts @@ -0,0 +1,74 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { parseCells } from '../cellParser'; + +function ggsqlDoc(content: string): Thenable { + return vscode.workspace.openTextDocument({ content, language: 'ggsql' }); +} + +suite('parseCells', () => { + test('a document with no markers yields no cells', async () => { + // parseCells only opens a cell at a marker, so a marker-free document has + // none. Callers rely on this: extension.ts falls back to running the whole + // document when cells is empty. + const document = await ggsqlDoc('SELECT 1\nVISUALISE *\n'); + assert.deepStrictEqual(parseCells(document), []); + }); + + test('each marker starts a cell and is excluded from its text', async () => { + const document = await ggsqlDoc( + '-- %%\nSELECT 1\n-- %%\nSELECT 2\n-- %%\nSELECT 3\n', + ); + const cells = parseCells(document); + assert.strictEqual(cells.length, 3); + assert.deepStrictEqual( + cells.map(c => c.text), + ['SELECT 1', 'SELECT 2', 'SELECT 3'], + ); + }); + + test('a marker with no body is dropped', async () => { + const document = await ggsqlDoc('-- %%\n-- %%\nSELECT 2\n'); + const cells = parseCells(document); + assert.strictEqual(cells.length, 1); + assert.strictEqual(cells[0].text, 'SELECT 2'); + }); + + test('a cell whose body is only whitespace is dropped', async () => { + // The marker opens a cell, so this exercises the trim-and-drop path in + // extractCellText (the whitespace body trims to '' and is filtered out), + // unlike a document with no marker at all, which never reaches that code. + const document = await ggsqlDoc('-- %%\n \n\t\n'); + assert.deepStrictEqual(parseCells(document), []); + }); + + test('the marker is recognised with and without a space', async () => { + const document = await ggsqlDoc('--%%\nSELECT 1\n-- %%\nSELECT 2\n'); + const cells = parseCells(document); + assert.strictEqual(cells.length, 2); + assert.deepStrictEqual(cells.map(c => c.text), ['SELECT 1', 'SELECT 2']); + }); + + test('cell ranges span from the marker to the line before the next', async () => { + const document = await ggsqlDoc('-- %%\nSELECT 1\nSELECT 2\n-- %%\nSELECT 3\n'); + const cells = parseCells(document); + assert.strictEqual(cells.length, 2); + assert.strictEqual(cells[0].range.start.line, 0); + assert.strictEqual(cells[0].range.end.line, 2); + assert.strictEqual(cells[1].range.start.line, 3); + // The document's trailing newline gives it a final empty line (line 5). + // parseCells closes the last cell at document.lineCount - 1 + // (src/cellParser.ts:59), so the last cell's range extends to that + // trailing empty line, not to the line holding "SELECT 3" (line 4). + assert.strictEqual(cells[1].range.end.line, 5); + }); + + test('content before the first marker is ignored', async () => { + // currentStart is undefined until the first marker, so leading content is + // not part of any cell. + const document = await ggsqlDoc('SELECT 0\n-- %%\nSELECT 1\n'); + const cells = parseCells(document); + assert.strictEqual(cells.length, 1); + assert.strictEqual(cells[0].text, 'SELECT 1'); + }); +}); From 2fed73632db3e013993c61cbf6e47b22a0da82cf Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 13:28:59 -0600 Subject: [PATCH 11/18] Fix comment to be accurate --- ggsql-vscode/src/cellParser.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ggsql-vscode/src/cellParser.ts b/ggsql-vscode/src/cellParser.ts index ae27a8833..9a2b47ee1 100644 --- a/ggsql-vscode/src/cellParser.ts +++ b/ggsql-vscode/src/cellParser.ts @@ -52,7 +52,9 @@ export function parseCells(document: vscode.TextDocument): Cell[] { } } - // Close the last cell (or treat entire document as one cell if no markers) + // Close the final cell. A document with no markers opened none, so it yields no + // cells at all; callers such as `sourceCurrentFile` treat that as "run the whole + // document" rather than relying on this function to synthesise a cell. if (currentStart !== undefined) { const range = new vscode.Range( new vscode.Position(currentStart, 0), From 17bf4e8e63a9cf88d438caffd5375f57524e583f Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 13:37:45 -0600 Subject: [PATCH 12/18] Test `.sql` file gating --- ggsql-vscode/src/test/languages.test.ts | 51 +++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 ggsql-vscode/src/test/languages.test.ts diff --git a/ggsql-vscode/src/test/languages.test.ts b/ggsql-vscode/src/test/languages.test.ts new file mode 100644 index 000000000..b68c1826d --- /dev/null +++ b/ggsql-vscode/src/test/languages.test.ts @@ -0,0 +1,51 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { isGgsqlDocument, sqlFilesEnabled } from '../languages'; + +function setSqlFiles(value: boolean | undefined): Thenable { + return vscode.workspace + .getConfiguration('ggsql') + .update('enableSqlFiles', value, vscode.ConfigurationTarget.Global); +} + +function doc(language: string): Thenable { + return vscode.workspace.openTextDocument({ content: 'SELECT 1\n', language }); +} + +suite('isGgsqlDocument', () => { + teardown(async () => { + await setSqlFiles(undefined); + }); + + test('defaults to attaching to sql files', async () => { + // Asserts the shipped default, so it must clear any value a previous + // test or file may have left behind rather than rely on teardown alone. + await setSqlFiles(undefined); + assert.strictEqual(sqlFilesEnabled(), true); + }); + + test('a ggsql document always qualifies', async () => { + const document = await doc('ggsql'); + await setSqlFiles(false); + assert.strictEqual(isGgsqlDocument(document), true); + }); + + test('a sql document qualifies only while enableSqlFiles is on', async () => { + const document = await doc('sql'); + + await setSqlFiles(true); + assert.strictEqual(isGgsqlDocument(document), true); + + await setSqlFiles(false); + assert.strictEqual(isGgsqlDocument(document), false); + }); + + test('an unrelated language never qualifies', async () => { + const document = await doc('plaintext'); + assert.strictEqual(isGgsqlDocument(document), false); + }); + + test('undefined does not qualify', () => { + assert.strictEqual(isGgsqlDocument(undefined), false); + }); +}); From 1dc9991ca3d755b0f301504f3e65131136694cab Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 13:44:37 -0600 Subject: [PATCH 13/18] Test code lens placement --- ggsql-vscode/src/test/codelens.test.ts | 66 ++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 ggsql-vscode/src/test/codelens.test.ts diff --git a/ggsql-vscode/src/test/codelens.test.ts b/ggsql-vscode/src/test/codelens.test.ts new file mode 100644 index 000000000..99136aa60 --- /dev/null +++ b/ggsql-vscode/src/test/codelens.test.ts @@ -0,0 +1,66 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { GgsqlCodeLensProvider } from '../codelens'; + +function setSqlFiles(value: boolean | undefined): Thenable { + return vscode.workspace + .getConfiguration('ggsql') + .update('enableSqlFiles', value, vscode.ConfigurationTarget.Global); +} + +function docOf(content: string, language = 'ggsql'): Thenable { + return vscode.workspace.openTextDocument({ content, language }); +} + +suite('GgsqlCodeLensProvider', () => { + let disposables: vscode.Disposable[]; + let provider: GgsqlCodeLensProvider; + + setup(() => { + disposables = []; + provider = new GgsqlCodeLensProvider(disposables); + }); + + teardown(async () => { + disposables.forEach(d => d.dispose()); + await setSqlFiles(undefined); + }); + + test('a single cell gets only Run Query', async () => { + const document = await docOf('-- %%\nSELECT 1\n'); + const titles = provider.provideCodeLenses(document).map(l => l.command?.title); + assert.deepStrictEqual(titles, ['$(run) Run Query']); + }); + + test('the first cell has no Run Above and the last no Run Next', async () => { + const document = await docOf('-- %%\nSELECT 1\n-- %%\nSELECT 2\n-- %%\nSELECT 3\n'); + const lenses = provider.provideCodeLenses(document); + + const byLine = new Map(); + for (const lens of lenses) { + const line = lens.range.start.line; + byLine.set(line, [...(byLine.get(line) ?? []), lens.command!.title]); + } + + assert.deepStrictEqual(byLine.get(0), ['$(run) Run Query', 'Run Next']); + assert.deepStrictEqual(byLine.get(2), ['$(run) Run Query', 'Run Above', 'Run Next']); + assert.deepStrictEqual(byLine.get(4), ['$(run) Run Query', 'Run Above']); + }); + + test('each lens carries its cell start line as the command argument', async () => { + const document = await docOf('-- %%\nSELECT 1\n-- %%\nSELECT 2\n'); + for (const lens of provider.provideCodeLenses(document)) { + assert.deepStrictEqual(lens.command?.arguments, [lens.range.start.line]); + } + }); + + test('sql documents get no lenses while enableSqlFiles is off', async () => { + const document = await docOf('-- %%\nSELECT 1\n', 'sql'); + + await setSqlFiles(true); + assert.ok(provider.provideCodeLenses(document).length > 0); + + await setSqlFiles(false); + assert.deepStrictEqual(provider.provideCodeLenses(document), []); + }); +}); From 65d9ae8cb82df3c59cefbf5f51073ced340fa6b7 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 14:28:47 -0600 Subject: [PATCH 14/18] Test TextMate scopes for core syntax --- ggsql-vscode/src/test/grammar/highlight.gsql | 44 +++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/ggsql-vscode/src/test/grammar/highlight.gsql b/ggsql-vscode/src/test/grammar/highlight.gsql index d3353e167..eff732374 100644 --- a/ggsql-vscode/src/test/grammar/highlight.gsql +++ b/ggsql-vscode/src/test/grammar/highlight.gsql @@ -1,3 +1,45 @@ -- SYNTAX TEST "source.ggsql" -SELECT + +-- a leading comment +-- <------------------- comment.line.double-dash.ggsql + +SELECT date, revenue, region FROM sales WHERE year = 2024 -- <----- keyword.other.ggsql +-- ^^^^ keyword.other.ggsql +-- ^^^^^ keyword.other.ggsql +-- ^^^^ constant.numeric.ggsql + +VISUALISE date AS x, revenue AS y, region AS color +-- <-------- keyword.other.ggsql +-- ^^ keyword.other.ggsql +-- ^ support.type.aesthetic.ggsql +-- ^^ keyword.other.ggsql +-- ^ support.type.aesthetic.ggsql +-- ^^ keyword.other.ggsql +-- ^^^^^ support.type.aesthetic.ggsql + +DRAW point +-- <--- keyword.other.ggsql +-- ^^^^^ support.type.geom.ggsql + +DRAW line +-- ^^^^ support.type.geom.ggsql + +DRAW bar +-- ^^^ support.type.geom.ggsql + +SCALE DISCRETE color TO viridis +-- <---- keyword.other.ggsql +-- ^^^^^^^^ keyword.control.scale-modifier.ggsql +-- ^^^^^^^ constant.language.scale-type.ggsql + +SCALE y VIA log10 +-- ^^^^^ constant.language.scale-type.ggsql + +LABEL title => 'Sales by Region', x => 'Date', y => 'Revenue' +-- <---- keyword.other.ggsql +-- ^^^^^ support.type.property.ggsql +-- ^^ keyword.operator.fatarrow.ggsql +-- ^^^^^^^^^^^^^^^^^ string.quoted.single.ggsql +-- ^ support.type.property.ggsql +-- ^ support.type.property.ggsql From 8941046d7df2c0544041376c1e1a033e24b7e851 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 14:39:40 -0600 Subject: [PATCH 15/18] Test FACET clause scopes --- ggsql-vscode/src/test/grammar/highlight.gsql | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ggsql-vscode/src/test/grammar/highlight.gsql b/ggsql-vscode/src/test/grammar/highlight.gsql index eff732374..09e24608f 100644 --- a/ggsql-vscode/src/test/grammar/highlight.gsql +++ b/ggsql-vscode/src/test/grammar/highlight.gsql @@ -43,3 +43,8 @@ LABEL title => 'Sales by Region', x => 'Date', y => 'Revenue' -- ^^^^^^^^^^^^^^^^^ string.quoted.single.ggsql -- ^ support.type.property.ggsql -- ^ support.type.property.ggsql + +FACET region SETTING free => 'y', missing => 'repeat' +-- <---- keyword.other.ggsql +-- ^^^^ support.type.property.ggsql +-- ^^^^^^^ support.type.property.ggsql From 539b3aef68f97d4bf5df42080a24856829cadc10 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 15:13:39 -0600 Subject: [PATCH 16/18] Strengthen extension tests after scrutiny pass --- ggsql-vscode/src/languages.ts | 10 +++++++++- ggsql-vscode/src/test/codelens.test.ts | 10 +++++++--- ggsql-vscode/src/test/grammar/highlight.gsql | 2 -- ggsql-vscode/src/test/harness.test.ts | 8 -------- ggsql-vscode/src/test/languages.test.ts | 4 ++++ 5 files changed, 20 insertions(+), 14 deletions(-) delete mode 100644 ggsql-vscode/src/test/harness.test.ts diff --git a/ggsql-vscode/src/languages.ts b/ggsql-vscode/src/languages.ts index bdc028346..5c1d41e5a 100644 --- a/ggsql-vscode/src/languages.ts +++ b/ggsql-vscode/src/languages.ts @@ -25,7 +25,15 @@ export const SQL_LANGUAGE_ID = 'sql'; */ export const CELL_LANGUAGE_IDS = [GGSQL_LANGUAGE_ID, SQL_LANGUAGE_ID]; -/** Whether ggsql offers its run affordances in plain `.sql` files. */ +/** + * Whether ggsql offers its run affordances in plain `.sql` files. + * + * The effective default in practice is the `ggsql.enableSqlFiles` default declared in + * `package.json`'s configuration schema, not the fallback argument below: once a setting + * is registered there, VS Code resolves `.get()` against that schema default before + * falling back to the argument passed here. The `true` below is defensive documentation + * for the (registered) default, not the value that actually applies. + */ export function sqlFilesEnabled(): boolean { return vscode.workspace .getConfiguration('ggsql') diff --git a/ggsql-vscode/src/test/codelens.test.ts b/ggsql-vscode/src/test/codelens.test.ts index 99136aa60..294f421be 100644 --- a/ggsql-vscode/src/test/codelens.test.ts +++ b/ggsql-vscode/src/test/codelens.test.ts @@ -49,9 +49,13 @@ suite('GgsqlCodeLensProvider', () => { test('each lens carries its cell start line as the command argument', async () => { const document = await docOf('-- %%\nSELECT 1\n-- %%\nSELECT 2\n'); - for (const lens of provider.provideCodeLenses(document)) { - assert.deepStrictEqual(lens.command?.arguments, [lens.range.start.line]); - } + // Expected values are written out literally (not read from each lens's own + // range) because comparing a lens's argument to its own range still passes + // even if every cell start line is uniformly wrong upstream. The two cells + // start on lines 0 and 2; in push order that is Run Query/Run Next for the + // first cell, then Run Query/Run Above for the second. + const args = provider.provideCodeLenses(document).map(l => l.command?.arguments); + assert.deepStrictEqual(args, [[0], [0], [2], [2]]); }); test('sql documents get no lenses while enableSqlFiles is off', async () => { diff --git a/ggsql-vscode/src/test/grammar/highlight.gsql b/ggsql-vscode/src/test/grammar/highlight.gsql index 09e24608f..6b2e64d3b 100644 --- a/ggsql-vscode/src/test/grammar/highlight.gsql +++ b/ggsql-vscode/src/test/grammar/highlight.gsql @@ -13,9 +13,7 @@ VISUALISE date AS x, revenue AS y, region AS color -- <-------- keyword.other.ggsql -- ^^ keyword.other.ggsql -- ^ support.type.aesthetic.ggsql --- ^^ keyword.other.ggsql -- ^ support.type.aesthetic.ggsql --- ^^ keyword.other.ggsql -- ^^^^^ support.type.aesthetic.ggsql DRAW point diff --git a/ggsql-vscode/src/test/harness.test.ts b/ggsql-vscode/src/test/harness.test.ts deleted file mode 100644 index 422532f24..000000000 --- a/ggsql-vscode/src/test/harness.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as assert from 'assert'; -import * as vscode from 'vscode'; - -suite('harness', () => { - test('the vscode API is reachable from tests', () => { - assert.ok(vscode.version.length > 0); - }); -}); diff --git a/ggsql-vscode/src/test/languages.test.ts b/ggsql-vscode/src/test/languages.test.ts index b68c1826d..1c45badbd 100644 --- a/ggsql-vscode/src/test/languages.test.ts +++ b/ggsql-vscode/src/test/languages.test.ts @@ -20,6 +20,10 @@ suite('isGgsqlDocument', () => { test('defaults to attaching to sql files', async () => { // Asserts the shipped default, so it must clear any value a previous // test or file may have left behind rather than rely on teardown alone. + // This pins the `ggsql.enableSqlFiles` default declared in package.json's + // configuration schema, not sqlFilesEnabled()'s own fallback argument: VS Code + // resolves `.get()` against the registered schema default first, so a change to + // the code-level fallback alone would not turn this test red. await setSqlFiles(undefined); assert.strictEqual(sqlFilesEnabled(), true); }); From 54c01ad4435bd59f7476c1b99d219af8a7b94a0e Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 15:42:46 -0600 Subject: [PATCH 17/18] Add GH action to run extension tests --- .github/workflows/test-extension.yaml | 44 ++++++++++++++++++++++++ ggsql-vscode/.vscode-test.mjs | 1 + ggsql-vscode/.vscodeignore | 2 ++ ggsql-vscode/CLAUDE.md | 31 ++++++++++++++++- ggsql-vscode/src/test/activation.test.ts | 10 ++++++ 5 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test-extension.yaml diff --git a/.github/workflows/test-extension.yaml b/.github/workflows/test-extension.yaml new file mode 100644 index 000000000..2816e727e --- /dev/null +++ b/.github/workflows/test-extension.yaml @@ -0,0 +1,44 @@ +name: Test Extension + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + # Runs the suite against stock VS Code. A sibling job will run the same + # extension against Positron, which covers the language runtime, connection + # drivers and cell execution that stock VS Code cannot reach. + test-extension: + runs-on: ubuntu-latest + name: Test (VS Code) + defaults: + run: + working-directory: ggsql-vscode + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: npm + cache-dependency-path: ggsql-vscode/package-lock.json + + - name: Install XVFB + # The extension tests drive a real VS Code instance, which needs a + # display. The grammar tests do not, but run under the same command. + run: sudo apt-get -y update && sudo apt-get -y install xvfb + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint + + - name: Run tests + run: xvfb-run -a npm test diff --git a/ggsql-vscode/.vscode-test.mjs b/ggsql-vscode/.vscode-test.mjs index d24b721c6..a6053b90a 100644 --- a/ggsql-vscode/.vscode-test.mjs +++ b/ggsql-vscode/.vscode-test.mjs @@ -2,4 +2,5 @@ import { defineConfig } from '@vscode/test-cli'; export default defineConfig({ files: 'out-test/test/**/*.test.js', + mocha: { timeout: 5000 }, }); diff --git a/ggsql-vscode/.vscodeignore b/ggsql-vscode/.vscodeignore index 04dc50a84..8dcb9cb3c 100644 --- a/ggsql-vscode/.vscodeignore +++ b/ggsql-vscode/.vscodeignore @@ -4,6 +4,7 @@ .yarnrc vsc-extension-quickstart.md **/tsconfig.json +tsconfig.test.json **/.eslintrc.json **/*.map **/*.ts @@ -16,3 +17,4 @@ out/**/*.map package-lock.json *.vsix out-test/** +.vscode-test.mjs diff --git a/ggsql-vscode/CLAUDE.md b/ggsql-vscode/CLAUDE.md index 1337ab7b9..23847792f 100644 --- a/ggsql-vscode/CLAUDE.md +++ b/ggsql-vscode/CLAUDE.md @@ -22,7 +22,9 @@ ggsql-vscode/ │ ├── codelens.ts "▶ Run cell" lens above each cell │ ├── decorations.ts Cell separator decorations │ ├── context.ts Sets editor context keys (e.g. ggsql.hasCodeCells) -│ └── types.ts Shared interfaces +│ ├── sqlAssociation.ts One-time notice pointing at files.associations for .sql highlighting +│ ├── types.ts Shared interfaces +│ └── test/ Mocha suites (unit + activation) and the grammar fixture ├── syntaxes/ │ └── ggsql.tmLanguage.json TextMate grammar (used for tokenization in VS Code) ├── examples/ Sample .ggsql files @@ -119,6 +121,33 @@ code --install-extension ggsql-.vsix Watch mode for development: `npm run watch` (runs esbuild + tsc in parallel). +## Testing + +```sh +cd ggsql-vscode +npm test # grammar scopes, then the VS Code suites +npm run test:grammar # TextMate scopes only; no Electron, fast +npm run test:extension +``` + +Tests live in `src/test/` and compile to `out-test/` via `tsconfig.test.json`, deliberately not to `out/`, which `esbuild.js` owns. The whole of `src/` compiles there, not just `src/test/`, because the unit tests import the extension's own modules. `@vscode/test-cli` launches a real VS Code instance, so a window appears while the suites run; CI wraps the same command in `xvfb-run`. + +Note that `tsc` does not prune output for deleted sources: if you delete or rename a test, remove its `.js` and `.js.map` from `out-test/test/` or the runner keeps executing the stale copy. `npm run test:extension` on its own does not recompile, so run `npm test` (or `npm run compile-tests` first) after editing any `.ts`. + +The suites cover the extension as stock VS Code sees it: activation, language resolution, cell parsing, `.sql` gating, CodeLens placement and TextMate scopes. The Positron surface (runtime manager, connection drivers, cell execution) is not covered, since it needs a Positron host. `sqlAssociation.ts`, `manager.ts` and `connections.ts` are also untested. + +Add new tests as `src/test/.test.ts`; no config change is needed. + +### Editing the grammar fixture + +`src/test/grammar/highlight.gsql` uses `vscode-tmgrammar-test`'s annotation format, which has three rules worth knowing before you touch it: + +- The header must be exactly `-- SYNTAX TEST "source.ggsql"`. A trailing `>>`, which some examples show, makes the tool reject the file with a parse error rather than an assertion failure. +- A `^` caret's column is the comment token length plus its index in the assertion line, matched against the source line's 0-based columns. Carets therefore cannot target source columns 0 and 1, which is why the `<---` form exists for line-initial tokens. +- For `<---`, the dash count sets the assertion's right edge from column 0. It must be at least 1 and no more than the target token's length, so it is not cosmetic. + +Assertion lines are stripped before tokenization, so they never tokenize as ggsql comments. + ## See also - [`/CLAUDE.md`](../CLAUDE.md) — workspace overview. diff --git a/ggsql-vscode/src/test/activation.test.ts b/ggsql-vscode/src/test/activation.test.ts index b0b85dafa..21fa1ee53 100644 --- a/ggsql-vscode/src/test/activation.test.ts +++ b/ggsql-vscode/src/test/activation.test.ts @@ -6,6 +6,9 @@ import * as vscode from 'vscode'; const EXTENSION_ID = 'ggsql.ggsql'; +// Directories created by openFileNamed, removed in suiteTeardown below. +const tempDirs: string[] = []; + /** * Opens a real file on disk so the workbench resolves its language from the * contributed extensions. `openTextDocument({content})` would let the test pick @@ -13,6 +16,7 @@ const EXTENSION_ID = 'ggsql.ggsql'; */ async function openFileNamed(name: string, content: string): Promise { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ggsql-test-')); + tempDirs.push(dir); const file = path.join(dir, name); fs.writeFileSync(file, content); return vscode.workspace.openTextDocument(file); @@ -57,4 +61,10 @@ suite('activation', () => { teardown(async () => { await vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); + + suiteTeardown(() => { + for (const dir of tempDirs) { + fs.rmSync(dir, { recursive: true, force: true }); + } + }); }); From 44132d2ce55a2a39717e8baed8521bff3ef5f22a Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Sat, 1 Aug 2026 15:43:02 -0600 Subject: [PATCH 18/18] Update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 1432765b6..37e2531c2 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,6 @@ ggsql.Rcheck/ *.dmg *.AppImage .cargo-packager/ + +# Superpowers SDD scratch (ledger, briefs, review packages) +.superpowers/