diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 186459d..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "dbaeumer.vscode-eslint", - "ms-vscode.extension-test-runner" - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index da72561..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,11 +0,0 @@ -// Place your settings in this file to overwrite default and user settings. -{ - "files.exclude": { - "out": false // set this to true to hide the "out" folder with the compiled JS files - }, - "search.exclude": { - "out": true // set this to false to include "out" folder in search results - }, - // Turn off tsc task auto detection since we have the necessary tasks as npm scripts - "typescript.tsc.autoDetect": "on" -} diff --git a/.vscodeignore b/.vscodeignore index 005d560..166bbb2 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -4,6 +4,7 @@ out/**.map resources/** scripts/** !resources/logo.png +dev/* tsconfig.json !file.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c6d083..c5403e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,216 @@ All notable changes to the "PostgreSQL Hacker Helper" extension will be document The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.9.0] + +### Added + +Show elements of Hash Tables: `HTAB` and `simplehash` (code gen from `lib/simplehash.c`). + +User can specify types of elements of custom `HTAB`/`simplehash` in configuration files. + +Add basic snippets: `IsA`, `foreach`, `PG_TRY`/`PG_CATCH`/`PG_FINALLY`. + +Add node tags from PG18beta1. + +Add `join_rel_level` to builtin array special members. + +## [1.8.2] + +### Fix + +`Bitmapset` elements stop iterating when encounters `0`. + +### Changed + +Do not show `=` in variables view. + +## [1.8.1] + +### Fix + +`Dump Node to stdout` command not worked, because `frameId` not passed for evaluation. + +## [1.8.0] + +### Added + +Add variable from PG Variables to `Watch` view in debug view container. + +### Fix + +Copy logic of `get_rte_attribute_name` function to get name of attribute for `Var` representation to prevent possible `ERROR` exceptions. + +## [1.7.1] + +### Fix + +Binary features info was cached only for current debug session and invalidated on debugger step. +Now it used the same for whole debug session. + +Typedef logic not worked correctly for `Node` variables. Specifically, `MemoryContext` - it was shown as `MemoryContext` even if it's `type` is `AllocSetContext`. + +### Changed + +Do not show `List` as expandable in PG Variables, if it is empty (`NIL`). + +Do not show `words` member for `Bitmapset`. + +Add more validity checking for `Bitmapset`: check `NodeTag` (for 16+ version) and pointer validity (for older versions). + +## [1.7.0] + +### Added + +Support for `Value` structs with different NodeTags (for old versions) - show actual value according to it's NodeTag (not whole union, but only required field). + +Custom pointer types for `List *`. Earlier, all `T_List` elements were casted to `Node *`, now some `List *` variables/members have elements with their real stored types. +I.e. `RestrictInfo->scansel_cache` is a `List *` that stores `MergeScanSelCache` structs (it is not Node). +Now such `List`s displayed with their type. + +User can define their own custom pointer types using configuration file. Configuration file layout updated (new field for this feature), current version 4. + +Add more checks when working with system catalog or some other functions. I.e. Oid/pointer checks, `MemoryContext` checks when allocating memory, etc... + +### Fix + +Memory leaks when evaluating `Expr` representation (pointers got from `get_func_name` and `get_opname` were not `pfree`'d). + +Caching of `Expr` representations worked incorrectly. That led to performance decrease (and more memory leaks). + +### Changed + +Track some binary features of postgres instance in current debug session. I.e. whether it have `bms_next_member` or not. (performance feature) + +Do not show `initial_elements`, `head` and `tail` members of `List *`. + +## [1.6.1] + +### Fix + +Errors messages appearing in bottom right corner when making debugger steps too fast and 'PG Variables' view interrupted during step. + +## [1.6.0] + +### Added + +Show text representation of expressions in variables (separate pseudo-member `$expr$`). + +Show text representation of expressions contained in `TargetEntry`, `EquivalenceMember` and `RestrictInfo` in description field (old pointer value). + +## [1.5.1] + +### Added + +Update configuration file contents created by command. Set version to 3 and `aliases` member with empty array. + +## [1.5.0] + +> Pre-release + +### Added + +Add `typedefs` setting to configuration file. This allows to set custom typedefs.list file for using by formatter. This is helpful when you working on patch which changes this file and you do not want to constantly modify global cached (by extension) typedefs file. +This also updates version of configuration file to 3. + +## [1.4.5] + +No changes + +## [1.4.4] + +Add `typedefs.list` file preprocessing for feeding to `pg_bsd_indent`. Processed file saved in `/tmp/pg-hacker-helper.typedefs.list` file and may be reused between different sessions. Without processing raw file (downloaded from buildfarm) `pg_bsd_indent` produces invalid formatted code. + +## [1.4.3] + +Add pre and post steps around running `pg_bsd_indent` to handle some formatting cases, like single space between type name and `*`. Relates to bug [#3](https://github.com/ashenBlade/postgres-dev-helper/issues/3). + +## [1.4.2] + +### Fixed + +Fix `pg_bsd_indent Path` setting not handled due to code constants misuse. Relates to bug [#2](https://github.com/ashenBlade/postgres-dev-helper/issues/2). + +## [1.4.1] + +### Added + +Add extension files bootstrapping: Makefile, \*.c, \*.sql, \*.control, tests. +To create extension this way need to run command `Bootstrap extension` from +command palette. + +Support for fixed size array expansion. Type of field must be in form `type[size]`. + +### Fixed + +Fix invalid struct detection in variables view: top level structs are not +expandable. + +Fix variable length arrays fields displayed as expandable. I.e. for `List` last +field previously was displayed as expandable, but there are no elements shown. + +## [1.4.0] + +### Added + +- Support for custom PostgreSQL source code directories. It can be set using `postgresql-hacker-helper.srcPath` in settings. + This setting is used to start searching for required files (i.e. default node tag files or `pg_bsd_indent`). + NOTE: if custom NodeTag files are set with relative path - search start from *workspace* directory, not your custom `srcPath`. + +### Fixed + +- Invalid message formatting for VS Code greater than 1.74.0. Caused by incompatible (with extension's) formatting logic used. + +## [1.3.0] + +### Added + +- Formatting using `pg_bsd_indent` with `Format Document` VS Code functionality. + Supported for PostgreSQL starting from 10 version. + +- Show elements to which `Bitmapset` and `Relids` points: Relids (`RangeTblEntry` and `RelOptInfo`) or index in array (show element of that array) + +### Changed + +- Integration with VS Code logging (output channel). Now can specify log level in `Output` window. + +## [1.2.1] + +### Changed + +- `aliases` parameter in configuration file now works for every type, not only + Node types. + +### Fixed + +- Check breakpoint in `bms_first_member` function to avoid infinite loop when + evaluating bms elements. +- Add support for `MemoryContext` Node + +## [1.2.0] + +### Added + +- Compatibility with PostgreSQL starting from **8.0** version. This includes: + - Linked List implementation of `List` + - `Bitmapset` traversal using `bms_first_member` and temp object + - `Bitmapset` handling for versions up to 16, when it was not Node type +- Compatibility with VS Code versions starting from **1.30**. But some features + can be unaccessible due to API incompatibility. + + In example some versions do not have `Dump Node to stdout` in variables debug + context menu. +- Add more NodeTags - searched from version 8.0 to 17. Current amount - 558. + +### Fixed + +- Log level not updated until restart of extension (or VS Code). +- Invalid Node casting when declared type has `struct` qualifier and NodeTag + type do not have it. + + Example: `typedef JoinPath NestJoin` (for versions up to 14) - failed to show + members when declared type is `struct Path *`. + ## [1.1.2] ### Fixed diff --git a/README.md b/README.md index a7d5381..d7e8549 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ ![Logo](resources/logo.png) -This is a Visual Studio Code extension to assist PostgreSQL source code developers. -It allows to investigate `Node *` variables to obtain it's real type based on `NodeTag` -and provide some other utilities. +This is a Visual Studio Code extension to assist PostgreSQL source code +developers. It allows to investigate `Node *` variables to obtain it's real type +based on `NodeTag` and provide some other utilities. ## Features @@ -17,45 +17,127 @@ They appear in separate action view. ![Overview of extension](resources/overview.gif) -It behaves like Debug->Variables view, but no colorization (limitations of VS Code Extension framework) and automatically detects real type of `Node *` variables. +It behaves like Debug->Variables view, but no colorization (limitations of VS +Code Extension framework) and automatically detects real type of `Node *` +variables. -Also, there are intrinsics for some types: +### Show contents of containers -- `List` elements are displayed according their types +Extension support showing contents of containers: `List` (including Oid, TransactionId, int and custom user pointer types) and `Bitmapset`. ![List * expansion](resources/list.gif) -- Support for special members like `PlannerInfo->simple_rel_array` - array is displayed using it's length +`Bitmapset` elements are displayed: -![Planner expansion](resources/planner.gif) +- `$elements$` - elements of set (array of integers) +- `$length$` - number of entries in set -Currently, there are 36 registered special members, but you can add your own using [pgsql_hacker_helper.json](#pgsql_hacker_helperjson) configuration file. +![Bitmapset expansion](resources/bitmapset.gif) -- `Bitmapset` elements with total lengths are displayed: `$elements$` - pseudo-member +Also, there is support for C-arrays (like `PlannerInfo->simple_rel_array`) - array is displayed using it's length. -![Bitmapset expansion](resources/bitmapset.gif) +![Array special member](resources/array-special-member.gif) + +Currently, there are 36 registered array members, but you can add your own using [pgsql_hacker_helper.json](#pgsql_hacker_helperjson) configuration file. + +Another containers - Hash Tables. There is support for both `HTAB *` and `simplehash` (from `lib/simplehash.h`) - you can see their elements in separate `$elements$` member. + +![TIDBitmap simplehash elements](resources/simplehash.gif) + +> NOTE: most `HTAB *` stored in `static` variables, so they are not shown in variables UI +> +> NOTE2: simplehashes have limitation due to compiler unused symbol pruning optimization (more in [configuration file documentation](./docs/config_file.md)) + +### Show where Bitmapset references + +`Bitmapset` and `Relids` often store indexes of other elements in other places. +Extension knows 53 such elements. I.e. `PlannerInfo->all_relids` or `RelOptInfo->eclass_indexes`. + +![Bitmapset references](resources/bitmapset-refs.gif) + +### Show Expr in their text representation + +In members of `Expr` nodes you can see their text representation. +I.e. for `OpExpr` you will see something like `a.x = 1`. +This works for most of `Exprs`, except *bulky* (`SubPlan`, `Case`, ...). + +![Show Exprs text representation](./resources/expr_repr.gif) + +Also, there are shortcuts for: `EquivalenceMember`, `RestrictInfo` and `TargetEntry`. +Their expressions are displayed right after their member, so you will not have to keep opening and closing variables to see what's inside. +A quick glance will make it clear what's inside! + +![Expressions of equivalence members displayed immediately](./resources/ec_members_exprs.png) + +> NOTE: not all and not always `Expr`s will be displayed. +> Some of subtypes just not supported (i.e. `SubPlan` or `Case`). +> Also, for displaying representation it's required to have range table. +> In such cases placeholder is displayed. ### Dump `Node *` state to log -In PostgreSQL there is `pprint(Node *)` which dumps passed Node variable to stdout with pretty printing it. -Using 'Dump Node to log' option in variable context menu you also will be able to do so. +In PostgreSQL there is `pprint(Node *)` which dumps passed Node variable to +stdout with pretty printing it. Using 'Dump Node to log' option in variable +context menu you also will be able to do so. ![call pprint](resources/dump.gif) +### Formatting + +Extension uses `pgindent` for formatting C code. It integrates with VS Code +extension and available with `Format Document` or `Ctrl + Shift + I` shortcut +(or another key binding if overridden). + +To enable this set formatter for C in settings (i.e. `.vscode/settings.json` +for workspace): + +```json +{ + "[c]": { + "editor.defaultFormatter": "ash-blade.postgresql-hacker-helper" + } +} +``` + +Or specify formatter manually using `Format Document With...`. Select +`PostgreSQL Hacker Helper` in pick up box. + +![Formatter work](resources/formatter-work.gif) + +Feature supported for PostgreSQL starting from 10 version. + +> This feature using tools from `src/tools`. If they are unavailable extension +> will try to build or download them. +> +> Primary tool required is `pg_bsd_indent`. If PostgreSQL version lower than +> 16 extension will ask you for `pg_config` path - it is required to build +> `pg_bsd_indent`. +> Look for warning message from extension in left bottom corner. + +Using command `PgSQL: Show diff preview for PostgreSQL formatter` you can +preview changes made by formatter. + +### Extension bootstrapping + +Extension can help with creation of basic PostgreSQL extension files: Makefile, +control file, source files (C, SQL) and tests. + +Just run command `Bootstrap extension` and enter initial values (extension +name, description, required files). Extension will be created inside `contrib` +directory. + ## Customization ### pgsql_hacker_helper.json This is a configuration file for extension. -It stored inside `.vscode` directory in your repository - `.vscode/pgsql_hacker_helper.json`. -You can use config file to extend built-in capabilities if there is no -support for something yet. +It stored inside `.vscode` directory in your repository - `.vscode/pgsql_hacker_helper.json`. You can use config file to extend built-in capabilities if there is no support for something yet. Example json: ```json { - "version": 2, + "version": 5, "specialMembers": { "array": [ { @@ -80,36 +162,266 @@ Example json: "alias": "PlannerRef", "type": "PlannerInfo *" } + ], + "typedefs": "my.typedefs.file", + "customListTypes": [ + { + "type": "char *", + "member": ["UserData", "knownNames"] + }, + { + "type": "struct FileChunks *", + "variable": ["ProcessFileChunks", "chunks"] + } + ], + "htab": [ + { + "type": "HashTableEntry *", + "member": ["ParentStruct", "hashtable"] + } + ], + "simplehash": [ + { + "prefix": "userdata", + "type": "UserDataHashEntry *" + } ] } ``` -In example 3 array special members - arrays will be shown with specified length, -not just pointers to arrays start. -Also, `PlannerRef` - typedef for `PlannerInfo *`. +Features: + +- 3 *array* special members (pointer field used as array) - `"typeName"->"memberName"` will be shown with length `"typeName"->"lengthExpression"`, not as simple pointers. + +- `PlannerRef` - custom user typedef for `PlannerInfo *`. + +- `UserData->knownNames` is a `List *` that contains pointer elements not `Node *`, but `char *` (`List` of strings). +Variable `chunks` in function `ProcessFileChunks` is a `List` that contains pointer elements not `Node *`, but `struct FileChunks *`. + +- User provided custom `typedefs` list (used by formatter). + +- `List *UserData->knownNames` contains pointers to `char *` (not Node), and variable `List *chunks` in function `ProcessFileChunks()` contains pointers to `struct FileChunks` (not Node) + +- Hash Table member `HTAB *hashtable` of struct `ParentStruct` contains entries of type `HashTableEntry *` + +- Simplehash struct `hashtable_hash` contains entries of type `UserDataHashEntry *`. For more info check [configuration file documentation](./docs/config_file.md). ## Extension Settings -There are 2 settings: +There are 4 settings: + +- `postgresql-hacker-helper.logLevel` - Log level -- Log level - set minimum level of log messages in Output channel. By default - `INFO` -- Files with NodeTag files - list of paths points to files that contain NodeTags. By default - `src/include/nodes/nodes.h`, `src/include/nodes/nodetags.h` + Minimum level of log messages in Output channel. + By default - `INFO`. If using VS Code 1.74.0 ang greater use `Output` channel + logger settings. + +- `postgresql-hacker-helper.srcPath` - Path to source code directory + + *Relative* path to custom PostgreSQL source code directory. Use it, if source + code files are not in your workspace root (i.e. in `${workspaceFolder}/postgresql`). Used for searching for + required files (node tag files, `pg_bsd_indent` and so on). If not specified + search starts from workspace root. (Next, this settings will be used as `*SrcPath*`). + +- `postgresql-hacker-helper.nodeTagFiles` - Files with NodeTag files + + List of paths points to files that contain NodeTags. + + - If path is absolute - specified files will be used directly. + - If path is relative, search starts from source files directory (see + `postgresql-hacker-helper.srcPath`). + - If not specified, `*SrcPath*/src/include/nodes/nodes.h` + and `*SrcPath*/src/include/nodes/nodetags.h` will be used. + +- `postgresql-hacker-helper.pg_bsd_indentPath` - Path to `pg_bsd_indent` + + Path to `pg_bsd_indent` tool. Required for formatting support. Use it if you have `pg_bsd_indent` installed globally or want to use specific version. + + - If not specified, it will be searched in `*SrcPath*/src/tools` directory. + - If specified, and failed to run extension will try to build it. + NOTE: If required, it will be downloaded (`wget` is required) and built. + +## Compatibility + +Extension tries to be compatible with multiple versions of both VS Code and +PostgreSQL. + +Minimal supported version of: + +- `VS Code` - 1.30 +- `PostgreSQL` - 8.0 + +> It is tested manually and not all use cases might be covered. If you found +> bug specific to some version please [create issue](https://github.com/ashenBlade/postgres-dev-helper/issues). + +Also, extension will target latest VS Code version and try to use the full functionality of new versions. +So, use latest VS Code versions to get new features earlier. + +For using formatter minimal supported version Postgres is `10`. + +> WARN: I *do not stand* that all extension features will work as expected on all versions ## Known Issues Known issues: -- Only tested on gdb debugger, UB for other debuggers (i.e. lldb) -- If in pointer variable was garbage, extension will not detect it and expand this variable (may be garbage) -- To get NodeTags extension reads all available NodeTag files (from settings), but - these files may be not created (./configure or make not run). I assume by time - of debugging start files will be created, so extension catch them and process. -- Tested only with [ms-vscode.cpptools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension +- If in pointer variable was garbage, extension will not detect it and expand this variable (may be garbage). + Usually, this will not lead to fatal errors, just note this. +- To get NodeTags extension reads all available NodeTag files (from settings), + but these files may be not created (./configure or make not run). I assume by + time of debugging start files will be created, so extension catch them and process. +- Works only with [ms-vscode.cpptools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) + extension. Currently, no support for other DAP adapters (i.e. Code LLDB). + Reason: strongly tied to output format of this extension (not only expression evaluation, but stack trace format i.e.) +- Sometimes formatting can misbehave. This is due to `pg_bsd_indent` internal + logic. If formatting is not applied try run command again. If file after + formatting is a mess this can be due to errors in logic. +- Some operations require data to be allocated (usually, for function invocation). + For this, `palloc` and `pfree` are used. So if you are debugging memory subsystem + you may want to disable extension, because it may affect debugging process. +- Some operations require for some work to be done with system catalog. + For example, to get function name using it's Oid. So, system catalog (system cache) + can be modified during extension work. ## Release Notes +### 1.9.0 + +Show elements of Hash Tables, according to stored types: `HTAB` and simplehash (from `simplehash.c`) + +Support for custom types of elements Hash Tables in configuration file. + +Add basic snippets: `IsA`, `foreach`, `PG_TRY()`/`PG_CATCH()`/`PG_FINALLY()`. + +Add `join_rel_level` to builtin array special members. + +### 1.8.2 + +Fix `Bitmapset` elements iteration stops if `0` appears in it. + +Remove trailing `=` from variables view. + +### 1.8.1 + +Fix `Dump Node to stdout` not working. + +### 1.8.0 + +Add variable from PG Variables to Watch view. + +More accurate `Var` representation extracting to prevent `ERROR` throwing. + +### 1.7.1 + +Do not show `List` as expandable if it is `NIL`. + +Add more checks for `Bitmapset` before search elements to prevent SEGFAULT and backend crash. + +Do not show `words` member for `Bitmapset`. + +Binary features info is cached for current debug session, not for current step. + +Typedef logic not worked correctly for Node variables. Specifically, `MemoryContext` did not show valid struct, i.e. not `AllocSetContext`. + +### 1.7.0 + +Add support for custom pointer types in `List *` elements. Earlier, all `void *` were casted to `Node *`. Users also can specify their own custom `List` types in configuration file. + +Fix memory leaking when evaluating `Expr` representation (`get_func_name` and `get_opname` were not `pfree`d). + +Fix caching not working for `Expr` representations. This led to performance degradations (multiple same evaluations). + +Add more checking when working with system catalog or some other functions (`MemoryContext` validity checking and so on). + +Tracking of postgres binary features (i.e. `bms_next_member` presence) for more performance. + +Do not show `initial_elements`, `head` and `tail` members of `List *`. + +### 1.6.1 + +Fix error message appeared when making debugger steps too fast. + +### 1.6.0 + +Show expression representation of `Expr` nodes. + +Show expression of `TargetEntry`, `EquivalenceMember` and `RestrictInfo` in description field to quick check elements of corresponding arrays. + +### 1.5.1 + +Update contents of created configuration file (by command). + +### 1.5.0 (pre-release) + +Add custom typedefs file setting in configuration file. This may be useful, if you want to change it and do not affect another workspaces. This is configured in `typedefs` member. + +Update configuration file layout version to 3. + +### 1.4.5 + +No changes + +### 1.4.4 + +Add `typedefs.list` file preprocessing for feeding to `pg_bsd_indent`. Processed file saved in `/tmp/pg-hacker-helper.typedefs.list` file and may be reused between different sessions. + +### 1.4.3 + +Add missing formatting rules when running `pg_bsd_indent`. [#3](https://github.com/ashenBlade/postgres-dev-helper/issues/3). + +### 1.4.2 + +Fix invalid handling of `pg_bsd_indentPath` setting. [#2](https://github.com/ashenBlade/postgres-dev-helper/issues/2) + +### 1.4.1 + +Fix invalid struct detection in variables view: top level structs are not +expandable. + +Add extension files bootstrapping: Makefile, \*.c, \*.sql, \*.control, tests. + +Fix variable length arrays fields displayed as expandable. + +Support for fixed size array expansion. + +### 1.4.0 + +Add support for custom PostgreSQL source code directories. Custom directory can +be specified using `postgresql-hacker-helper.srcPath` setting. + +Fix invalid logging for VS Code with version greater 1.74.0. + +### 1.3.0 + +Add formatting functionality using `pg_bsd_indent` integrated with VS Code: +can use with `Format Document` command or `Ctrl + Shift + I` (keybinding). + +Add showing `RangeTblEntry` and `RelOptInfo` to which Bitmapset points. +`RangeTblEntry` shown from `Query->rtable`, `RelOptInfo` - from +`PlannerInfo->simple_rel_array`. Referencing also available for other Bitmapsets +which points not to rte or rel. + +### 1.2.1 + +Add check for breakpoint in `bms_first_member` to avoid infinite loop. + +Add support for `MemoryContext` Node. + +### 1.2.0 + +Expand range of supported versions both for PostgreSQL (from 8.0) and VS Code +(from 1.30). + +Add support for Bitmapset for versions below 16. + +Add support for List with Linked List implementation. + +Fix log level updated only after extension or VS Code reload. + +Fix invalid Node cast in some cases when declared type has `struct` keyword. + ### 1.1.2 Fix invalid `List` behaviour with different declared type - members shown for @@ -147,7 +459,8 @@ Add more special members. Separate json configuration file to add your own special members. -Specifying real NodeTag in variable name if it differs from declared type. Shows in square brackets. +Specifying real NodeTag in variable name if it differs from declared type. Shows +in square brackets. Setup logging infrastructure. Availability to change minimum log level. diff --git a/scripts/gif-converter.sh b/dev/gif-converter.sh similarity index 100% rename from scripts/gif-converter.sh rename to dev/gif-converter.sh diff --git a/dev/merge-nodetags.sh b/dev/merge-nodetags.sh new file mode 100755 index 0000000..16bf2c1 --- /dev/null +++ b/dev/merge-nodetags.sh @@ -0,0 +1,43 @@ +#!/usr/bin/bash + +# Primarily used to update builtin node tags after major release. +# 1. Copy contents of node tags array from src/constants.ts to one file (remove leading spaces) +# src/constants.ts -> oldnodetags +# 2. Run 'read-nodetags.sh' on new major-released nodetags.h file to get new nodetags. +# ./read-nodetags.sh /home/user/postgresql-major-release/src/include/nodetags.h >newnodetags +# 3. Run this script on these files and update array from src/constants.ts with output +# ./merge-nodetags.sh newnodetags oldnodetags >mergednodetags + +function print_help { + cat < This file belongs to latest schema version. @@ -73,6 +73,7 @@ Examples: ```json { + "version": 3, "specialMembers": { "array": [ { @@ -123,7 +124,7 @@ Example: ```json { - "version": 2, + "version": 3, "aliases": [ { "alias": "Relids", @@ -132,3 +133,156 @@ Example: ] } ``` + +### Custom typedef file + +typedef file is required for correct `pg_bsd_indent` work. It contains list of types that treated by formatter differently. Usually, it does not change, but sometimes you may want to change it manually. I.e. when creating new patches or testing new features. + +By default, extension manages to create this file and cache for later you (optimization). But when this file should be changed it is not very handy to edit global file. So this setting is created for such cases - you just create own copy of typedefs file, edit it and use for specific workspace. + +Path to this file can be in 2 forms: + +- Absolute - specified file is used +- Relative - file with base folder as [postgresql-hacker-helper.srcPath](../README.md#extension-settings) is used + +Example: + +Read typedefs file `custom.typedefs.list` in current src path. + +```json +{ + "version": 3, + "typedefs": "custom.typedefs.list" +} +``` + +Read global typedefs file stored in temporary directory. + +```json +{ + "version": 3, + "typedefs": "/tmp/cached.custom.typedefs.list" +} +``` + +### Custom `List` types + +Usually, `List *` contains nodes (inherits from `Node`), but actually it can contain any pointer. +Extension treats all `List` as they contain `Node` variables, but you can say that this variable or struct member contains custom type (pointer to it). + +This information stored in `customListTypes` member. This is array of objects: + +```json +{ + "version": 4, + "customListTypes": [ + { + "type": "MyCustomType *", + "member": ["ParentStruct", "parent_member"] + }, + { + "type": "MyCustomType *", + "variable": ["ParentFunction", "variable_name"] + } + ] +} +``` + +Each object contain: + +- `type` - fully-qualified type name (that is `struct` or `pointer` should be included) to which pointer will be casted. +- `member` - pair of struct name and member of this struct. Definition looks like this: + + ```c + typedef struct ParentStruct + { + List *parent_member; + } ParentStruct; + ``` + +- `variable` - pair of function name and variable inside it. Definition looks like this: + + ```c + void + ParentFunction() + { + List *variable_name; + } + ``` + +With this 2 strategies extension detects `List`s with custom types. + +> NOTE: if your function is from extension, then you must prepend `EXT_NAME!` to your function name (`EXT_NAME` - name of shared library). +> Just like you see this extension name in `Call Stack` view in VS Code. +> +> i.e. for function `pgss_store` in `pg_stat_statements` you will use `pg_stat_statements!pgss_store` (because it's shared library name is `pg_stat_statements.so`). + +### Explore entries in Hash Tables + +`HTAB *` Hash Table entries can be traversed using `hash_seq_search`, but it returns `void *` - no information about it's type. +Extension has built-in types for several `HTAB`s. If you want to create your own hash table and see entries, then you can add information about that hash table entries types. + +This information stored in `htab` member. This is an array of objects similar to `customListTypes`: + +```json +{ + "version": 5, + "htab": [ + { + "type": "SampleType *", + "member": ["ParentStruct", "parent_member"] + }, + { + "type": "SampleType *", + "variable": ["ParentFunction", "variable_name"] + } + ] +} +``` + +Each object contain: + +- `type` - fully qualified type name of entry in `HTAB` +- `member` - array of struct name and member inside this struct with `HTAB *` type. Definition looks like this: + + ```c + typedef struct ParentStruct + { + HTAB *parent_member; + } ParentStruct; + ``` + +- `variable` - array of function name and variable inside this function with `HTAB *` type. Definition looks like this + + ```c + void ParentFunction() + { + HTAB *variable_name; + } + ``` + +Also, there is support for `simplehash.c` hash tables ("simplehash" further). They are code generated using macros, so for each specific hash table there are functions and structures defined. +Several builtin simplehashes exists and using configuration file you can add your own. +To define your custom simplehash you need to specify 2 things: prefix and entry type: + +```json +{ + "version": 5, + "simplehash": [ + { + "prefix": "sometableprefix", + "type": "HashTableEntry *" + } + ] +} +``` + +So, `simplehash` is an array of objects with members defining simplehash: + +- `prefix` - prefix for simplehash, that was specified by `SH_PREFIX` macro, when simplehash was defined in source code +- `type` - fully qualified type of entry stored in this simplehash + +Identifiers of structures and functions are derived from `prefix` and generated the same way, i.e. `PREFIX_iterator` - structure-state for iterator. + +> NOTE: compiler can apply unused symbol stripping, so after compilation there can be no structures/functions for iteration. +> In such situation, you should add some code that uses `PREFIX_iterator`, `PREFIX_start_iterate` and `PREFIX_iterate` (i.e. wrap such code with debug macros). diff --git a/docs/pg_variables.md b/docs/pg_variables.md index 3919eb2..c6407ba 100644 --- a/docs/pg_variables.md +++ b/docs/pg_variables.md @@ -92,6 +92,8 @@ Also, there are some Special Node types: - `IntList` - `OidList` - `XidList` + + Additionally, you can specify custom pointer type in `List`. See [custom `List` types](./config_file.md#custom-list-types) section in configuration file documentation. - Bitmapset - show elements of set in pseudo-member `$elements$` @@ -200,9 +202,8 @@ this you should update config file like this: } ``` -There are about 36 supported asm. For example, `simple_rel_array` for `PlannerInfo` +There are about 36 supported ASM. For example, `simple_rel_array` for `PlannerInfo` ![PlannerInfo->simple_rel_array](../resources/tutorial_array_sm.png) > For more info about configuration check [documentation](config_file.md) - diff --git a/package-lock.json b/package-lock.json index 35aef68..17fcbf6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,3266 +1,3266 @@ { - "name": "postgresql-hacker-helper", - "version": "1.1.2", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "postgresql-hacker-helper", - "version": "1.1.2", - "devDependencies": { - "@types/mocha": "^10.0.7", - "@types/node": "20.x", - "@types/vscode": "^1.91.0", - "@typescript-eslint/eslint-plugin": "^7.14.1", - "@typescript-eslint/parser": "^7.11.0", - "@vscode/test-cli": "^0.0.9", - "@vscode/test-electron": "^2.4.0", - "eslint": "^8.57.0", - "typescript": "^5.4.5" - }, - "engines": { - "vscode": "^1.0.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true - }, - "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, - "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-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "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, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "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, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "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 - }, - "node_modules/@types/mocha": { - "version": "10.0.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", - "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.14.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", - "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/vscode": { - "version": "1.91.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.91.0.tgz", - "integrity": "sha512-PgPr+bUODjG3y+ozWUCyzttqR9EHny9sPAfJagddQjDwdtf66y2sDKJMnFZRuzBA2YtBGASqJGPil8VDUPvO6A==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.1.tgz", - "integrity": "sha512-SxdPak/5bO0EnGktV05+Hq8oatjAYVY3Zh2bye9pGZy6+jwyR3LG3YKkV4YatlsgqXP28BTeVm9pqwJM96vf2A==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.16.1", - "@typescript-eslint/type-utils": "7.16.1", - "@typescript-eslint/utils": "7.16.1", - "@typescript-eslint/visitor-keys": "7.16.1", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.1.tgz", - "integrity": "sha512-u+1Qx86jfGQ5i4JjK33/FnawZRpsLxRnKzGE6EABZ40KxVT/vWsiZFEBBHjFOljmmV3MBYOHEKi0Jm9hbAOClA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "7.16.1", - "@typescript-eslint/types": "7.16.1", - "@typescript-eslint/typescript-estree": "7.16.1", - "@typescript-eslint/visitor-keys": "7.16.1", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.1.tgz", - "integrity": "sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.16.1", - "@typescript-eslint/visitor-keys": "7.16.1" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.1.tgz", - "integrity": "sha512-rbu/H2MWXN4SkjIIyWcmYBjlp55VT+1G3duFOIukTNFxr9PI35pLc2ydwAfejCEitCv4uztA07q0QWanOHC7dA==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "7.16.1", - "@typescript-eslint/utils": "7.16.1", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.1.tgz", - "integrity": "sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.1.tgz", - "integrity": "sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.16.1", - "@typescript-eslint/visitor-keys": "7.16.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.1.tgz", - "integrity": "sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.16.1", - "@typescript-eslint/types": "7.16.1", - "@typescript-eslint/typescript-estree": "7.16.1" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.1.tgz", - "integrity": "sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.16.1", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/@vscode/test-cli": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.9.tgz", - "integrity": "sha512-vsl5/ueE3Jf0f6XzB0ECHHMsd5A0Yu6StElb8a+XsubZW7kHNAOw4Y3TSSuDzKEpLnJ92nbMy1Zl+KLGCE6NaA==", - "dev": true, - "dependencies": { - "@types/mocha": "^10.0.2", - "c8": "^9.1.0", - "chokidar": "^3.5.3", - "enhanced-resolve": "^5.15.0", - "glob": "^10.3.10", - "minimatch": "^9.0.3", - "mocha": "^10.2.0", - "supports-color": "^9.4.0", - "yargs": "^17.7.2" - }, - "bin": { - "vscode-test": "out/bin.mjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@vscode/test-electron": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz", - "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==", - "dev": true, - "dependencies": { - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", - "jszip": "^3.10.1", - "ora": "^7.0.1", - "semver": "^7.6.2" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "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 - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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 - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dev": true, - "dependencies": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "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 - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/c8": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", - "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@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": "^6.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1" - }, - "bin": { - "c8": "bin/c8.js" - }, - "engines": { - "node": ">=14.14.0" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "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/chalk/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dev": true, - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "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, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/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 - }, - "node_modules/cliui/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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/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, - "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/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "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 - }, - "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 - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "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, - "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", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "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 - }, - "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "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", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "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, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, - "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "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 - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "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, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "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/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "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, - "engines": { - "node": ">=8" - } - }, - "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, - "bin": { - "he": "bin/he" - } - }, - "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 - }, - "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, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "engines": { - "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 - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "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, - "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 - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "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, - "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, - "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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 - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "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, - "engines": { - "node": ">=8" - } - }, - "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, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "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, - "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.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "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 - }, - "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 - }, - "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 - }, - "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, - "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, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "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, - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "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, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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 - }, - "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, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mocha": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz", - "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "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 - }, - "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "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, - "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/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "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, - "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/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "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", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", - "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", - "dev": true, - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.0", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.3.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "string-width": "^6.1.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", - "dev": true - }, - "node_modules/ora/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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", - "dev": true, - "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/string-width": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", - "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true - }, - "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 - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "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, - "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", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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, - "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/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "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 - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "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, - "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/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "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/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" + "name": "postgresql-hacker-helper", + "version": "1.4.5", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "postgresql-hacker-helper", + "version": "1.4.5", + "devDependencies": { + "@types/mocha": "^10.0.7", + "@types/node": "20.x", + "@types/vscode": "^1.30.0", + "@typescript-eslint/eslint-plugin": "^7.14.1", + "@typescript-eslint/parser": "^7.11.0", + "@vscode/test-cli": "^0.0.9", + "@vscode/test-electron": "^2.4.0", + "eslint": "^8.57.0", + "typescript": "^5.4.5" + }, + "engines": { + "vscode": "^1.30.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "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, + "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-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "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, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "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, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "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 + }, + "node_modules/@types/mocha": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", + "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/vscode": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.93.0.tgz", + "integrity": "sha512-kUK6jAHSR5zY8ps42xuW89NLcBpw1kOabah7yv38J8MyiYuOHxLQBi0e7zeXbQgVefDy/mZZetqEFC+Fl5eIEQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.1.tgz", + "integrity": "sha512-SxdPak/5bO0EnGktV05+Hq8oatjAYVY3Zh2bye9pGZy6+jwyR3LG3YKkV4YatlsgqXP28BTeVm9pqwJM96vf2A==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.16.1", + "@typescript-eslint/type-utils": "7.16.1", + "@typescript-eslint/utils": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.1.tgz", + "integrity": "sha512-u+1Qx86jfGQ5i4JjK33/FnawZRpsLxRnKzGE6EABZ40KxVT/vWsiZFEBBHjFOljmmV3MBYOHEKi0Jm9hbAOClA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.16.1", + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/typescript-estree": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.1.tgz", + "integrity": "sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.1.tgz", + "integrity": "sha512-rbu/H2MWXN4SkjIIyWcmYBjlp55VT+1G3duFOIukTNFxr9PI35pLc2ydwAfejCEitCv4uztA07q0QWanOHC7dA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.16.1", + "@typescript-eslint/utils": "7.16.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.1.tgz", + "integrity": "sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.1.tgz", + "integrity": "sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.1.tgz", + "integrity": "sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.16.1", + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/typescript-estree": "7.16.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.1.tgz", + "integrity": "sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vscode/test-cli": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.9.tgz", + "integrity": "sha512-vsl5/ueE3Jf0f6XzB0ECHHMsd5A0Yu6StElb8a+XsubZW7kHNAOw4Y3TSSuDzKEpLnJ92nbMy1Zl+KLGCE6NaA==", + "dev": true, + "dependencies": { + "@types/mocha": "^10.0.2", + "c8": "^9.1.0", + "chokidar": "^3.5.3", + "enhanced-resolve": "^5.15.0", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^10.2.0", + "supports-color": "^9.4.0", + "yargs": "^17.7.2" + }, + "bin": { + "vscode-test": "out/bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz", + "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^7.0.1", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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 + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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 + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "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 + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/c8": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", + "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@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": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "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/chalk/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "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, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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 + }, + "node_modules/cliui/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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, + "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/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "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 + }, + "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 + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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, + "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", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "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 + }, + "node_modules/enhanced-resolve": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", + "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "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", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "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, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", + "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "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 + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "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/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "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, + "engines": { + "node": ">=8" + } + }, + "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, + "bin": { + "he": "bin/he" + } + }, + "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 + }, + "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, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "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 + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "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, + "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 + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "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, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "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, + "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, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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 + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "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, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "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, + "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.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "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 + }, + "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 + }, + "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 + }, + "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, + "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, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "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, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "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, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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 + }, + "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, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mocha": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz", + "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "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 + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "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, + "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/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "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, + "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/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "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", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", + "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", + "dev": true, + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.9.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.3.0", + "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", + "string-width": "^6.1.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true + }, + "node_modules/ora/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, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dev": true, + "dependencies": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", + "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^10.2.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true + }, + "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 + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "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, + "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", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "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/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "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 + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "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, + "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/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "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/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "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 + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "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, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "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 + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dev": true, + "dependencies": { + "bl": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "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, + "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/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, + "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/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 + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "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/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "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 + }, + "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, + "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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "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, + "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/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, + "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/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 + }, + "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, + "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/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?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 + }, + "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, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "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/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, + "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, + "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/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 + }, + "node_modules/yargs/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "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 - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "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, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "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 - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/stdin-discarder": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", - "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", - "dev": true, - "dependencies": { - "bl": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "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, - "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/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, - "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/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 - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", - "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "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/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "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 - }, - "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, - "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/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true - }, - "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, - "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/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, - "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/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 - }, - "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, - "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/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?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 - }, - "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, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "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/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, - "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, - "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/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 - }, - "node_modules/yargs/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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } - } } diff --git a/package.json b/package.json index 71bd107..d16e08f 100644 --- a/package.json +++ b/package.json @@ -2,18 +2,20 @@ "name": "postgresql-hacker-helper", "displayName": "PostgreSQL Hacker Helper", "description": "Extension to assist Postgres hackers - source code developers", - "version": "1.1.2", + "version": "1.9.0", "engines": { - "vscode": "^1.0.0" + "vscode": "^1.30.0" }, "categories": [ - "Debuggers" + "Debuggers", + "Formatters", + "Other" ], "icon": "resources/logo.png", "activationEvents": [ "workspaceContains:.vscode/pgsql_hacker_helper.json", - "workspaceContains:src/include/nodes/nodes.h", - "workspaceContains:src/include/nodes/nodetags.h" + "workspaceContains:**/src/include/nodes/nodes.h", + "workspaceContains:**/src/include/nodes/nodetags.h" ], "main": "./out/entrypoint.js", "publisher": "ash-blade", @@ -27,7 +29,10 @@ "postgres", "pgsql", "pg", - "c" + "c", + "pgindent", + "formatter", + "format" ], "contributes": { "commands": [ @@ -55,6 +60,24 @@ "title": "Refresh configuration file", "shortTitle": "Refresh config file", "category": "PgSQL" + }, + { + "command": "postgresql-hacker-helper.formatterShowDiff", + "title": "Show diff preview for PostgreSQL formatter", + "shortTitle": "Diff PostgreSQL formatter", + "category": "PgSQL" + }, + { + "command": "postgresql-hacker-helper.bootstrapExtension", + "title": "Bootstrap extension", + "shortTitle": "Bootstrap extension", + "category": "PgSQL" + }, + { + "command": "postgresql-hacker-helper.addVariableToWatch", + "title": "Add Variable to Watch", + "shortTitle": "Add to Watch", + "category": "PgSQL" } ], "menus": { @@ -68,7 +91,13 @@ { "command": "postgresql-hacker-helper.refreshPostgresVariablesView", "group": "navigation", - "when": "inDebugMode && debugState == stopped" + "when": "inDebugMode && debugState == stopped && postgresql-hacker-helper:activated" + } + ], + "view/item/context": [ + { + "command": "postgresql-hacker-helper.addVariableToWatch", + "when": "inDebugMode && view == postgresql-hacker-helper.node-tree-view" } ], "commandPalette": [ @@ -76,6 +105,10 @@ "command": "postgresql-hacker-helper.dumpNodeToLog", "when": "false" }, + { + "command": "postgresql-hacker-helper.addVariableToWatch", + "when": "false" + }, { "command": "postgresql-hacker-helper.refreshPostgresVariablesView", "group": "navigation", @@ -100,11 +133,8 @@ "postgresql-hacker-helper.nodeTagFiles": { "title": "Files with NodeTags", "type": "array", - "default": [ - "src/include/nodes/nodes.h", - "src/include/nodes/nodetags.h" - ], - "description": "Files, where NodeTag values defined. It will be used to check if variable can have NodeTag. Can be either relative or absolute path. Relative paths used within current workspace" + "default": null, + "description": "Files, where NodeTag values defined. It will be used to check if variable can have NodeTag.\nCan be either relative or absolute path. Relative paths will be applied with base path of `srcPath` setting" }, "postgresql-hacker-helper.logLevel": { "title": "Log level", @@ -117,7 +147,17 @@ "DISABLE" ], "default": "INFO", - "description": "Minimum level of log messages to display in Output view" + "description": "Minimum level of log messages to display in Output view. For VS Code version greater 1.74.0 use log level in Output channel panel" + }, + "postgresql-hacker-helper.pg_bsd_indentPath": { + "title": "Path to pg_bsd_indent", + "type": "string", + "description": "Path to pg_bsd_indent executable used to format file.\nIf not specified, pg_bsd_indent will be search in all available location, and maybe built" + }, + "postgresql-hacker-helper.srcPath": { + "title": "Path to root of PostgreSQL source files", + "type": "string", + "description": "Path to source files of PostgreSQL. Set it if you have sources in separate sub-directory from root of project.\nIf not set search will start in project root directory" } } }, @@ -126,6 +166,12 @@ "fileMatch": "pgsql_hacker_helper.json", "url": "./properties.schema.json" } + ], + "snippets": [ + { + "language": "c", + "path": "./snippets.json" + } ] }, "scripts": { @@ -138,14 +184,14 @@ "deploy": "vsce publish" }, "devDependencies": { - "@types/vscode": "^1.0.0", "@types/mocha": "^10.0.7", "@types/node": "20.x", + "@types/vscode": "^1.30.0", "@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/parser": "^7.11.0", - "eslint": "^8.57.0", - "typescript": "^5.4.5", "@vscode/test-cli": "^0.0.9", - "@vscode/test-electron": "^2.4.0" + "@vscode/test-electron": "^2.4.0", + "eslint": "^8.57.0", + "typescript": "^5.4.5" } } diff --git a/properties.schema.json b/properties.schema.json index 48d5627..34ae06b 100644 --- a/properties.schema.json +++ b/properties.schema.json @@ -78,14 +78,93 @@ ] } }, + "typedefs": { + "type": "string", + "description": "Path to file with custom typdefs processed file" + }, "version": { "type": "integer", - "default": 1, + "default": 5, "enum": [ 1, - 2 + 2, + 3, + 4, + 5 ], "description": "Version of configuration file layout. Managed by extension" + }, + "customListType": { + "type": "object", + "description": "Description of custom pointer type for specified List", + "properties": { + "type": { + "type": "string", + "pattern": "\\*$", + "description": "Type to which 'ListCell's will be casted. Must be pointer." + }, + "member": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "description": "Pair of parent struct name and member name inside this struct, identifying this List*" + }, + "variable": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "description": "Pair of function name and variable name inside this function, identifying this List*" + } + }, + "required": [ + "type" + ] + }, + "htab": { + "type": "object", + "description": "Description of types of entries in HTAB", + "properties": { + "type": { + "type": "string", + "pattern": "\\*$", + "description": "Type of entry in Hash Table. Must be pointer." + }, + "member": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "description": "Pair of parent struct name and member name inside this struct, identifying this HTAB*" + }, + "variable": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "description": "Pair of function name and variable inside this function, identifying this HTAB*" + } + }, + "required": [ + "type" + ] + }, + "simplehash": { + "type": "object", + "description": "Description of types of entries in simplehash", + "properties": { + "prefix": { + "type": "string", + "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$", + "description": "Prefix for simplehash operations/structures specified in SH_PREFIX" + }, + "type": { + "type": "string", + "pattern": "\\*$", + "description": "Type of entry in Hash Table. Must be pointer." + } + }, + "required": [ + "prefix", + "type" + ] } }, "oneOf": [ @@ -135,6 +214,118 @@ "$ref": "#/definitions/aliasesV2" } } + }, + { + "type": "object", + "properties": { + "version": { + "const": 3, + "$ref": "#/definitions/version" + }, + "specialMembers": { + "type": "object", + "description": "Configurations for special members", + "properties": { + "array": { + "type": "array", + "description": "Special members that represent arrays - separate fields for array and it's length", + "items": { + "$ref": "#/definitions/arraySpecialMemberV2" + } + } + } + }, + "aliases": { + "$ref": "#/definitions/aliasesV2" + }, + "typedefs": { + "$ref": "#/definitions/typedefs" + } + } + }, + { + "type": "object", + "properties": { + "version": { + "const": 4, + "$ref": "#/definitions/version" + }, + "specialMembers": { + "type": "object", + "description": "Configurations for special members", + "properties": { + "array": { + "type": "array", + "description": "Special members that represent arrays - separate fields for array and it's length", + "items": { + "$ref": "#/definitions/arraySpecialMemberV2" + } + } + } + }, + "aliases": { + "$ref": "#/definitions/aliasesV2" + }, + "typedefs": { + "$ref": "#/definitions/typedefs" + }, + "customListTypes": { + "type": "array", + "description": "Array of definitions of custom List types", + "items": { + "$ref": "#/definitions/customListType" + } + } + } + }, + { + "type": "object", + "properties": { + "version": { + "const": 5, + "$ref": "#/definitions/version" + }, + "specialMembers": { + "type": "object", + "description": "Configurations for special members", + "properties": { + "array": { + "type": "array", + "description": "Special members that represent arrays - separate fields for array and it's length", + "items": { + "$ref": "#/definitions/arraySpecialMemberV2" + } + } + } + }, + "aliases": { + "$ref": "#/definitions/aliasesV2" + }, + "typedefs": { + "$ref": "#/definitions/typedefs" + }, + "customListTypes": { + "type": "array", + "description": "Array of definitions of custom List types", + "items": { + "$ref": "#/definitions/customListType" + } + }, + "htab": { + "type": "array", + "description": "Array of definitions of types of entries in HTAB", + "items": { + "$ref": "#/definitions/htab" + } + }, + "simplehash": { + "type": "array", + "description": "Array of definitions of types in simple hash tables", + "items": { + "$ref": "#/definitions/simplehash" + } + } + } } ], "required": [ diff --git a/resources/array-special-member.gif b/resources/array-special-member.gif new file mode 100644 index 0000000..759ad16 Binary files /dev/null and b/resources/array-special-member.gif differ diff --git a/resources/bitmapset-refs.gif b/resources/bitmapset-refs.gif new file mode 100644 index 0000000..3c72fa0 Binary files /dev/null and b/resources/bitmapset-refs.gif differ diff --git a/resources/bitmapset.gif b/resources/bitmapset.gif index 310c69f..74ee30f 100644 Binary files a/resources/bitmapset.gif and b/resources/bitmapset.gif differ diff --git a/resources/ec_members_exprs.png b/resources/ec_members_exprs.png new file mode 100644 index 0000000..bfa4529 Binary files /dev/null and b/resources/ec_members_exprs.png differ diff --git a/resources/expr_repr.gif b/resources/expr_repr.gif new file mode 100644 index 0000000..f120e13 Binary files /dev/null and b/resources/expr_repr.gif differ diff --git a/resources/formatter-work.gif b/resources/formatter-work.gif new file mode 100644 index 0000000..dbde229 Binary files /dev/null and b/resources/formatter-work.gif differ diff --git a/resources/list.gif b/resources/list.gif index 34766ca..0fac978 100644 Binary files a/resources/list.gif and b/resources/list.gif differ diff --git a/resources/overview.gif b/resources/overview.gif index 419fe60..dbeaa66 100644 Binary files a/resources/overview.gif and b/resources/overview.gif differ diff --git a/resources/planner.gif b/resources/planner.gif deleted file mode 100644 index 7323f60..0000000 Binary files a/resources/planner.gif and /dev/null differ diff --git a/resources/simplehash.gif b/resources/simplehash.gif new file mode 100644 index 0000000..9268e8a Binary files /dev/null and b/resources/simplehash.gif differ diff --git a/snippets.json b/snippets.json new file mode 100644 index 0000000..e94f62f --- /dev/null +++ b/snippets.json @@ -0,0 +1,50 @@ +{ + "foreach loop": { + "prefix": "foreach", + "body": [ + "foreach(${1:lc}, $2)", + "{", + "\t$4 ${3:node} = ($4) lfirst($1);", + "\t$0", + "}" + ] + }, + "if (IsA())": { + "prefix": "ifisa", + "body": [ + "if (IsA(${1:node}, $2))", + "{", + "\t$2 *$3 = ($2 *) $1;", + "\t$0", + "}" + ] + }, + "PG_TRY()/PG_CATCH() block": { + "prefix": "try", + "body": [ + "PG_TRY();", + "{", + "\t${TM_SELECTED_TEXT}$1", + "}", + "PG_CATCH();", + "{", + "\t$0", + "}", + "PG_END_TRY();" + ] + }, + "PG_TRY()/PG_FINALLY() block": { + "prefix": "tryf", + "body": [ + "PG_TRY();", + "{", + "\t${TM_SELECTED_TEXT}$1", + "}", + "PG_FINALLY();", + "{", + "\t$0", + "}", + "PG_END_TRY();" + ] + } +} \ No newline at end of file diff --git a/src/constants.ts b/src/constants.ts index c2e875b..c27cf9b 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,488 +1,1343 @@ +import { HtabEntryInfo, ListPtrSpecialMemberInfo, SimplehashEntryInfo } from "./variables"; + export function getDefaultNodeTags(): string[] { - /* PG17beta3, nodetags.h */ + /* Compiled from versions from 8.0 to 17 */ return [ - /* Pseudo NodeTags */ + /* + * Pseudo NodeTags. + * They are abstract Nodes that do not + * have own NodeTag, but they must be + * handled as `Node' - get real NodeTag + */ 'Node', 'Expr', 'Plan', - - 'List', - 'Alias', - 'RangeVar', - 'TableFunc', - 'IntoClause', - 'Var', - 'Const', - 'Param', + 'MemoryContextData', + + 'A_ArrayExpr', + 'AccessPriv', + 'A_Const', + 'A_Expr', + 'Agg', + 'AggInfo', + 'AggPath', 'Aggref', - 'GroupingFunc', - 'WindowFunc', - 'WindowFuncRunCondition', - 'MergeSupportFunc', - 'SubscriptingRef', - 'FuncExpr', - 'NamedArgExpr', - 'OpExpr', - 'DistinctExpr', - 'NullIfExpr', - 'ScalarArrayOpExpr', - 'BoolExpr', - 'SubLink', - 'SubPlan', + 'AggrefExprState', + 'AGGSPLIFINAL_DESERIAL', + 'AGGSPLIINITIAL_SERIAL', + 'AGGSPLISIMPLE', + 'AggState', + 'AggTransInfo', + 'A_Indices', + 'A_Indirection', + 'Alias', + 'AllocSetContext', + 'AlterCollationStmt', + 'AlterDatabaseRefreshCollStmt', + 'AlterDatabaseSetStmt', + 'AlterDatabaseStmt', + 'AlterDefaultPrivilegesStmt', + 'AlterDomainStmt', + 'AlterEnumStmt', + 'AlterEventTrigStmt', + 'AlterExtensionContentsStmt', + 'AlterExtensionStmt', + 'AlterFdwStmt', + 'AlterForeignServerStmt', + 'AlterFunctionStmt', + 'AlterGroupStmt', 'AlternativeSubPlan', - 'FieldSelect', - 'FieldStore', - 'RelabelType', - 'CoerceViaIO', + 'AlternativeSubPlanState', + 'AlterObjectDependsStmt', + 'AlterObjectSchemaStmt', + 'AlterOperatorStmt', + 'AlterOpFamilyStmt', + 'AlterOwnerStmt', + 'AlterPolicyStmt', + 'AlterPublicationStmt', + 'AlterReplicationSlotCmd', + 'AlterRoleSetStmt', + 'AlterRoleStmt', + 'AlterSeqStmt', + 'AlterStatsStmt', + 'AlterSubscriptionStmt', + 'AlterSystemStmt', + 'AlterTableCmd', + 'AlterTableMoveAllStmt', + 'AlterTableSpaceOptionsStmt', + 'AlterTableStmt', + 'AlterTSConfigurationStmt', + 'AlterTSDictionaryStmt', + 'AlterTypeStmt', + 'AlterUserMappingStmt', + 'AlterUserSetStmt', + 'AlterUserStmt', + 'Append', + 'AppendPath', + 'AppendRelInfo', + 'AppendState', 'ArrayCoerceExpr', - 'ConvertRowtypeExpr', - 'CollateExpr', + 'ArrayCoerceExprState', + 'ArrayExpr', + 'ArrayExprState', + 'ArrayRef', + 'ArrayRefExprState', + 'A_Star', + 'ATAlterConstraint', + 'BaseBackupCmd', + 'BitmapAnd', + 'BitmapAndPath', + 'BitmapAndState', + 'BitmapHeapPath', + 'BitmapHeapScan', + 'BitmapHeapScanState', + 'BitmapIndexScan', + 'BitmapIndexScanState', + 'BitmapOr', + 'BitmapOrPath', + 'BitmapOrState', + 'Bitmapset', + 'BitString', + 'Boolean', + 'BooleanTest', + 'BoolExpr', + 'BoolExprState', + 'BumpContext', + 'CallContext', + 'CallStmt', 'CaseExpr', - 'CaseWhen', + 'CaseExprState', 'CaseTestExpr', - 'ArrayExpr', - 'RowExpr', - 'RowCompareExpr', + 'CaseWhen', + 'CaseWhenState', + 'CheckPointStmt', + 'ClosePortalStmt', + 'ClusterStmt', 'CoalesceExpr', - 'MinMaxExpr', - 'SQLValueFunction', - 'XmlExpr', - 'JsonFormat', - 'JsonReturning', - 'JsonValueExpr', - 'JsonConstructorExpr', - 'JsonIsPredicate', - 'JsonBehavior', - 'JsonExpr', - 'JsonTablePath', - 'JsonTablePathScan', - 'JsonTableSiblingJoin', - 'NullTest', - 'BooleanTest', - 'MergeAction', + 'CoalesceExprState', 'CoerceToDomain', + 'CoerceToDomainState', 'CoerceToDomainValue', - 'SetToDefault', - 'CurrentOfExpr', - 'NextValueExpr', - 'InferenceElem', - 'TargetEntry', - 'RangeTblRef', - 'JoinExpr', - 'FromExpr', - 'OnConflictExpr', - 'Query', - 'TypeName', - 'ColumnRef', - 'ParamRef', - 'A_Expr', - 'A_Const', - 'TypeCast', + 'CoerceViaIO', + 'CoerceViaIOState', 'CollateClause', - 'RoleSpec', - 'FuncCall', - 'A_Star', - 'A_Indices', - 'A_Indirection', - 'A_ArrayExpr', - 'ResTarget', - 'MultiAssignRef', - 'SortBy', - 'WindowDef', - 'RangeSubselect', - 'RangeFunction', - 'RangeTableFunc', - 'RangeTableFuncCol', - 'RangeTableSample', + 'CollateExpr', 'ColumnDef', - 'TableLikeClause', - 'IndexElem', - 'DefElem', - 'LockingClause', - 'XmlSerialize', - 'PartitionElem', - 'PartitionSpec', - 'PartitionBoundSpec', - 'PartitionRangeDatum', - 'SinglePartitionSpec', - 'PartitionCmd', - 'RangeTblEntry', - 'RTEPermissionInfo', - 'RangeTblFunction', - 'TableSampleClause', - 'WithCheckOption', - 'SortGroupClause', - 'GroupingSet', - 'WindowClause', - 'RowMarkClause', - 'WithClause', - 'InferClause', - 'OnConflictClause', - 'CTESearchClause', - 'CTECycleClause', + 'ColumnRef', + 'CommentStmt', 'CommonTableExpr', - 'MergeWhenClause', - 'TriggerTransition', - 'JsonOutput', - 'JsonArgument', - 'JsonFuncExpr', - 'JsonTablePathSpec', - 'JsonTable', - 'JsonTableColumn', - 'JsonKeyValue', - 'JsonParseExpr', - 'JsonScalarExpr', - 'JsonSerializeExpr', - 'JsonObjectConstructor', - 'JsonArrayConstructor', - 'JsonArrayQueryConstructor', - 'JsonAggConstructor', - 'JsonObjectAgg', - 'JsonArrayAgg', - 'RawStmt', - 'InsertStmt', - 'DeleteStmt', - 'UpdateStmt', - 'MergeStmt', - 'SelectStmt', - 'SetOperationStmt', - 'ReturnStmt', - 'PLAssignStmt', - 'CreateSchemaStmt', - 'AlterTableStmt', - 'ReplicaIdentityStmt', - 'AlterTableCmd', - 'AlterCollationStmt', - 'AlterDomainStmt', - 'GrantStmt', - 'ObjectWithArgs', - 'AccessPriv', - 'GrantRoleStmt', - 'AlterDefaultPrivilegesStmt', - 'CopyStmt', - 'VariableSetStmt', - 'VariableShowStmt', - 'CreateStmt', + 'CompositeTypeStmt', + 'Const', 'Constraint', - 'CreateTableSpaceStmt', - 'DropTableSpaceStmt', - 'AlterTableSpaceOptionsStmt', - 'AlterTableMoveAllStmt', + 'ConstraintsSetStmt', + 'ConvertRowtypeExpr', + 'ConvertRowtypeExprState', + 'CopyStmt', + 'CreateAmStmt', + 'CreateCastStmt', + 'CreateConversionStmt', + 'CreatedbStmt', + 'CreateDomainStmt', + 'CreateEnumStmt', + 'CreateEventTrigStmt', 'CreateExtensionStmt', - 'AlterExtensionStmt', - 'AlterExtensionContentsStmt', 'CreateFdwStmt', - 'AlterFdwStmt', 'CreateForeignServerStmt', - 'AlterForeignServerStmt', 'CreateForeignTableStmt', - 'CreateUserMappingStmt', - 'AlterUserMappingStmt', - 'DropUserMappingStmt', - 'ImportForeignSchemaStmt', - 'CreatePolicyStmt', - 'AlterPolicyStmt', - 'CreateAmStmt', - 'CreateTrigStmt', - 'CreateEventTrigStmt', - 'AlterEventTrigStmt', + 'CreateFunctionStmt', + 'CreateGroupStmt', + 'CreateOpClassItem', + 'CreateOpClassStmt', + 'CreateOpFamilyStmt', 'CreatePLangStmt', + 'CreatePolicyStmt', + 'CreatePublicationStmt', + 'CreateRangeStmt', + 'CreateReplicationSlotCmd', 'CreateRoleStmt', - 'AlterRoleStmt', - 'AlterRoleSetStmt', - 'DropRoleStmt', + 'CreateSchemaStmt', 'CreateSeqStmt', - 'AlterSeqStmt', - 'DefineStmt', - 'CreateDomainStmt', - 'CreateOpClassStmt', - 'CreateOpClassItem', - 'CreateOpFamilyStmt', - 'AlterOpFamilyStmt', - 'DropStmt', - 'TruncateStmt', - 'CommentStmt', - 'SecLabelStmt', - 'DeclareCursorStmt', - 'ClosePortalStmt', - 'FetchStmt', - 'IndexStmt', 'CreateStatsStmt', - 'StatsElem', - 'AlterStatsStmt', - 'CreateFunctionStmt', - 'FunctionParameter', - 'AlterFunctionStmt', - 'DoStmt', - 'InlineCodeBlock', - 'CallStmt', - 'CallContext', - 'RenameStmt', - 'AlterObjectDependsStmt', - 'AlterObjectSchemaStmt', - 'AlterOwnerStmt', - 'AlterOperatorStmt', - 'AlterTypeStmt', - 'RuleStmt', - 'NotifyStmt', - 'ListenStmt', - 'UnlistenStmt', - 'TransactionStmt', - 'CompositeTypeStmt', - 'CreateEnumStmt', - 'CreateRangeStmt', - 'AlterEnumStmt', - 'ViewStmt', - 'LoadStmt', - 'CreatedbStmt', - 'AlterDatabaseStmt', - 'AlterDatabaseRefreshCollStmt', - 'AlterDatabaseSetStmt', - 'DropdbStmt', - 'AlterSystemStmt', - 'ClusterStmt', - 'VacuumStmt', - 'VacuumRelation', - 'ExplainStmt', + 'CreateStmt', + 'CreateSubscriptionStmt', 'CreateTableAsStmt', - 'RefreshMatViewStmt', - 'CheckPointStmt', - 'DiscardStmt', - 'LockStmt', - 'ConstraintsSetStmt', - 'ReindexStmt', - 'CreateConversionStmt', - 'CreateCastStmt', + 'CreateTableSpaceStmt', 'CreateTransformStmt', - 'PrepareStmt', - 'ExecuteStmt', + 'CreateTrigStmt', + 'CreateUserMappingStmt', + 'CreateUserStmt', + 'CTECycleClause', + 'CteScan', + 'CteScanState', + 'CTESearchClause', + 'CurrentOfExpr', + 'CustomPath', + 'CustomScan', + 'CustomScanState', 'DeallocateStmt', + 'DeclareCursorStmt', + 'DefElem', + 'DefineStmt', + 'DeleteStmt', + 'DiscardStmt', + 'DistinctExpr', + 'DomainConstraintState', + 'DoStmt', + 'DropCastStmt', + 'DropdbStmt', + 'DropFdwStmt', + 'DropForeignServerStmt', + 'DropGroupStmt', 'DropOwnedStmt', - 'ReassignOwnedStmt', - 'AlterTSDictionaryStmt', - 'AlterTSConfigurationStmt', - 'PublicationTable', - 'PublicationObjSpec', - 'CreatePublicationStmt', - 'AlterPublicationStmt', - 'CreateSubscriptionStmt', - 'AlterSubscriptionStmt', + 'DropPLangStmt', + 'DropPropertyStmt', + 'DropReplicationSlotCmd', + 'DropRoleStmt', + 'DropStmt', 'DropSubscriptionStmt', - 'PlannerGlobal', - 'PlannerInfo', - 'RelOptInfo', - 'IndexOptInfo', - 'ForeignKeyOptInfo', - 'StatisticExtInfo', - 'JoinDomain', + 'DropTableSpaceStmt', + 'DropUserMappingStmt', + 'DropUserStmt', 'EquivalenceClass', 'EquivalenceMember', - 'PathKey', - 'GroupByOrdering', - 'PathTarget', - 'ParamPathInfo', - 'Path', - 'IndexPath', - 'IndexClause', - 'BitmapHeapPath', - 'BitmapAndPath', - 'BitmapOrPath', - 'TidPath', - 'TidRangePath', - 'SubqueryScanPath', + 'ErrorSaveContext', + 'EState', + 'EventTriggerData', + 'ExecuteStmt', + 'ExplainStmt', + 'Expr', + 'ExprContext', + 'ExprState', + 'ExtensibleNode', + 'FdwRoutine', + 'FetchStmt', + 'FieldSelect', + 'FieldSelectState', + 'FieldStore', + 'FieldStoreState', + 'FkConstraint', + 'Float', + 'ForeignKeyCacheInfo', + 'ForeignKeyOptInfo', 'ForeignPath', - 'CustomPath', - 'AppendPath', - 'MergeAppendPath', - 'GroupResultPath', - 'MaterialPath', - 'MemoizePath', - 'UniquePath', - 'GatherPath', + 'ForeignScan', + 'ForeignScanState', + 'FromExpr', + 'FuncCall', + 'FuncExpr', + 'FuncExprState', + 'FunctionParameter', + 'FunctionScan', + 'FunctionScanState', + 'FuncWithArgs', + 'Gather', + 'GatherMerge', 'GatherMergePath', - 'NestPath', - 'MergePath', - 'HashPath', - 'ProjectionPath', - 'ProjectSetPath', - 'SortPath', - 'IncrementalSortPath', - 'GroupPath', - 'UpperUniquePath', - 'AggPath', + 'GatherMergeState', + 'GatherPath', + 'GatherState', + 'GenerationContext', + 'GenericExprState', + 'GrantRoleStmt', + 'GrantStmt', + 'Group', + 'GroupByOrdering', + 'GroupClause', + 'GroupingFunc', + 'GroupingFuncExprState', + 'GroupingSet', 'GroupingSetData', - 'RollupData', 'GroupingSetsPath', - 'MinMaxAggPath', - 'WindowAggPath', - 'SetOpPath', - 'RecursiveUnionPath', - 'LockRowsPath', - 'ModifyTablePath', + 'GroupPath', + 'GroupResultPath', + 'GroupState', + 'Hash', + 'HashJoin', + 'HashJoinState', + 'HashPath', + 'HashState', + 'IdentifySystemCmd', + 'ImportForeignSchemaStmt', + 'InClauseInfo', + 'IncrementalSort', + 'IncrementalSortPath', + 'IncrementalSortState', + 'IndexAmRoutine', + 'IndexClause', + 'IndexElem', + 'IndexInfo', + 'IndexOnlyScan', + 'IndexOnlyScanState', + 'IndexOptInfo', + 'IndexPath', + 'IndexScan', + 'IndexScanState', + 'IndexStmt', + 'InferClause', + 'InferenceElem', + 'InhRelation', + 'InlineCodeBlock', + 'InnerIndexscanInfo', + 'InsertStmt', + 'Integer', + 'IntList', + 'IntoClause', + 'Invalid', + 'Join', + 'JoinDomain', + 'JoinExpr', + 'JoinInfo', + 'JOIN_RIGHANTI', + 'JoinState', + 'JsonAggConstructor', + 'JsonArgument', + 'JsonArrayAgg', + 'JsonArrayConstructor', + 'JsonArrayQueryConstructor', + 'JsonBehavior', + 'JsonConstructorExpr', + 'JsonExpr', + 'JsonFormat', + 'JsonFuncExpr', + 'JsonIsPredicate', + 'JsonKeyValue', + 'JsonObjectAgg', + 'JsonObjectConstructor', + 'JsonOutput', + 'JsonParseExpr', + 'JsonReturning', + 'JsonScalarExpr', + 'JsonSerializeExpr', + 'JsonTable', + 'JsonTableColumn', + 'JsonTablePath', + 'JsonTablePathScan', + 'JsonTablePathSpec', + 'JsonTableSiblingJoin', + 'JsonValueExpr', + 'JunkFilter', + 'LateralJoinInfo', + 'LIMIOPTION_COUNT', + 'LIMIOPTION_DEFAULT', + 'LIMIOPTION_WITH_TIES', + 'Limit', 'LimitPath', - 'RestrictInfo', - 'PlaceHolderVar', - 'SpecialJoinInfo', - 'OuterJoinClauseInfo', - 'AppendRelInfo', - 'RowIdentityVarInfo', - 'PlaceHolderInfo', - 'MinMaxAggInfo', - 'PlannerParamItem', - 'AggInfo', - 'AggTransInfo', - 'PlannedStmt', - 'Result', - 'ProjectSet', - 'ModifyTable', - 'Append', + 'LimitState', + 'List', + 'ListenStmt', + 'LoadStmt', + 'LockingClause', + 'LockRows', + 'LockRowsPath', + 'LockRowsState', + 'LockStmt', + 'Material', + 'MaterialPath', + 'MaterialState', + 'Memoize', + 'MemoizePath', + 'MemoizeState', + 'MemoryContext', + 'MemoryContextData', + 'MergeAction', + 'MergeActionState', 'MergeAppend', - 'RecursiveUnion', - 'BitmapAnd', - 'BitmapOr', - 'SeqScan', - 'SampleScan', - 'IndexScan', - 'IndexOnlyScan', - 'BitmapIndexScan', - 'BitmapHeapScan', - 'TidScan', - 'TidRangeScan', - 'SubqueryScan', - 'FunctionScan', - 'ValuesScan', - 'TableFuncScan', - 'CteScan', + 'MergeAppendPath', + 'MergeAppendState', + 'MergeJoin', + 'MergeJoinState', + 'MergePath', + 'MergeStmt', + 'MergeSupportFunc', + 'MergeWhenClause', + 'MinMaxAggInfo', + 'MinMaxAggPath', + 'MinMaxExpr', + 'MinMaxExprState', + 'ModifyTable', + 'ModifyTablePath', + 'ModifyTableState', + 'MultiAssignRef', + 'NamedArgExpr', 'NamedTuplestoreScan', - 'WorkTableScan', - 'ForeignScan', - 'CustomScan', + 'NamedTuplestoreScanState', 'NestLoop', 'NestLoopParam', - 'MergeJoin', - 'HashJoin', - 'Material', - 'Memoize', - 'Sort', - 'IncrementalSort', - 'Group', - 'Agg', - 'WindowAgg', - 'Unique', - 'Gather', - 'GatherMerge', - 'Hash', - 'SetOp', - 'LockRows', - 'Limit', - 'PlanRowMark', - 'PartitionPruneInfo', + 'NestLoopState', + 'NestPath', + 'NextValueExpr', + 'NotifyStmt', + 'Null', + 'NullIfExpr', + 'NullTest', + 'NullTestState', + 'ObjectWithArgs', + 'OidList', + 'ONCONFLICNONE', + 'ONCONFLICNOTHING', + 'OnConflictClause', + 'OnConflictExpr', + 'OnConflictSetState', + 'ONCONFLICUPDATE', + 'OpExpr', + 'OuterJoinClauseInfo', + 'Param', + 'ParamPathInfo', + 'ParamRef', + 'PartitionBoundSpec', + 'PartitionCmd', + 'PartitionedChildRelInfo', 'PartitionedRelPruneInfo', - 'PartitionPruneStepOp', + 'PartitionElem', + 'PartitionPruneInfo', 'PartitionPruneStepCombine', + 'PartitionPruneStepOp', + 'PartitionRangeDatum', + 'PartitionSpec', + 'Path', + 'PathKey', + 'PathKeyInfo', + 'PathKeyItem', + 'PathTarget', + 'PlaceHolderInfo', + 'PlaceHolderVar', + 'Plan', 'PlanInvalItem', - 'ExprState', - 'IndexInfo', - 'ExprContext', - 'ReturnSetInfo', + 'PlannedStmt', + 'PlannerGlobal', + 'PlannerInfo', + 'PlannerParamItem', + 'PlanRowMark', + 'PlanState', + 'PLAssignStmt', + 'PrepareStmt', + 'PrivGrantee', + 'PrivTarget', 'ProjectionInfo', - 'JunkFilter', - 'OnConflictSetState', - 'MergeActionState', - 'ResultRelInfo', - 'EState', - 'WindowFuncExprState', - 'SetExprState', - 'SubPlanState', - 'DomainConstraintState', - 'ResultState', + 'ProjectionPath', + 'ProjectSet', + 'ProjectSetPath', 'ProjectSetState', - 'ModifyTableState', - 'AppendState', - 'MergeAppendState', + 'PublicationObjSpec', + 'PublicationTable', + 'Query', + 'RangeFunction', + 'RangeSubselect', + 'RangeTableFunc', + 'RangeTableFuncCol', + 'RangeTableSample', + 'RangeTblEntry', + 'RangeTblFunction', + 'RangeTblRef', + 'RangeVar', + 'RawStmt', + 'ReadReplicationSlotCmd', + 'ReassignOwnedStmt', + 'RecursiveUnion', + 'RecursiveUnionPath', 'RecursiveUnionState', - 'BitmapAndState', - 'BitmapOrState', + 'RefreshMatViewStmt', + 'ReindexStmt', + 'RelabelType', + 'RelOptInfo', + 'RemoveAggrStmt', + 'RemoveFuncStmt', + 'RemoveOpClassStmt', + 'RemoveOperStmt', + 'RemoveOpFamilyStmt', + 'RenameStmt', + 'ReplicaIdentityStmt', + 'Resdom', + 'ResTarget', + 'RestrictInfo', + 'Result', + 'ResultPath', + 'ResultRelInfo', + 'ResultState', + 'ReturningClause', + 'ReturningExpr', + 'ReturningOption', + 'ReturnSetInfo', + 'ReturnStmt', + 'RoleSpec', + 'RollupData', + 'RowCompareExpr', + 'RowCompareExprState', + 'RowExpr', + 'RowExprState', + 'RowIdentityVarInfo', + 'RowMarkClause', + 'RTEPermissionInfo', + 'RuleStmt', + 'SampleScan', + 'SampleScanState', + 'ScalarArrayOpExpr', + 'ScalarArrayOpExprState', + 'Scan', 'ScanState', + 'SecLabelStmt', + 'SelectStmt', + 'SeqScan', 'SeqScanState', - 'SampleScanState', - 'IndexScanState', - 'IndexOnlyScanState', - 'BitmapIndexScanState', - 'BitmapHeapScanState', - 'TidScanState', - 'TidRangeScanState', - 'SubqueryScanState', - 'FunctionScanState', - 'ValuesScanState', - 'TableFuncScanState', - 'CteScanState', - 'NamedTuplestoreScanState', - 'WorkTableScanState', - 'ForeignScanState', - 'CustomScanState', - 'JoinState', - 'NestLoopState', - 'MergeJoinState', - 'HashJoinState', - 'MaterialState', - 'MemoizeState', - 'SortState', - 'IncrementalSortState', - 'GroupState', - 'AggState', - 'WindowAggState', - 'UniqueState', - 'GatherState', - 'GatherMergeState', - 'HashState', + 'SetExprState', + 'SetOp', + 'SETOPCMD_EXCEPALL', + 'SETOPCMD_INTERSECALL', + 'SetOperationStmt', + 'SetOpPath', 'SetOpState', - 'LockRowsState', - 'LimitState', - 'IndexAmRoutine', - 'TableAmRoutine', - 'TsmRoutine', - 'EventTriggerData', - 'TriggerData', - 'TupleTableSlot', - 'FdwRoutine', - 'Bitmapset', - 'ExtensibleNode', - 'ErrorSaveContext', - 'IdentifySystemCmd', - 'BaseBackupCmd', - 'CreateReplicationSlotCmd', - 'DropReplicationSlotCmd', - 'AlterReplicationSlotCmd', + 'SetToDefault', + 'SinglePartitionSpec', + 'SlabContext', + 'Sort', + 'SortBy', + 'SortClause', + 'SortGroupClause', + 'SortPath', + 'SortState', + 'SpecialJoinInfo', + 'SQLCmd', + 'SQLValueFunction', 'StartReplicationCmd', - 'ReadReplicationSlotCmd', - 'TimeLineHistoryCmd', - 'UploadManifestCmd', - 'SupportRequestSimplify', - 'SupportRequestSelectivity', + 'StatisticExtInfo', + 'StatsElem', + 'String', + 'SubLink', + 'SubPlan', + 'SubPlanState', + 'SubqueryScan', + 'SubqueryScanPath', + 'SubqueryScanState', + 'SubscriptingRef', 'SupportRequestCost', - 'SupportRequestRows', 'SupportRequestIndexCondition', - 'SupportRequestWFuncMonotonic', + 'SupportRequestModifyInPlace', 'SupportRequestOptimizeWindowClause', - 'Integer', - 'Float', - 'Boolean', - 'String', - 'BitString', - 'ForeignKeyCacheInfo', - 'IntList', - 'OidList', - 'XidList', - 'AllocSetContext', - 'GenerationContext', - 'SlabContext', - 'BumpContext', + 'SupportRequestRows', + 'SupportRequestSelectivity', + 'SupportRequestSimplify', + 'SupportRequestWFuncMonotonic', + 'TableAmRoutine', + 'TableFunc', + 'TableFuncScan', + 'TableFuncScanState', + 'TableLikeClause', + 'TableSampleClause', + 'TargetEntry', 'TIDBitmap', + 'TidPath', + 'TidRangePath', + 'TidRangeScan', + 'TidRangeScanState', + 'TidScan', + 'TidScanState', + 'TimeLineHistoryCmd', + 'TransactionStmt', + 'TriggerData', + 'TriggerTransition', + 'TruncateStmt', + 'TsmRoutine', + 'TupleTableSlot', + 'TypeCast', + 'TypeName', + 'Unique', + 'UniquePath', + 'UniqueRelInfo', + 'UniqueState', + 'UnlistenStmt', + 'UpdateStmt', + 'UploadManifestCmd', + 'UpperUniquePath', + 'VacuumRelation', + 'VacuumStmt', + 'Value', + 'ValuesScan', + 'ValuesScanState', + 'Var', + 'VariableResetStmt', + 'VariableSetStmt', + 'VariableShowStmt', + 'ViewStmt', + 'WholeRowVarExprState', + 'WindowAgg', + 'WindowAggPath', + 'WindowAggState', + 'WindowClause', + 'WindowDef', + 'WindowFunc', + 'WindowFuncExprState', + 'WindowFuncRunCondition', 'WindowObjectData', + 'WithCheckOption', + 'WithClause', + 'WorkTableScan', + 'WorkTableScanState', + 'XidList', + 'XmlExpr', + 'XmlExprState', + 'XmlSerialize', + ] +} + +/** + * Returns list of Expr nodes, whose text representation is displayed in + * variables view as separate member. + */ +export function getDisplayedExprs(): string[] { + return [ + 'Aggref', + 'ArrayCoerceExpr', + 'ArrayExpr', + 'ArrayRef', + 'BoolExpr', + 'BooleanTest', + 'CaseWhen', + 'CoalesceExpr', + 'CoerceToDomain', + 'CoerceViaIO', + 'Const', + 'ConvertRowtypeExpr', + 'CurrentOfExpr', + 'DistinctExpr', + 'FieldSelect', + 'FieldStore', + 'FuncExpr', + 'GroupingFunc', + 'InferenceElem', + 'JsonConstructorExpr', + 'JsonExpr', + 'JsonIsPredicate', + 'JsonValueExpr', + 'MinMaxExpr', + 'NullIfExpr', + 'NullTest', + 'OpExpr', + 'Param', + 'RelabelType', + 'RowCompareExpr', + 'RowExpr', + 'SQLValueFunction', + 'ScalarArrayOpExpr', + 'SubLink', + 'SubscriptingRef', + 'TargetEntry', + 'Var', + 'WindowFunc', + 'WindowFuncFuncCondition', + 'XmlExpr', + + /* This is actually not Expr, but handy to see representation */ + 'PlaceHolderVar', ] } +export function getKnownCustomListPtrs(): ListPtrSpecialMemberInfo[] { + const member = (type: string, struct: string, member: string): ListPtrSpecialMemberInfo => ({ + type: type + ' *', + member: [struct, member] + }); + + const variable = (type: string, func: string, variable: string): ListPtrSpecialMemberInfo => ({ + type: type + ' *', + variable: [func, variable] + }); + + return [ + /* contrib/amcheck/verify_heapam.c */ + member('ToastedAttribute', 'HeapCheckContext', 'toasted_attributes'), + + /* src/backend/access/index/amvalidate.c */ + variable('OpFamilyOpFuncGroup', 'indentify_opfamily_groups', 'result'), + + /* contrib/bloom/blvalidate.c */ + variable('OpFamilyOpFuncGroup', 'blvalidate', 'grouplist'), + + /* contrib/pg_trgm/trgm_regexp.c */ + member('TrgmState', 'TrgmNFA', 'queue'), + member('TrgmStateKey', 'TrgmNFA', 'keysQueue'), + member('TrgmStateKey', 'TrgmNFA', 'enterKeys'), + member('TrgmArcInfo', 'ColorTrgmInfo', 'arcs'), + member('TrgmArc', 'TrgmState', 'arcs'), + + /* contrib/postgres_fdw/connection.c */ + variable('ConnCacheEntry', 'pgfdw_finish_pre_commit_cleanup', 'pending_entries'), + variable('ConnCacheEntry', 'pgfdw_xact_callback', 'pending_entries'), + variable('ConnCacheEntry', 'pgfdw_xact_callback', 'cancel_requested'), + variable('ConnCacheEntry', 'pgfdw_finish_pre_commit_cleanup', 'pending_deallocs'), + variable('ConnCacheEntry', 'pgfdw_finish_abort_cleanup', 'cancel_requested'), + variable('ConnCacheEntry', 'pgfdw_finish_abort_cleanup', 'pending_deallocs'), + variable('ConnCacheEntry', 'pgfdw_finish_abort_cleanup', 'pending_entries'), + + /* contrib/postgres_fdw/deparse.c */ + variable('char', 'appendWhereClause', 'additional_conds'), + variable('char', 'deparseFromExpr', 'additional_conds'), + variable('char', 'postgresImportForeignSchema', 'commands'), + variable('RelationData', 'deparseTruncateSql', 'rels'), + variable('RelationData', 'postgresExecForeignTruncate', 'rels'), + + + /* contrib/postgres_fdw/option.c */ + variable('const char', 'ExtractExtensionList', 'extlist'), + + /* contrib/sepgsql/label.c */ + variable('pending_label', 'sepgsql_subxact_callback', 'client_label_pending'), + + /* src/backend/access/brin/brin_validate.c */ + variable('OpFamilyOpFuncGroup', 'brinvalidate', 'grouplist'), + member('local_relopt', 'local_relopts', 'options'), + member('void', 'local_relopts', 'validators'), + + /* src/backend/access/gin/ginvalidate.c */ + variable('OpFamilyMember', 'ginadjustmembers', 'operators'), + variable('OpFamilyMember', 'ginadjustmembers', 'functions'), + + /* src/backend/access/gist/gistbuild.c */ + variable('GISTPageSplitInfo', 'gistbufferinginserttuples', 'splitinfo'), + + /* src/backend/access/gist/gistbuildbuffers.c */ + variable('GISTPageSplitInfo', 'gistRelocateBuildBuffersOnSplit', 'splitinfo'), + + /* src/backend/access/gist/gistvalidate.c */ + variable('OpFamilyOpFuncGroup', 'gistvalidate', 'grouplist'), + variable('OpFamilyMember', 'gistadjustmembers', 'operators'), + variable('OpFamilyMember', 'gistadjustmembers', 'functions'), + + /* src/backend/access/hash/hashvalidate.c */ + variable('OpFamilyOpFuncGroup', 'hashvalidate', 'grouplist'), + variable('OpFamilyMember', 'hashadjustmembers', 'operators'), + variable('OpFamilyMember', 'hashadjustmembers', 'functions'), + + /* src/backend/access/nbtree/nbvalidate.c */ + variable('OpFamilyOpFuncGroup', 'btvalidate', 'grouplist'), + variable('OpFamilyMember', 'btadjustmembers', 'operators'), + variable('OpFamilyMember', 'btadjustmembers', 'functions'), + + /* src/backend/access/spgist/spgvalidate.c */ + variable('OpFamilyOpFuncGroup', 'spgvalidate', 'grouplist'), + variable('OpFamilyMember', 'spgadjustmembers', 'functions'), + variable('OpFamilyMember', 'spgadjustmembers', 'operators'), + + /* src/backend/access/transam/timeline.c */ + variable('TimeLineHistoryEntry', 'tliInHistory', 'expectedTLEs'), + variable('TimeLineHistoryEntry', 'tliOfPointInHistory', 'history'), + variable('TimeLineHistoryEntry', 'tliSwitchPoint', 'history'), + + /* src/backend/access/transam/xlog.c */ + variable('char *', 'check_wal_consistency_checking', 'elemlist'), + + /* src/backend/access/transam/timeline.c */ + variable('TimeLineHistoryEntry', 'readTimeLineHistory', 'result'), + + /* src/backend/access/transam/xlogrecovery.c */ + variable('tablespaceinfo', 'InitWalRecovery', 'tablespaces'), + variable('TimeLineHistoryEntry', 'rescanLatestTimeLine', 'newExpectedTLEs'), + variable('TimeLineHistoryEntry', 'XLogFileReadAnyTLI', 'tles'), + variable('TimeLineHistoryEntry', 'checkTimeLineSwitch', 'exptectedTLEs'), + variable('TimeLineHistoryEntry', 'WaitForWALToBecomeAvailable', 'exptectedTLEs'), + + /* src/backend/backup/backup_manifest.c */ + variable('TimeLineHistoryEntry', 'AddWALInfoToBackupManifest', 'timelines'), + + /* src/backend/backup/basebackup_copy.c */ + variable('tablespaceinfo', 'SendTablespaceList', 'tablespaces'), + + /* src/backend/backup/basebackup_incremental.c */ + variable('TimeLineHistoryEntry', 'PrepareForIncrementalBackup', 'expectedTLEs'), + variable('TimeLineHistoryEntry', 'PrepareForIncrementalBackup', 'required_wslist'), + + /* src/backend/backup/basebackup_target.c */ + variable('BaseBackupTargetType', 'BaseBackupAddTarget', 'BaseBackupTargetTypeList'), + variable('BaseBackupTargetType', 'BaseBackupGetTargetHandle', 'BaseBackupTargetTypeList'), + + /* src/backend/{backup/relation}/basebackup.c */ + member('tablespaceinfo', 'bbsink_state', 'tablespaces'), + variable('char', 'perform_base_backup', 'walFileList'), + variable('char', 'perform_base_backup', 'historyFileList'), + variable('tablespaceinfo', 'perform_base_backup', 'tablespaces'), + variable('tablespaceinfo', 'SendBackupHeader', 'tablespaces'), + variable('tablespaceinfo', 'sendDir', 'tablespaces'), + + /* src/backend/backup/walsummary.c */ + variable('WalSummaryFile', 'FilterWalSummaries', 'wslist'), + variable('WalSummaryFile', 'WalSummariesAreComplete', 'wslist'), + variable('WalSummaryFile', 'GetWalSummaries', 'result'), + + /* src/backend/backup/walsummaryfuncs.c */ + variable('WalSummaryFile', 'pg_available_wal_summaries', 'wslist'), + + /* src/backned/bootstrap/bootstrap.c */ + variable('strcut typmap', 'gettype', 'Typ'), + variable('strcut typmap', 'boot_get_type_io_data', 'Typ'), + + /* src/backend/catalog/heap.c */ + variable('CookedConstraint', 'StoreConstraints', 'cooked_constraints'), + variable('char', 'AddRelationNewConstraints', 'checknames'), + variable('char', 'AddRelationRawConstraints', 'checknames'), + variable('RawColumnDefault', 'AddRelationNewConstraints', 'newColDefaults'), + variable('RelationData', 'heap_truncate', 'relations'), + variable('RelationData', 'heap_truncate_check_FKs', 'relations'), + + /* src/backend/catalog/index.c */ + variable('char', 'ConstructTupleDescriptor', 'indexColNames'), + variable('char', 'index_create', 'indexColNames'), + + /* srck/backend/catalog/namespace.c */ + variable('char', 'MatchNamedCall', 'argnames'), + variable('char', 'preprocessNamespacePath', 'namelist'), + variable('char', 'FuncnameGetCandidates', 'argnames'), + variable('char', 'recomputeNamespacePath', 'namelist'), + + /* src/backend/catalog/objectaddress.c */ + variable('char', 'strlist_to_textarray', 'list'), + variable('char', 'pg_identify_object_as_address', 'names'), + variable('char', 'pg_identify_object_as_address', 'args'), + + /* src/backend/catalog/pg_constraint.c */ + variable('char', 'ChooseConstraintName', 'others'), + + /* src/backend/catalog/pg_subscription.c */ + variable('SubscriptionRelState', 'GetSubscriptionRelations', 'res'), + variable('SubscriptionRelState', 'GetSubscriptionNotReadyRelations', 'res'), + + /* src/backend/catalog/pg_publication.c */ + variable('published_rel', 'is_ancestor_member_tableinfos', 'table_infos'), + variable('published_rel', 'filter_partitions', 'table_infos'), + variable('published_rel', 'pg_get_publication_tables', 'table_infos'), + + /* src/backend/catalog/pg_shdepend.c */ + variable('remoteDep', 'checkSharedDependencies', 'remDeps'), + + /* src/backend/commands/async.c */ + member('ListenAction', 'ActionList', 'actions'), + variable('char', 'Exec_ListenCommit', 'listenChannels'), + variable('char', 'IsListeningOn', 'listenChannels'), + member('Notification', 'NotificationList', 'events'), + variable('ListenAction', 'AtCommit_Notify', 'pendingActions'), + variable('char', 'AtCommit_Notify', 'pendingNotifies'), + + /* src/backend/commands/cluster.c */ + variable('RelToCluster', 'cluster_multiple_rels', 'rtcs'), + variable('RelToCluster', 'cluster', 'rtcs'), + variable('RelToCluster', 'cluster', 'rvs'), + variable('RelToCluster', 'get_tables_to_cluster', 'rtcs'), + variable('RelToCluster', 'get_tables_to_cluster', 'rvs'), + variable('RelToCluster', 'get_tables_to_cluster_partitioned', 'rtcs'), + + /* src/backend/commands/copyfrom.c */ + member('CopyMultiInsertBuffer', 'CopyMultiInsertInfo', 'multiInsertBuffers'), + + /* src/backend/commands/dbcommands.c */ + variable('CreateDBRelInfo', 'CreateDatabaseUsingWalLog', 'rlocatorlist'), + variable('CreateDBRelInfo', 'ScanSourceDatabasePgClass', 'rlocatorlist'), + variable('CreateDBRelInfo', 'ScanSourceDatabasePgClassPage', 'rlocatorlist'), + + /* src/backend/commands/event_trigger.c */ + variable('const char', 'filter_list_to_array', 'filterlist'), + variable('EventTriggerCacheItem', 'EventTriggerCommonSetup', 'cachelist'), + member('CollectedCommand', 'EventTriggerQueryState', 'commandList'), + member('char', 'SQLDropObject', 'addrnames'), + member('char', 'SQLDropObject', 'addrargs'), + + /* src/backend/commands/explain.c */ + variable('const char', 'show_tablesample', 'params'), + variable('char', 'show_incremental_sort_group_info', 'methodNames'), + variable('const char', 'ExplainPropertyList', 'data'), + variable('const char', 'ExplainPropertyListNested', 'data'), + variable('const char', 'show_plan_tlist', 'result'), + variable('const char', 'show_sort_group_keys', 'result'), + variable('const char', 'show_sort_group_keys', 'resultPresorted'), + variable('const char', 'show_incremental_sort_group_info', 'methodNames'), + variable('const char', 'show_modifytable_info', 'idxNames'), + + /* src/backend/commands/extension.c */ + member('ExtensionVersionInfo', 'ExtensionVersionInfo', 'reachable'), + member('char', 'ExtensionControlFile', 'no_relocate'), + member('char', 'ExtensionControlFile', 'requires'), + variable('ExtensionVersionInfo', 'get_nearest_unprocessed_vertex', 'evi_list'), + variable('ExtensionVersionInfo', 'find_update_path', 'evi_list'), + variable('ExtensionVersionInfo', 'find_install_path', 'evi_list'), + variable('ExtensionVersionInfo', 'identify_update_path', 'evi_list'), + variable('ExtensionVersionInfo', 'CreateExtensionInternal', 'evi_list'), + variable('char', 'get_required_extension', 'parents'), + variable('ExtensionVersionInfo', 'get_available_versions_for_extension', 'evi_list'), + variable('char', 'convert_requires_to_datum', 'requires'), + variable('ExtensionVersionInfo', 'pg_extension_update_paths', 'evi_list'), + variable('char', 'pg_extension_update_paths', 'path'), + variable('char', 'ApplyExtensionUpdates', 'updateVersions'), + variable('ExtensionVersionInfo', 'get_ext_ver_list', 'evi_list'), + + /* src/backend/commands/foreigncmds.c */ + variable('char', 'ImportForeignSchema', 'cmd_list'), + + /* src/backend/commands/indexcmds.c */ + variable('char', 'ChooseIndexNameAddition', 'colnames'), + variable('char', 'ChooseIndexColumnNames', 'result'), + variable('ReindexIndexInfo', 'ReindexRelationConcurrently', 'indexIds'), + variable('ReindexIndexInfo', 'ReindexRelationConcurrently', 'newIndexIds'), + variable('LOCKTAG', 'ReindexRelationConcurrently', 'lockTags'), + variable('LockRelId', 'ReindexRelationConcurrently', 'relationLocks'), + variable('char', 'ChooseIndexName', 'colnames'), + variable('char', 'DefineIndex', 'indexColNames'), + + /* src/backend/commands/opclasscmds.c */ + variable('OpFamilyMember', 'DefineOpClass', 'operators'), + variable('OpFamilyMember', 'DefineOpClass', 'procedures'), + variable('OpFamilyMember', 'storeOperators', 'operators'), + variable('OpFamilyMember', 'storeProcedures', 'procedures'), + variable('OpFamilyMember', 'dropProcedures', 'procedures'), + + /* src/backend/commands/publicationcmds.c */ + variable('char', 'parse_publication_options', 'publish_list'), + variable('PublicationRelInfo', 'TransformPubWhereClauses', 'tables'), + variable('PublicationRelInfo', 'CheckPubRelationColumnList', 'tables'), + variable('PublicationRelInfo', 'AlterPublicationTables', 'rels'), + variable('PublicationRelInfo', 'CloseTableList', 'rels'), + variable('PublicationRelInfo', 'PublicationAddTables', 'rels'), + variable('PublicationRelInfo', 'PublicationDropTables', 'rels'), + variable('PublicationRelInfo', 'CreatePublication', 'rels'), + variable('PublicationRelInfo', 'OpenTableList', 'rels'), + /* old versions have plain 'Relation' instead of 'PublicationRelInfo' */ + + /* src/backend/commands/seclabel.c */ + variable('LabelProvider', 'ExecSecLabelStmt', 'label_provider_list'), + + /* src/backend/commands/subscriptioncmds.c */ + variable('SubscriptionRelState', 'AlterSubscription_refresh', 'subrel_states'), + variable('LogicalRepWorker', 'DropSubscription', 'subworkers'), + variable('SubscriptionRelState', 'DropSubscription', 'rstates'), + variable('SubscriptionRelState', 'ReportSlotConnectionError', 'rstates'), + + /* src/backend/commands/tablecmds.c */ + variable('CookedConstraint', 'MergeCheckConstraint', 'constraints'), + member('NewConstraint', 'AlteredTableInfo', 'constraints'), + member('NewColumnValue', 'AlteredTableInfo', 'newvals'), + member('char', 'AlteredTableInfo', 'changedIndexDefs'), + member('char', 'AlteredTableInfo', 'changedConstraintDefs'), + member('char', 'AlteredTableInfo', 'changedStatisticsDefs'), + member('RelationData', 'ForeignTruncateInfo', 'rels'), + variable('const char', 'ChooseForeignKeyConstraintNameAddition', 'colnames'), + variable('CookedConstraint','ATAddCheckConstraint', 'newcons'), + variable('OnCommitItem', 'remove_on_commit_action', 'on_commits'), + variable('OnCommitItem', 'PreCommit_on_commit_actions', 'on_commits'), + variable('OnCommitItem', 'AtEOXact_on_commit_actions', 'on_commits'), + variable('OnCommitItem', 'AtEOSubXact_on_commit_actions', 'on_commits'), + variable('CookedConstraint', 'MergeAttributes', 'constraints'), + variable('RelationData', 'ExecuteTruncateGuts', 'rels'), + variable('RelationData', 'ExecuteTruncate', 'rels'), + variable('char', 'RemoveInheritance', 'connames'), + + /* src/backend/commands/tablespace.c */ + variable('char', 'check_temp_tablespaces', 'namelist'), + variable('char', 'PrepareTempTablespaces', 'namelist'), + + /* src/backend/commands/trigger.c */ + member('AfterTriggersTableData', 'AfterTriggersQueryData', 'tables'), + variable('AfterTriggersTableData', 'AfterTriggerFreeQuery', 'tables'), + + /* src/backend/commands/tsearchcmds.c */ + variable('TSTokenTypeItem', 'tstoken_list_member', 'tokens'), + variable('TSTokenTypeItem', 'MakeConfigurationMapping', 'tokens'), + variable('TSTokenTypeItem', 'getTokenTypes', 'result'), + + /* src/backend/commands/typecmds.c */ + variable('RelToCheck', 'validateDomainNotNullConstraint', 'rels'), + variable('RelToCheck', 'validateDomainCheckConstraint', 'rels'), + variable('RelToCheck', 'get_rels_with_domain', 'result'), + + /* src/backend/commands/user.c */ + variable('char', 'check_createrole_self_grant', 'elemlist'), + + /* src/backend/commands/variable.c */ + variable('char', 'check_datestyle', 'elemlist'), + variable('char', 'assign_datestyle', 'elemlist'), + + /* src/backend/executor/execMain.c */ + member('ExecAuxRowMark', 'EPQState', 'arowMarks'), + + /* src/backend/executor/execPartition.c */ + variable('PartitionPruneStepOp', 'InitPartitionPruneContext', 'pruning_steps'), + member('PartitionPruneStepOp', 'PartitionedRelPruneInfo', 'initial_pruning_steps'), + member('PartitionPruneStepOp', 'PartitionedRelPruneInfo', 'exec_pruning_steps'), + member('PartitionPruneStepOp', 'PartitionedRelPruningData', 'initial_pruning_steps'), + member('PartitionPruneStepOp', 'PartitionedRelPruningData', 'exec_pruning_steps'), + + /* src/include/nodes/execnodes.h */ + member('ResultRelInfoExtra', 'EState', 'es_resultrelinfo_extra'), + member('execRowMark', 'EState', 'es_rowMark'), + + /* src/backend/executor/functions.c */ + variable('execution_state', 'fmgr_sql', 'eslist'), + member('execution_state', 'SQLFunctionCachePtr', 'func_state'), + variable('execution_state', 'init_execution_state', 'eslist'), + + /* src/backend/executor/nodeLockRows.c */ + member('ExecAuxRowMark', 'LockRowsState', 'lr_arowMarks'), + variable('ExecAuxRowMark', 'ExecInitLockRows', 'epq_arowmarks'), + + /* src/backend/executor/nodeTidrangescan.c */ + member('TidOpExpr', 'TidRangeScanState', 'trss_tidexprs'), + + /* src/backend/executor/nodeTidscan.c */ + member('TidExpr', 'TidScanState', 'tss_tidexprs'), + + /* src/backend/executor/spi.c */ + member('CachedPlanSource', 'SPIPlanPtr', 'plancache_list'), + variable('CachedPlanSource', '_SPI_prepare_plan', 'plancache_list'), + + /* src/backend/jit/llvm/llvmjit.c */ + variable('LLVMJitHandle', 'LLVMJitContext', 'handles'), + + /* src/include/libpq/hba.h */ + member('char', 'HbaLine', 'radiusservers'), + member('char', 'HbaLine', 'radiussecrets'), + member('char', 'HbaLine', 'radiusidentifiers'), + member('char', 'HbaLine', 'radiusports'), + member('AuthToken', 'HbaLine', 'databases'), + member('AuthToken', 'HbaLine', 'roles'), + + /* src/backend/libpq/hba.c */ + variable('char', 'tokenize_expand_file', 'inc_lines'), + variable('AuthToken', 'tokenize_expand_file', 'inc_tokens'), + variable('AuthToken', 'tokenize_expand_file', 'tokens'), + variable('AuthToken', 'check_role', 'tokens'), + variable('AuthToken', 'check_db', 'tokens'), + variable('AuthToken', 'parse_hba_line', 'tokens'), + /* old versions have 'HbaToken' instead of 'AuthToken' */ + variable('char', 'parse_hba_auth_opt', 'parsed_servers'), + variable('char', 'parse_hba_auth_opt', 'parsed_ports'), + variable('HbaLine', 'check_hba', 'parsed_hba_lines'), + variable('IdentLine', 'check_usermap', 'parsed_ident_lines'), + variable('TokenizedAuthLine', 'load_hba', 'hba_lines'), + variable('TokenizedAuthLine', 'load_ident', 'ident_lines'), + + /* old versions have 'TokenizedLine' instead of 'TokenizedAuthLine' */ + variable('HbaToken', 'tokenize_inc_file', 'inc_tokens'), + + /* src/backend/libpq/pqcomm.c */ + variable('char', 'TouchSocketFiles', 'sock_paths'), + variable('char', 'RemoveSocketFiles', 'sock_paths'), + + /* src/backend/optimizer/geqo/geqo_eval.c */ + variable('Clump', 'gimme_tree', 'clumps'), + variable('Clump', 'merge_clump', 'clumps'), + + /* src/backend/optimizer/path/allpaths.c */ + variable('OpBtreeInterpretation', 'find_window_run_conditions', 'opinfos'), + + /* src/backend/optimizer/prep/prepjointree.c */ + member('reduce_outer_joins_state', 'reduce_outer_joins_state', 'sub_states'), + member('reduce_outer_joins_pass1_state', 'reduce_outer_joins_pass1_state', 'sub_states'), + member('reduce_outer_joins_partial_state', 'reduce_outer_joins_pass2_state', 'sub_states'), + + /* src/include/nodes/pathnodes.h */ + member('MergeScanSelCache', 'RestrictInfo', 'scansel_cache'), + + /* src/backend/utils/cache/lsyscache.c */ + variable('OpBtreeInterpretation', 'get_op_btree_interpretation', 'result'), + + /* src/backend/utils/cache/typcache.c */ + { + type: 'struct tupleDesc *', + member: ['RecordCacheEntry', 'tupdescs'] + }, + + /* src/backend/optimizer/util/predtest.c */ + variable('OpBtreeInterpretation', 'lookup_proof_cache', 'clause_op_infos'), + variable('OpBtreeInterpretation', 'lookup_proof_cache', 'pred_op_infos'), + + /* src/backend/optimizer/util/tlist.c */ + variable('split_pathtarget_item', 'add_sp_items_to_pathtargets', 'items'), + variable('split_pathtarget_item', 'split_pathtarget_at_srfs', 'level_srfs'), + variable('split_pathtarget_item', 'split_pathtarget_at_srfs', 'input_vars'), + variable('split_pathtarget_item', 'split_pathtarget_at_srfs', 'input_srfs'), + + /* src/backend/optimizer/util/plancat.c */ + member('PartitionSchemeData', 'PlannerInfo', 'part_schemes'), + + /* src/backend/parser/parse_clause.c */ + variable('ParseNamespaceItem', 'setNamespaceColumnVisibility', 'namespace'), + variable('ParseNamespaceItem', 'setNamespaceLateralState', 'namespace'), + variable('ParseNamespaceItem', 'transformFromClauseItem', 'my_namespace'), + variable('ParseNamespaceItem', 'transformFromClauseItem', 'r_namespace'), + variable('ParseNamespaceItem', 'transformFromClauseItem', 'l_namespace'), + variable('ParseNamespaceItem', 'transformFromClause', 'namespace'), + + /* src/backend/parser/parse_func.c */ + variable('char', 'ParseFuncOrColumn', 'argnames'), + variable('char', 'func_get_detail', 'fargnames'), + + /* src/backend/parser/parse_jsontable.c */ + member('char', 'JsonTableParseContext', 'pathNames'), + + /* src/backend/parser/parse_merge.c */ + variable('ParseNamespaceItem', 'setNamespaceVisibilityForRTE', 'namespace'), + + /* src/include/parser/parse_node.h */ + member('ParseNamespaceItem', 'ParseState', 'p_namespace'), + + /* src/backend/parser/parse_relation.c */ + variable('ParseNamespaceItem', 'checkNameSpaceConflicts', 'namespace1'), + variable('ParseNamespaceItem', 'checkNameSpaceConflicts', 'namespace2'), + + /* src/backend/partitioning/partbounds.c */ + variable('Datum', 'build_merged_partition_bounds', 'merged_datums'), + variable('PartitionRangeDatumKind', 'build_merged_partition_bounds', 'merged_kinds'), + variable('Datum', 'merge_list_bounds', 'marged_datums'), + + /* src/backend/partitioning/partprune.c */ + variable('PartClauseInfo', 'gen_prune_steps_from_opexps', 'eq_clauses'), + variable('PartClauseInfo', 'gen_prune_steps_from_opexps', 'le_clauses'), + variable('PartClauseInfo', 'gen_prune_steps_from_opexps', 'ge_clauses'), + variable('PartClauseInfo', 'gen_prune_steps_from_opexps', 'clauselist'), + variable('PartClauseInfo', 'get_steps_using_prefix_recurse', 'prefix'), + variable('PartClauseInfo', 'get_steps_using_prefix', 'prefix'), + + /* src/backend/postmaster/autovacuum.c */ + variable('avw_dbase', 'rebuild_database_list', 'dblist'), + variable('avw_dbase', 'do_start_worker', 'dblist'), + + /* src/backend/postmaster/postmaster.c */ + variable('char', 'PostmasterMain', 'elemlist'), + + /* src/backend/postmaster/syslogger.c */ + variable('save_buffer', 'process_pipe_input', 'buffer_list'), + variable('save_buffer', 'flush_pipe_input', 'list'), + + /* src/backend/postmaster/walsummarizer.c */ + variable('WalSummaryFile', 'GetOldestUnsummarizedLSN', 'existing_summaries'), + variable('WalSummaryFile', 'MayberemoveOldWalSummaries', 'wslist'), + + /* src/backend/replication/syncrep_gram.c */ + variable('char', 'create_syncrep_config', 'members'), + + /* src/backend/replication/logical/applyparallelworker.c */ + variable('ParallelApplyWorkerInfo', 'pa_launch_parallel_worker', 'ParallelApplyWorkerPool'), + variable('ParallelApplyWorkerInfo', 'pa_launch_parallel_worker', 'ParallelApplyWorkerPool'), + variable('ParallelApplyWorkerInfo', 'HandleParallelApplyMessages', 'ParallelApplyWorkerPool'), + + /* src/backend/replication/logical/launcher.c */ + variable('LogicalRepWorker', 'logicalrep_worker_detach','workers'), + variable('LogicalRepWorker', 'logicalrep_workers_find', 'res'), + variable('Subscription', 'ApplyLauncherMain', 'sublist'), + variable('Subscription', 'get_subscription_list', 'res'), + + /* src/backend/replication/logical/reorderbuffer.c */ + variable('RewriteMappingFile', 'UpdateLogicalMappings', 'files'), + + /* src/backend/replication/logical/tablesync.c */ + variable('SubscriptionRelState', 'process_syncing_tables_for_apply', 'table_states_not_ready'), + variable('SubscriptionRelState', 'FetchTableStates', 'rstates'), + + /* src/backend/replication/logical/worker.c */ + variable('LogicalRepRelMapEntry', 'apply_handle_truncate', 'remote_rels'), + variable('RelationData', 'apply_handle_truncate', 'part_rels'), + variable('RelationData', 'apply_handle_truncate', 'rels'), + variable('LogicalRepWorker', 'AtEOXact_LogicalRepWorkers', 'workers'), + + /* src/backend/replication/pgoutput/pgoutput.c */ + variable('Publication', 'pgoutput_row_filter_init', 'publications'), + variable('Publication', 'pgoutput_column_list_init', 'publications'), + variable('char', 'LoadPublications', 'pubnames'), + member('Publication', 'PGOutputData', 'publications'), + member('Publication', 'PGOutputData', 'publication_names'), + + /* src/backend/rewrite/rewriteHandler.c */ + variable('RewriteRule', 'rewriteValuesRTE', 'locks'), + variable('RewriteRule', 'fireRIRrules', 'locks'), + variable('RewriteRule', 'fireRules', 'locks'), + variable('rewrite_event', 'RewriteQuery', 'rewrite_events'), + variable('RewriteRule', 'matchLocks', 'matching_locks'), + + /* src/backend/rewrite/rowsecurity.c */ + member('RowSecurityPolicy', 'RowSecurityDesc', 'policies'), + variable('RowSecurityPolicy', 'add_security_quals', 'permissive_policies'), + variable('RowSecurityPolicy', 'add_security_quals', 'restrictive_policies'), + variable('RowSecurityPolicy', 'add_with_check_options', 'permissive_policies'), + variable('RowSecurityPolicy', 'add_with_check_options', 'restrictive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'update_permissive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'update_restrictive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'permissive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'restrictive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'select_permissive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'select_restrictive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'conflict_permissive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'conflict_restrictive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'conflict_select_permissive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'conflict_select_restrictive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'merge_update_permissive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'merge_update_restrictive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'merge_delete_permissive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'merge_delete_restrictive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'merge_insert_permissive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'merge_insert_restrictive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'merge_select_permissive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'merge_select_restrictive_policies'), + variable('RowSecurityPolicy', 'get_row_security_policies', 'hook_policies'), + + /* src/backend/statistics/extended_stats.c */ + variable('StatExtEntry', 'BuildRelationExtStatistics', 'statslist'), + variable('StatExtEntry', 'ComputeExtStatisticsRows', 'lstats'), + variable('StatExtEntry', 'fetch_statentries_for_relation', 'result'), + + /* src/backend/storage/file/fd.c */ + variable('char', 'check_debug_io_direct', 'elemlist'), + + /* src/backend/storage/file/sharedfileset.c */ + variable('SharedFileSet', 'SharedFileSetUnregister', 'filesetlist'), + variable('SharedFileSet', 'SharedFileSetInit', 'filesetlist'), + variable('SharedFileSet', 'SharedFileSetDeleteOnProcExit', 'filesetlist'), + + /* src/backend/storage/ipc/standby.c */ + variable('xl_standby_lock', 'StandbyReleaseLockList', 'locks'), + member('xl_standby_lock', 'RecoveryLockListsEntry', 'locks'), + + /* src/backend/storage/lmgr/lmgr.c */ + variable('LOCKTAG', 'WaitForLockersMultiple', 'locktags'), + variable('VirtualTransactionId', 'WaitForLockersMultiple', 'holders'), + + /* src/backend/storage/sync/sync.c */ + variable('PendingUnlinkEntry', 'SyncPostCheckpoint', 'pendingUnlinks'), + variable('PendingUnlinkEntry', 'RememberSyncRequest', 'pendingUnlinks'), + + /* src/backend/tcop/backend_startup.c */ + variable('const char', 'SendNegotiateProtocolVersion', 'unrecognized_protocol_options'), + variable('const char', 'ProcessStartupPacket', 'unrecognized_protocol_options'), + + /* src/backend/tcop/postgres.c */ + variable('char', 'check_restrict_nonsystem_relation_kind', 'elemlist'), + variable('char', 'PostgresMain', 'guc_names'), + variable('char', 'PostgresMain', 'guc_values'), + + /* src/backend/tsearch/wparser_def.c */ + variable('ExecPhraseData', 'hlCover', 'locations'), + variable('ExecPhraseData', 'mark_hl_fragments', 'locations'), + variable('ExecPhraseData', 'prsd_headline', 'locations'), + + /* src/backend/utils/adt/hbafuncs.c */ + variable('TokenizedAuthLine', 'fill_hba_view', 'hba_lines'), + variable('TokenizedAuthLine', 'fill_ident_view', 'ident_lines'), + variable('char', 'fill_hba_line', 'names'), + + /* src/backend/utils/adt/jsonb_gin.c */ + variable('JsonPathGinNode', 'make_jsp_expr_node_args', 'args'), + variable('JsonPathGinNode', 'extract_jsp_path_expr', 'nodes'), + variable('JsonPathGinNode', 'extract_jsp_path_expr_nodes', 'nodes'), + variable('JsonPathGinNode', 'jsonb_ops__extract_nodes', 'nodes'), + variable('JsonPathGinNode', 'jsonb_path_ops__extract_nodes', 'nodes'), + + /* src/backend/utils/adt/jsonpath_exec.c */ + variable('JsonPathVariable', 'GetJsonPathVar', 'vars'), + member('JsonValue', 'JsonValueList', 'list'), + + /* src/backend/utils/adt/regproc.c */ + variable('char', 'stringToQualifiedNameList', 'namelist'), + + /* src/backend/utils/adt/ruleutils.c */ + variable('char', 'pg_get_functiondef', 'namelist'), + variable('deparse_namespace', 'set_rtable_names', 'parent_namespaces'), + variable('deparse_namespace', 'set_deparse_for_query', 'parent_namespaces'), + member('char', 'deparse_namespace', 'rtable_names'), + member('char', 'deparse_namespace', 'using_names'), + member('deparse_columns', 'deparse_namespace', 'rtable_columns'), + member('char', 'deparse_columns', 'parentUsing'), + member('char', 'deparse_columns', 'usingNames'), + member('deparse_namespace', 'deparse_context', 'namespaces'), + variable('deparse_namespace', 'generate_relation_name', 'namespaces'), + variable('deparse_namespace', 'get_query_def', 'parentnamespace'), + variable('deparse_namespace', 'get_name_for_var_field', 'parent_namespaces'), + + /* src/backend/utils/adt/selfuncs.c */ + variable('GroupVarInfo', 'add_unique_group_var', 'varinfos'), + variable('GroupVarInfo', 'estimate_num_groups', 'varinfos'), + variable('GroupVarInfo', 'estimate_num_groups', 'relvarinfos'), + variable('GroupVarInfo', 'estimate_num_groups', 'newvarinfos'), + + /* src/backend/utils/adt/tsquery.c */ + member('QueryItem', 'TSQueryParserStateData', 'polstr'), + + /* src/backend/utils/adt/tsvector_op.c */ + variable('ExecPhraseData', 'TS_execute_locations_recurse', 'llocations'), + variable('ExecPhraseData', 'TS_execute_locations_recurse', 'rlocations'), + + /* src/backend/utils/adt/varlena.c */ + variable('char', 'textToQualifiedNameList', 'namelist'), + + /* src/backend/utils/adt/xml.c */ + variable('TupleDescData', 'map_sql_typecoll_to_xmlschema_types', 'tupdesc_list'), + variable('TupleDescData', 'schema_to_xmlschema_internal', 'tupdesc_list'), + variable('TupleDescData', 'database_to_xmlschema_internal', 'tupdesc_list'), + variable('char', 'xmlelement', 'arg_strings'), + variable('char', 'xmlelement', 'named_arg_strings'), + + /* src/backend/utils/cache/catcache.c */ + variable('CatCTup', 'SearchCatCacheList', 'ctlist'), + + /* src/backend/utils/cache/relcache.c */ + variable('RelationData', 'RelationCacheInvalidate', 'rebuildFirstList'), + variable('RelationData', 'RelationCacheInvalidate', 'rebuildList'), + + /* src/backend/utils/error/elog.c */ + variable('char', 'check_log_destination', 'elemlist'), + + /* src/backend/utils/fmgr/fmgr.c */ + member('char', 'fmgr_security_definer_cache', 'configNames'), + member('char', 'fmgr_security_definer_cache', 'configValues'), + + /* src/backend/utils/init/miscinit.c */ + variable('char', 'UnlinkLockFiles', 'lock_files'), + variable('char', 'TouchSocketLockFiles', 'lock_files'), + variable('char', 'load_libraries', 'elemlist'), + variable('char', 'process_preload_libraries', 'elemlist'), + + /* src/backend/utils/init/postinit.c */ + member('char', 'Port', 'guc_options'), + + /* src/backend/utils/mb/mbutils.c */ + variable('ConvProcInfo', 'PrepareClientEncoding', 'ConvProcList'), + variable('ConvProcInfo', 'SetClientEncoding', 'ConvProcList'), + + /* src/backend/utils/misc/guc.c */ + variable('const char', 'assignable_custom_variable_name', 'reserved_class_prefix'), + variable('char', 'check_wal_consistency_checking', 'elemlist'), + variable('char', 'check_log_destination', 'elemlist'), + variable('char', 'assign_log_destination', 'elemlist'), + + /* src/backend/utils/misc/queryenvironment.c */ + member('EphemeralNamedRelation', 'QueryEnvironment', 'namedRelList'), + + /* src/backend/utils/time/snapmgr.c */ + variable('ExportedSnapshot', 'AtEOXact_Snapshot', 'exportedSnapshots'), + + /* src/pl/plpgsql/src/plpgsql.h */ + member('PLpgSQL_exception', 'PLpgSQL_exception_block', 'exc_list'), + member('PLpgSQL_diag_item', 'PLpgSQL_stmt_getdiag', 'diag_items'), + member('PLpgSQL_if_elsif', 'PLpgSQL_stmt_if', 'elsif_list'), + member('PLpgSQL_case_when', 'PLpgSQL_stmt_case', 'case_when_list'), + member('PLpgSQL_raise_option', 'PLpgSQL_stmt_raise', 'options'), + member('PLpgSQL_expr', 'PLpgSQL_stmt_return_query', 'params'), + member('PLpgSQL_expr', 'PLpgSQL_stmt_open', 'params'), + member('PLpgSQL_expr', 'PLpgSQL_stmt_raise', 'params'), + member('PLpgSQL_raise_option', 'PLpgSQL_stmt_raise', 'options'), + member('PLpgSQL_expr', 'PLpgSQL_stmt_dynexecute', 'params'), + member('PLpgSQL_expr', 'PLpgSQL_stmt_dynfors', 'params'), + + /* src/pl/plpgsql/src/pl_exec.c */ + variable('PLpgSQL_stmt', 'exec_stmts', 'stmts'), + variable('PLpgSQL_expr', 'exec_eval_using_params', 'params'), + + /* src/pl/plpgsql/src/pl_funcs.c */ + variable('PLpgSQL_stmt', 'free_stmts', 'stmts'), + + /* src/pl/plpgsql/src/pl_handler.c */ + variable('char', 'plpgsql_extra_checks_check_hook', 'elemlist'), + + /* src/pl/tcl/pltcl.c */ + variable('char', 'pltcl_SPI_prepare', 'names'), + + /* src/test/modules/injection_points/injection_points.c */ + variable('char', 'injection_points_cleanup', 'inj_list_local'), + ] +} /** * Return array of known Node `typedef's. @@ -492,7 +1347,8 @@ export function getDefaultNodeTags(): string[] { */ export function getDefaultAliases(): [string, string][] { return [ - ['Relids', 'Bitmapset *'] + ['Relids', 'Bitmapset *'], + ['MemoryContext', 'MemoryContextData *'] ] } @@ -503,13 +1359,18 @@ export interface ArraySpecialMember { } export function getArraySpecialMembers(): ArraySpecialMember[] { - const _ = (typeName: string, memberName: string, lengthExpr: string) => ({ typeName, memberName, lengthExpr }); + const _ = (typeName: string, memberName: string, lengthExpr: string) => ({ + typeName, + memberName, + lengthExpr + }); return [ _('PlannerInfo', 'simple_rel_array', 'simple_rel_array_size'), _('PlannerInfo', 'simple_rte_array', 'simple_rel_array_size'), _('PlannerInfo', 'append_rel_array', 'simple_rel_array_size'), _('PlannerInfo', 'placeholder_array', 'placeholder_array_size'), + _('PlannerInfo', 'join_rel_level', 'join_cur_level'), _('ResultRelInfo', 'ri_IndexRelationInfo', 'ri_NumIndices'), _('ResultRelInfo', 'ri_TrigWhenExprs', 'ri_TrigDesc->numtriggers'), @@ -591,7 +1452,7 @@ export function getArraySpecialMembers(): ArraySpecialMember[] { _('Memoize', 'hashOperators', 'numKeys'), _('Memoize', 'collations', 'numKeys'), - + _('Sort', 'sortColIdx', 'numCols'), _('Sort', 'sortOperators', 'numCols'), _('Sort', 'collations', 'numCols'), @@ -631,8 +1492,8 @@ export function getArraySpecialMembers(): ArraySpecialMember[] { _('PartitionedRelPruneInfo', 'subplan_map', 'nparts'), _('PartitionedRelPruneInfo', 'subpart_map', 'nparts'), - _('PartitionedRelPruneInfo', 'relid_map', 'nparts'), - + _('PartitionedRelPruneInfo', 'relid_map', 'nparts'), + _('PLpgSQL_row', 'fieldnames', 'nfields'), _('PLpgSQL_stmt_block', 'initvarnoss', 'n_initvars'), _('PLpgSQL_function', 'datums', 'ndatums'), @@ -733,6 +1594,190 @@ export function getArraySpecialMembers(): ArraySpecialMember[] { _('TestSpec', 'setupsqls', 'nsetupsqls'), _('TestSpec', 'sessions', 'nsesssions'), - _('TestSpec', 'permutations', 'npermutations'), + _('TestSpec', 'permutations', 'npermutations'), + ]; +} + +/* + * + * Computed like this: + * + * type->field: + * - ind1 = (path->path->path)[ind1 - (indexDelta ?? 0)] + * - ind2 = (path->path->path)[ind2 - (indexDelta ?? 0)] + * - ind3 = (path->path->path)[ind3 - (indexDelta ?? 0)] + */ +export interface BitmapsetReference { + /* + * Type in which this Bitmapset stored. + */ + type: string; + /* + * Field name of this Bitmapset member + */ + field: string; + /* + * Paths to fields to which this bitmapset refers. + * Examined starting from PlannerInfo. + */ + paths: { + /* + * One of possible paths to referer + */ + path: string[], + /* + * Delta to apply to result number for element in set. + * Useful i.e. for rtable index in RelOptInfo->relids. + * By default (not set) - 0 + */ + indexDelta?: number; + }[]; + + /* + * From which element search should be started (accessing via `paths'). + * 'PlannerInfo' - search in parents until reach 'PlannerInfo' + * (PlannerInfo->...->elem->bms - search PlannerInfo's fields) + * 'Parent' - from direct parent of containing element + * (parent-elem->bms - search parent's fields) + * 'Self' - search directly in containing element + * (elem->bms - search in elem's fields) + */ + start?: 'PlannerInfo' | 'Parent' | 'Self' ; +} + +export function getWellKnownBitmapsetReferences(): [string, BitmapsetReference][] { + const pathToRangeTable = ['parse', 'rtable']; + const pathToRelOptInfos = ['simple_rel_array']; + const pathToRteAndRelOptInfos = [{path: pathToRangeTable, indexDelta: -1}, + {path: pathToRelOptInfos}]; + const ref = (type: string, field: string, + paths: {path: string[], indexDelta?: number}[], + start?: 'PlannerInfo' | 'Parent' | 'Self'): [string, BitmapsetReference] => + [ field, { type, field, paths, start } ]; + + return [ + ref('RelOptInfo', 'relids', pathToRteAndRelOptInfos), + ref('RelOptInfo', 'eclass_indexes', [{path: ['eclasses']}]), + ref('RelOptInfo', 'nulling_relids', pathToRteAndRelOptInfos), + ref('RelOptInfo', 'direct_lateral_relids', pathToRteAndRelOptInfos), + ref('RelOptInfo', 'lateral_relids', pathToRteAndRelOptInfos), + ref('RelOptInfo', 'lateral_referencers', pathToRteAndRelOptInfos), + ref('RelOptInfo', 'top_parent_relids', pathToRteAndRelOptInfos), + ref('RelOptInfo', 'live_parts', [{path: ['part_rels']}], 'Self'), + ref('RelOptInfo', 'all_partrels', pathToRteAndRelOptInfos), + + ref('JoinDomain', 'jd_relids', pathToRteAndRelOptInfos), + + ref('EquivalenceClass', 'ec_relids', pathToRteAndRelOptInfos), + + ref('EquivalenceMember', 'em_relids', pathToRteAndRelOptInfos), + + ref('PlannerInfo', 'all_baserels', pathToRteAndRelOptInfos, 'Self'), + ref('PlannerInfo', 'outer_join_rels', pathToRteAndRelOptInfos, 'Self'), + ref('PlannerInfo', 'all_query_rels', pathToRteAndRelOptInfos, 'Self'), + ref('PlannerInfo', 'all_result_relids', pathToRteAndRelOptInfos, 'Self'), + ref('PlannerInfo', 'leaf_result_relids', pathToRteAndRelOptInfos, 'Self'), + ref('PlannerInfo', 'curOuterRels', pathToRteAndRelOptInfos, 'Self'), + + ref('ParamPathInfo', 'ppi_req_outer', pathToRteAndRelOptInfos), + + ref('RestrictInfo', 'required_relids', pathToRteAndRelOptInfos), + ref('RestrictInfo', 'clause_relids', pathToRteAndRelOptInfos), + ref('RestrictInfo', 'incompatible_relids', pathToRteAndRelOptInfos), + ref('RestrictInfo', 'outer_relids', pathToRteAndRelOptInfos), + ref('RestrictInfo', 'left_relids', pathToRteAndRelOptInfos), + ref('RestrictInfo', 'right_relids', pathToRteAndRelOptInfos), + + ref('PlaceHolderVar', 'phrelds', pathToRteAndRelOptInfos), + ref('PlaceHolderVar', 'phnullingrels', pathToRteAndRelOptInfos), + + ref('SpecialJoinInfo', 'min_lefthand', pathToRteAndRelOptInfos), + ref('SpecialJoinInfo', 'min_righthand', pathToRteAndRelOptInfos), + ref('SpecialJoinInfo', 'syn_lefthand', pathToRteAndRelOptInfos), + ref('SpecialJoinInfo', 'syn_righthand', pathToRteAndRelOptInfos), + ref('SpecialJoinInfo', 'compute_above_l', pathToRteAndRelOptInfos), + ref('SpecialJoinInfo', 'compute_above_r', pathToRteAndRelOptInfos), + ref('SpecialJoinInfo', 'compute_below_l', pathToRteAndRelOptInfos), + ref('SpecialJoinInfo', 'compute_below_r', pathToRteAndRelOptInfos), + + ref('RowIdentifyVarInfo', 'rowidrels', pathToRteAndRelOptInfos), + + ref('PlaceHolderInfo', 'ph_evalat', pathToRteAndRelOptInfos), + ref('PlaceHolderInfo', 'ph_lateral', pathToRteAndRelOptInfos), + ref('PlaceHolderInfo', 'ph_needed', pathToRteAndRelOptInfos), + + ref('JoinPathExtraData', 'param_source_rels', pathToRteAndRelOptInfos), + + ref('PlannedStmt', 'rewindPlanIDs', [{path: ['subplans']}], 'Self'), + + ref('ModifyTable', 'fdwDirectModifyPlans', [{path: ['resultRelations']}], 'Self'), + + ref('Append', 'apprelids', pathToRteAndRelOptInfos), + ref('MergeAppend', 'apprelids', pathToRteAndRelOptInfos), + + ref('ForeignScan', 'fs_relids', pathToRteAndRelOptInfos), + ref('ForeignScan', 'fs_base_relids', pathToRteAndRelOptInfos), + + ref('CustomScan', 'custom_relids', pathToRteAndRelOptInfos), + + ref('Var', 'varnullingrels', pathToRteAndRelOptInfos), + + ref('AppendState', 'as_asyncplans', [{path: ['as_asyncrequests']}], 'Self'), + ref('AppendState', 'as_needrequest', [{path: ['as_asyncrequests']}], 'Self'), + ref('AppendState', 'as_valid_subplans', [{path: ['as_asyncrequests']}], 'Self'), + ref('AppendState', 'as_valid_asyncplans', [{path: ['as_asyncrequests']}], 'Self'), + + ref('MergeAppendState', 'ms_valid_subplans', [{path: ['mergeplans']}], 'Self'), + ] +} + +export function getWellKnownHTABTypes(): HtabEntryInfo[] { + const type = (parent: string, member: string, type: string) => ({ + parent, member, type + } as HtabEntryInfo); + + return [ + type('PlannerInfo', 'join_rel_hash', 'JoinHashEntry *'), + type('RewriteStateData', 'rs_unresolved_tups', 'UnresolvedTupData *'), + type('RewriteStateData', 'rs_old_new_tid_map', 'OldToNewMappingData *'), + type('TIDBitmap', 'pagetable', 'PagetableEntry *'), + type('do_autovacuum', 'table_toast_map', 'av_relation *'), + type('rebuild_database_list', 'dbhash', 'avl_dbase *'), + type('compute_tsvector_stats', 'lexemes_tab', 'TrackItem *'), + type('prune_lexemes_hashtable', 'lexemes_tab', 'TrackItem *'), + type('TupleHashTableData', 'hashtab', 'TupleHashEntryData *'), + type('plperl_interp_desc', 'query_hash', 'plperl_query_entry *'), + type('PgStat_StatDBEntry', 'tables', 'PgStat_StatTabEntry *'), + type('PgStat_StatDBEntry', 'functions', 'PgStat_StatFuncEntry *'), + ]; +} + +export function getWellKnownSimpleHashTableTypes(): SimplehashEntryInfo[] { + const type = (prefix: string, elementType: string, canIterate: boolean = true) => ({ + prefix, elementType, canIterate + } as SimplehashEntryInfo); + + return [ + type('blockreftable', 'BlockRefTableEntry *'), + type('filehash', 'file_entry_t *'), + type('manifest_files', 'manifest_file *'), + type('memoize', 'MemoizeEntry *'), + type('pagetable', 'PagetableEntry *'), + type('pgstat_entry_ref_hash', 'PgStat_EntryRefHashEntry *'), + type('tuplehash', 'TupleHashEntryData *'), + + /* + * These simple hash tables have iteration logic trimmed, + * but leave it to show, that I hadn't forgotten it. + */ + type('backup_file', 'backup_file_entry *', false), + type('catalogid', 'CatalogIdMapEntry *', false), + type('collation_cache', 'collation_cache_entry *', false), + type('derives', 'ECDerivesEntry *', false), + type('keepwal', 'keepwal_entry *', false), + type('nsphash', 'SearchPathCacheEntry *', false), + type('pgstat_snapshot', 'PgStat_SnapshotEntry *', false), + type('rolename', 'RoleNameEntry *', false), + type('saophash', 'ScalarArrayOpExprHashEntry *', false), ]; -} \ No newline at end of file +} diff --git a/src/dap.d.ts b/src/dap.d.ts index 57f243f..ad747e3 100644 --- a/src/dap.d.ts +++ b/src/dap.d.ts @@ -128,7 +128,7 @@ export interface DebugVariable { * This attribute may be returned by a debug adapter if corresponding * capability `supportsMemoryReferences` is true. */ - memoryReference: string; + memoryReference?: string; } export interface VariablesResponse { @@ -154,19 +154,6 @@ export interface Scope { */ name: string; - /** - * A hint for how to present this scope in the UI. If this attribute is - * missing, the scope is shown with a generic UI. - * Values: - * 'arguments': Scope contains method arguments. - * 'locals': Scope contains local variables. - * 'registers': Scope contains registers. Only a single `registers` scope - * should be returned from a `scopes` request. - * 'returnValue': Scope contains one or more return values. - * etc. - */ - presentationHint?: 'arguments' | 'locals' | 'registers' | 'returnValue' | string; - /** * The variables of this scope can be retrieved by passing the value of * `variablesReference` to the `variables` request as long as execution @@ -182,4 +169,97 @@ export interface ScopesResponse { * scopes available. */ scopes: Scope[]; -} \ No newline at end of file +} + +export interface ProtocolMessage { + type: 'event' | 'response' | 'request' | string; + + /** + * Type of event, if type === 'event' + */ + event?: 'stopped' | string; + + /** + * The command requested or executed. + */ + command?: 'continue' | string; + + /** + * Event-specific information. + */ + body?: any; +} + +export interface StackTraceArguments { + /** + * Retrieve the stacktrace for this thread. + */ + threadId: number; + + /** + * The maximum number of frames to return. If levels is not specified or 0, + * all frames are returned. + */ + levels?: number; + + /** + * The index of the first frame to return; if omitted frames start at 0. + */ + startFrame?: number; +} + +export interface StackFrame { + /** + * An identifier for the stack frame. It must be unique across all threads. + * This id can be used to retrieve the scopes of the frame with the `scopes` + * request or to restart the execution of a stack frame. + */ + id: number; + + /** + * The name of the stack frame, typically a method name. + */ + name: string; +} + +export interface StackTraceResponse { + /** + * The frames of the stack frame. If the array has length zero, there are no + * stack frames available. + * This means that there is no location information available. + */ + stackFrames: StackFrame[]; + + /** + * The total number of frames available in the stack. If omitted or if + * `totalFrames` is larger than the available frames, a client is expected + * to request frames until a request returns less frames than requested + * (which indicates the end of the stack). Returning monotonically + * increasing `totalFrames` values for subsequent requests can be used to + * enforce paging in the client. + */ + totalFrames?: number; +} + +export interface ThreadsRequest extends Request { + command: 'threads'; +} + +export interface ThreadsResponse { + /** + * All threads. + */ + threads: Thread[]; +} + +export interface Thread { + /** + * Unique identifier for the thread. + */ + id: number; + + /** + * The name of the thread. + */ + name: string; +} diff --git a/src/entrypoint.ts b/src/entrypoint.ts index 1660da4..e57d899 100644 --- a/src/entrypoint.ts +++ b/src/entrypoint.ts @@ -1,163 +1,118 @@ import * as vscode from 'vscode'; import * as vars from './variables'; import * as utils from './utils'; -import { NodePreviewTreeViewProvider, dumpVariableToLogCommand, Configuration as config, setupConfigFiles } from './extension'; - -async function setupNodeTagFiles(execCtx: vars.ExecContext, log: utils.ILogger, - context: vscode.ExtensionContext): Promise { - const section = vscode.workspace.getConfiguration(config.ConfigSections.TopLevelSection); - const nodeTagFiles = section.get(config.ConfigSections.NodeTagFiles); - - if (!(nodeTagFiles && 0 < nodeTagFiles.length)) { - const fullSectionName = config.ConfigSections.fullSection(config.ConfigSections.NodeTagFiles); - log.error(`no NodeTag files defined. check ${fullSectionName} setting`); - return; +import * as formatter from './formatter'; +import { + NodePreviewTreeViewProvider as PgVariablesView, + Configuration as config, + getCurrentLogLevel, + setupExtension +} from './extension'; + +function createDebugFacade(context: vscode.ExtensionContext) { + const debug = new utils.VsCodeDebuggerFacade(); + if (!utils.Features.hasEvaluateArrayLength()) { + debug.switchToManualArrayExpansion(); } + context.subscriptions.push(debug); + return debug; +} - const handleNodeTagFile = async (path: vscode.Uri) => { - if (!await utils.fileExists(path)) { - return; - } - - log.debug(`processing ${path.fsPath} NodeTags file`); - const document = await vscode.workspace.openTextDocument(path) - try { - const added = execCtx.nodeVarRegistry.updateNodeTypesFromFile(document); - log.debug(`added ${added} NodeTags from ${path.fsPath} file`); - } catch (err: any) { - log.error(`could not initialize node tags array`, err); +function createLogger(context: vscode.ExtensionContext): utils.ILogger { + let outputChannel; + let logger; + + if (utils.Features.hasLogOutputChannel()) { + outputChannel = vscode.window.createOutputChannel(config.ExtensionPrettyName, {log: true}); + logger = new utils.VsCodeLogger(outputChannel); + } else { + if (utils.Features.logOutputLanguageEnabled()) { + outputChannel = vscode.window.createOutputChannel(config.ExtensionPrettyName, 'log'); + } else { + outputChannel = vscode.window.createOutputChannel(config.ExtensionPrettyName); } + + const logLevelConfigSection = config.ConfigSections.LogLevel; + const fullConfigSectionName = config.getFullConfigSection(logLevelConfigSection); + const vsLogger = new utils.ObsoleteVsCodeLogger(outputChannel, getCurrentLogLevel()); + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => { + if (!event.affectsConfiguration(fullConfigSectionName)) { + return; + } + + vsLogger.minLogLevel = getCurrentLogLevel(); + }, undefined, context.subscriptions)); + logger = vsLogger; } - const setupSingleFolder = async (folder: vscode.WorkspaceFolder) => { - await Promise.all(nodeTagFiles.map(async filePath => { - await handleNodeTagFile(vscode.Uri.file(folder.uri.fsPath + '/' + filePath)); - - /* - * Create watcher to handle file updates and creations, but not deletions. - * This is required, because extension can be activated before running - * of 'configure' script and NodeTags are not created at that moment. - * We will handle them later - */ - const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(folder, filePath), false, false, true); - watcher.onDidChange(uri => { - log.info(`detected change in NodeTag file: ${uri.fsPath}`); - handleNodeTagFile(uri); - }, context.subscriptions); - watcher.onDidCreate(uri => { - log.info(`detected creation of NodeTag file: ${uri.fsPath}`); - handleNodeTagFile(uri); - }, context.subscriptions); - - context.subscriptions.push(watcher); - })); - } - - if (vscode.workspace.workspaceFolders?.length) { - await Promise.all( - vscode.workspace.workspaceFolders.flatMap(async folder => - await setupSingleFolder(folder) - ) - ); - } - - vscode.workspace.onDidChangeWorkspaceFolders(async e => { - for (let i = 0; i < e.added.length; i++) { - const folder = e.added[i]; - await setupSingleFolder(folder); - } - }, undefined, context.subscriptions); + context.subscriptions.push(outputChannel); + return logger; } -function createNodeVariablesDataProvider(logger: utils.VsCodeLogger, debug: utils.VsCodeDebuggerFacade, context: vscode.ExtensionContext) { - const nodeRegistry = new vars.NodeVarRegistry(); - const execCtx: vars.ExecContext = { - debug, - nodeVarRegistry: nodeRegistry, - specialMemberRegistry: new vars.SpecialMemberRegistry(), - } - - /* Support for configuration files */ - setupConfigFiles(execCtx, logger, context); - - /* Support for NodeTag files */ - setupNodeTagFiles(execCtx, logger, context); - - return new NodePreviewTreeViewProvider(logger, execCtx); +function createPostgresVariablesView(context: vscode.ExtensionContext, + logger: utils.ILogger, + nodeVars: vars.NodeVarRegistry, + specialMembers: vars.SpecialMemberRegistry, + debug: utils.VsCodeDebuggerFacade, + hashTableTypes: vars.HashTableTypes) { + const nodesView = new PgVariablesView(logger, nodeVars, + specialMembers, debug, hashTableTypes); + const nodesViewName = config.Views.NodePreviewTreeView; + const treeDisposable = vscode.window.registerTreeDataProvider(nodesViewName, + nodesView); + context.subscriptions.push(treeDisposable); + return nodesView; } -function createLogger(context: vscode.ExtensionContext): utils.VsCodeLogger { - const outputChannel = vscode.window.createOutputChannel(config.ExtensionPrettyName, 'log'); - const configuration = vscode.workspace.getConfiguration(config.ConfigSections.TopLevelSection); - const getLogLevel = () => { - const configValue = configuration.get(config.ConfigSections.LogLevel); - if (typeof configValue !== 'string') { - return utils.LogLevel.Info; - } - switch (configValue) { - case 'INFO': - return utils.LogLevel.Info; - case 'DEBUG': - return utils.LogLevel.Debug; - case 'WARNING': - return utils.LogLevel.Warn; - case 'ERROR': - return utils.LogLevel.Error; - case 'DISABLE': - return utils.LogLevel.Disable; - default: - outputChannel.appendLine(`Unknown log level '${configValue}' - setting to 'INFO'`); - return utils.LogLevel.Info; - } - } - const logger = new utils.VsCodeLogger(outputChannel, getLogLevel()); - const fullConfigSectionName = config.ConfigSections.fullSection(config.ConfigSections.LogLevel); - vscode.workspace.onDidChangeConfiguration(event => { - if (!event.affectsConfiguration(fullConfigSectionName)) { - return; - } - - logger.minLogLevel = getLogLevel(); - }, undefined, context.subscriptions); +function setupDebugger( + dataProvider: PgVariablesView, + logger: utils.ILogger, + debug: utils.VsCodeDebuggerFacade, + context: vscode.ExtensionContext) { + + if (utils.Features.debugFocusEnabled()) { + vscode.debug.onDidChangeActiveStackItem(() => dataProvider.refresh(), + undefined, context.subscriptions); + } else { + logger.warn( + 'Current version of VS Code (%s) do not support ' + + 'debugFocus API, falling back to compatible event-based implementation. ' + + 'Some features might be not accessible. ' + + 'Please update VS Code to version 1.90 or higher', vscode.version + ); - context.subscriptions.push(outputChannel); - return logger; + debug.switchToEventBasedRefresh(context, dataProvider); + } + return; } export function activate(context: vscode.ExtensionContext) { const logger = createLogger(context); - logger.info('Extension is activating'); - const debug = new utils.VsCodeDebuggerFacade(); - - /* Register command to dump variable to log */ - const dumpVarsToLogCmd = vscode.commands.registerCommand(config.Commands.DumpNodeToLog, async (args) => { - try { - await dumpVariableToLogCommand(args, logger, debug); - } catch (err: any) { - logger.error(`could not dump node to log`, err); - } - }); + try { + logger.info('Extension is activating'); + const debug = createDebugFacade(context); + const nodeVars = new vars.NodeVarRegistry(); + const specialMembers = new vars.SpecialMemberRegistry(); + const hashTableTypes = new vars.HashTableTypes(); - /* Setup Node variable support */ + const nodesView = createPostgresVariablesView(context, logger, nodeVars, + specialMembers, debug, hashTableTypes); - const dataProvider = createNodeVariablesDataProvider(logger, debug, context); + setupExtension(context, specialMembers, nodeVars, hashTableTypes, debug, logger, nodesView); + + setupDebugger(nodesView, logger, debug, context); - const treeDisposable = vscode.window.registerTreeDataProvider(config.Views.NodePreviewTreeView, dataProvider); - const asiDisposable = vscode.debug.onDidChangeActiveStackItem(() => dataProvider.refresh()); - const refreshVariablesCommand = vscode.commands.registerCommand(config.Commands.RefreshPostgresVariables, () => { - dataProvider.refresh(); - }); + formatter.registerFormatting(logger); - context.subscriptions.push(refreshVariablesCommand); - context.subscriptions.push(asiDisposable); - context.subscriptions.push(dumpVarsToLogCmd); - context.subscriptions.push(treeDisposable); - context.subscriptions.push(debug); - vscode.commands.executeCommand('setContext', config.Contexts.ExtensionActivated, true); + config.setExtensionActive(true); - logger.info('Extension activated'); + logger.info('Extension activated'); + } catch (error) { + logger.error('Failed to activate extension', error); + config.setExtensionActive(false); + } } export function deactivate() { - vscode.commands.executeCommand('setContext', config.Contexts.ExtensionActivated, false); - } + config.setExtensionActive(false); +} diff --git a/src/extension.ts b/src/extension.ts index b44fa99..8458705 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,12 +1,46 @@ import * as vscode from 'vscode'; import * as utils from './utils'; import * as vars from './variables'; -import * as fs from 'fs'; +import path from 'path'; + +export class NodePreviewTreeViewProvider implements vscode.TreeDataProvider, vscode.Disposable { + subscriptions: vscode.Disposable[] = []; + + /** + * ExecContext used to pass to all members. + * Updated on each debug session start/end. + */ + execContext?: vars.ExecContext; -export class NodePreviewTreeViewProvider implements vscode.TreeDataProvider { constructor( private log: utils.ILogger, - private context: vars.ExecContext) { + private nodeVars: vars.NodeVarRegistry, + private specialMembers: vars.SpecialMemberRegistry, + private debug: utils.VsCodeDebuggerFacade, + private hashTableTypes: vars.HashTableTypes) { + this.subscriptions = [ + vscode.debug.onDidStartDebugSession(s => { + if (!this.execContext) { + this.execContext = new vars.ExecContext(this.nodeVars, this.specialMembers, + this.debug, this.hashTableTypes); + } + }), + vscode.debug.onDidTerminateDebugSession(s => { + if (this.execContext) { + this.execContext = undefined; + } + }), + ]; + } + + private getExecContext() { + if (this.execContext) { + return this.execContext; + } + + this.execContext = new vars.ExecContext(this.nodeVars, this.specialMembers, + this.debug, this.hashTableTypes); + return this.execContext; } /* https://code.visualstudio.com/api/extension-guides/tree-view#updating-tree-view-content */ @@ -21,7 +55,7 @@ export class NodePreviewTreeViewProvider implements vscode.TreeDataProvider v.parent = topLevelVariable); + return topLevel; + } + } catch (err) { + /* + * There may be race condition when our state of debugger + * is 'ready', but real debugger is not. Such cases include + * debugger detach, continue after breakpoint etc. + * (we can not send commands to debugger). + * + * In this cases we must return empty array - this will + * clear our tree view. + */ + if (err instanceof Error && + err.message.indexOf('No debugger available') !== -1) { + return; + } } + } - const variables = await this.context.debug.getVariables(); - return (await Promise.all(variables.map(v => vars.Variable.createVariable(v, frameId, this.context, this.log)))) - .filter(v => v !== undefined); + async getTopLevelVariables(context: vars.ExecContext, frameId: number) { + const variables = await context.debug.getVariables(frameId); + return await vars.Variable.mapVariables(variables, frameId, context, + this.log, undefined); + } + + dispose() { + this.subscriptions.forEach(s => s.dispose()); + this.subscriptions = []; } } -export async function dumpVariableToLogCommand(args: any, log: utils.ILogger, debug: utils.IDebuggerFacade) { +export async function dumpVariableToLogCommand(args: any, log: utils.ILogger, + debug: utils.IDebuggerFacade) { const session = vscode.debug.activeDebugSession; if (!session) { vscode.window.showWarningMessage('Can not dump variable - no active debug session!'); @@ -58,83 +124,110 @@ export async function dumpVariableToLogCommand(args: any, log: utils.ILogger, de } const variable = args.variable; - if (!variable) { - log.info('Variable info not present in args'); + if (!variable?.value) { + log.warn('Variable info not present in args'); + return; + } + + console.assert(typeof variable.value === 'string'); + + if (!(utils.isValidPointer(variable.value))) { + vscode.window.showWarningMessage(`Variable ${variable.name} is not valid pointer`); return; } - const frameId = (vscode.debug.activeStackItem as vscode.DebugStackFrame | undefined)?.frameId; - if (frameId === undefined) { - log.info('Could not find active stack frame'); + const stackFrame = await debug.getCurrentFrameId(); + if (stackFrame === undefined) { + log.error('could not obtain current frameId'); return; } + + /* Simple `pprint(Node*)' function call */ + const expression = `-exec call pprint((const void *) ${variable.value})`; try { - /* Simple `pprint(Node*) call, just like in gdb */ - await debug.evaluate(`-exec call pprint(${variable.evaluateName})`, frameId); + await debug.evaluate(expression, stackFrame); } catch (err: any) { - log.error(`could not dump variable ${variable.name} to log`, err); + log.error('could not dump variable %s to log', variable.name, err); + vscode.window.showErrorMessage(`Could not dump variable ${variable.name}. ` + + `See errors in Output log`) } } class ConfigFileParseResult { + /* Array special members */ arrayInfos?: vars.ArraySpecialMemberInfo[]; + /* Information about type aliases */ aliasInfos?: vars.AliasInfo[]; + /* Path to custom typedef's file */ + typedefs?: string; + /* Custom List types */ + customListTypes?: vars.ListPtrSpecialMemberInfo[]; + /* Types stored in HTABs */ + htabTypes?: vars.HtabEntryInfo[]; + /* Types for simple hash */ + simpleHashTableTypes?: vars.SimplehashEntryInfo[]; } function parseConfigurationFile(configFile: any): ConfigFileParseResult | undefined { - if (!configFile === undefined) { - return; - } - - if (typeof configFile !== 'object') { - return; - } - - const parseArraySm1 = (obj: any): vars.ArraySpecialMemberInfo => { + const parseArraySm1 = (obj: any): vars.ArraySpecialMemberInfo | undefined => { + if (!(obj && typeof obj === 'object' && obj !== null)) { + return; + } + let nodeTag = obj.nodeTag; if (!nodeTag) { - throw new Error("nodeTag field not provided"); + vscode.window.showErrorMessage('"nodeTag" field not provided'); + return; } if (typeof nodeTag !== 'string') { - throw new Error(`nodeTag type must be string, given: ${typeof nodeTag}`); + vscode.window.showErrorMessage(`nodeTag type must be string, given: ${typeof nodeTag}`); + return; } nodeTag = nodeTag.trim().replace('T_', ''); /* NodeTag used also as type name, so it must be valid identifier */ if (!utils.isValidIdentifier(nodeTag)) { - throw new Error(`nodeTag must be valid identifier. given: ${obj.nodeTag}`); + vscode.window.showErrorMessage(`nodeTag must be valid identifier. given: ${obj.nodeTag}`); + return; } let memberName = obj.memberName; if (!memberName) { - throw new Error(`memberName field not provided for type with NodeTag: ${obj.nodeTag}`); + vscode.window.showErrorMessage(`"memberName" field not provided for type with NodeTag: ${obj.nodeTag}`); + return; } if (typeof memberName !== 'string') { - throw new Error(`memberName field must be string for type with NodeTag: ${obj.nodeTag}`); + vscode.window.showErrorMessage(`"memberName" field must be string for type with NodeTag: ${obj.nodeTag}`); + return; } memberName = memberName.trim(); if (!utils.isValidIdentifier(memberName)) { - throw new Error(`memberName field ${memberName} is not valid identifier`); + vscode.window.showErrorMessage(`"memberName" field ${memberName} is not valid identifier`); + return; } let lengthExpr = obj.lengthExpression; if (!lengthExpr) { - throw new Error(`lengthExpression not provided for: ${obj.nodeTag}->${memberName}`); + vscode.window.showErrorMessage(`lengthExpression not provided for: ${obj.nodeTag}->${memberName}`); + return; } if (typeof lengthExpr !== 'string') { - throw new Error(`lengthExpression field must be string for: ${obj.nodeTag}->${memberName}`); + vscode.window.showErrorMessage(`lengthExpression field must be string for: ${obj.nodeTag}->${memberName}`); + return; } lengthExpr = lengthExpr.trim(); if (!lengthExpr) { - throw new Error('lengthExpression can not be empty string'); + vscode.window.showErrorMessage('lengthExpression can not be empty string'); + return; } + return { typeName: nodeTag, memberName, @@ -142,49 +235,62 @@ function parseConfigurationFile(configFile: any): ConfigFileParseResult | undefi } } - const parseArraySm2 = (obj: any): vars.ArraySpecialMemberInfo => { + const parseArraySm2 = (obj: any): vars.ArraySpecialMemberInfo | undefined => { + if (!(obj && typeof obj === 'object' && obj !== null)) { + return; + } + let typeName = obj.typeName; if (!typeName) { - throw new Error("typeName field not provided"); + vscode.window.showErrorMessage('"typeName" field not provided'); + return; } if (typeof typeName !== 'string') { - throw new Error(`typeName type must be string, given: ${typeof typeName}`); + vscode.window.showErrorMessage(`"typeName" type must be string, given: ${typeof typeName}`); + return; } typeName = typeName.trim(); /* NodeTag used also as type name, so it must be valid identifier */ if (!utils.isValidIdentifier(typeName)) { - throw new Error(`typeName must be valid identifier. given: ${typeName}`); + vscode.window.showErrorMessage(`typeName must be valid identifier. given: ${typeName}`); + return; } let memberName = obj.memberName; if (!memberName) { - throw new Error(`memberName field not provided for type: ${typeName}`); + vscode.window.showErrorMessage(`memberName field not provided for type: ${typeName}`); + return; } if (typeof memberName !== 'string') { - throw new Error(`memberName field must be string for type: ${typeName}`); + vscode.window.showErrorMessage(`memberName field must be string for type: ${typeName}`); + return; } memberName = memberName.trim(); if (!utils.isValidIdentifier(memberName)) { - throw new Error(`memberName field ${memberName} is not valid identifier`) + vscode.window.showErrorMessage(`memberName field ${memberName} is not valid identifier`); + return; } let lengthExpr = obj.lengthExpression; if (!lengthExpr) { - throw new Error(`lengthExpression not provided for: ${typeName}->${memberName}`); + vscode.window.showErrorMessage(`lengthExpression not provided for: ${typeName}->${memberName}`); + return; } if (typeof lengthExpr !== 'string') { - throw new Error(`lengthExpression field must be string for: ${typeName}->${memberName}`); + vscode.window.showErrorMessage(`lengthExpression field must be string for: ${typeName}->${memberName}`); + return; } lengthExpr = lengthExpr.trim(); if (!lengthExpr) { - throw new Error('lengthExpression can not be empty string'); + vscode.window.showErrorMessage('lengthExpression can not be empty string'); + return; } return { typeName, @@ -193,32 +299,31 @@ function parseConfigurationFile(configFile: any): ConfigFileParseResult | undefi } } - const configVersion = Number(configFile.version); - if (Number.isNaN(configVersion) || !(configVersion == 1 || configVersion == 2)) { - throw new Error(`unknown version of config file: ${configFile.version}`); - } - - const parseAliasV2 = (obj: any): vars.AliasInfo => { + const parseAliasV2 = (obj: any): vars.AliasInfo | undefined => { if (typeof obj !== 'object') { - throw new Error(`AliasInfo object must be object type. given: ${typeof obj}`); + return; } if (!(obj.alias && typeof obj.alias === 'string')) { - throw new Error(`"alias" field must be string. given: ${typeof obj.alias}`); + vscode.window.showErrorMessage(`"alias" field must be string. given: ${typeof obj.alias}`); + return; } const alias = obj.alias.trim(); if (!alias) { - throw new Error(`"alias" field must not be empty`); + vscode.window.showErrorMessage(`"alias" field must not be empty`); + return; } if (!(obj.type && typeof obj.type === 'string')) { - throw new Error(`"type" field must be string. given: ${typeof obj.type}`); + vscode.window.showErrorMessage(`"type" field must be string. given: ${typeof obj.type}`); + return; } const type = obj.type.trim(); if (!type) { - throw new Error(`"type" field must not be empty`); + vscode.window.showErrorMessage(`"type" field must not be empty`); + return; } return { @@ -227,31 +332,530 @@ function parseConfigurationFile(configFile: any): ConfigFileParseResult | undefi } } + const parseTypedefs = (obj: any): string | undefined => { + if (!obj) { + return; + } + + if (typeof obj !== 'string') { + vscode.window.showErrorMessage('"typedefs" field must be a string'); + return; + } + + return obj.trim(); + } + + const parseListTypes = (obj: any): vars.ListPtrSpecialMemberInfo[] | undefined => { + /* + * [ + * { + * "type": "string", + * "member": ["string", "string"], + * "variable": ["string", "string"] + * } + * ] + */ + if (!Array.isArray(obj)) { + return; + } + + const elements: vars.ListPtrSpecialMemberInfo[] = []; + for (const o of obj) { + if (!(typeof o === 'object' && o)) { + continue; + } + + const type = o.type; + if (typeof type !== 'string' && type) { + vscode.window.showErrorMessage(`"type" field must be non-empty string. given ${typeof type}`); + continue; + } + + let memberEntry: [string, string] | undefined; + if (Array.isArray(o.member) && o.member.length === 2) { + const struct = o.member[0]; + const member = o.member[1]; + if (!(typeof struct === 'string' && typeof member === 'string' && + struct && member)) { + vscode.window.showErrorMessage(`"member" entry should be array of struct and member strings. given: [${typeof struct}, ${typeof member}]`); + continue; + } + + memberEntry = [struct, member]; + } + + let variableEntry: [string, string] | undefined; + if (Array.isArray(o.variable) && o.variable.length === 2) { + const func = o.variable[0]; + const variable = o.variable[1]; + if (!(typeof func === 'string' && typeof variable === 'string' && + func && variable)) { + vscode.window.showErrorMessage(`"variable" entry should be array of function name and variable strings. given: [${typeof func}, ${typeof variable}]`); + continue; + } + + variableEntry = [func, variable]; + } + + elements.push({ + type, + member: memberEntry, + variable: variableEntry + }) + } + + return elements; + } + + const parseHtabTypes = (obj: any): vars.HtabEntryInfo[] | undefined => { + /* + * { + * "parent": "string", + * "member": ["string", "string"], + * "variable": ["string", "string"] + * } + */ + const extractParentMember = (o: any): [string, string] | undefined => { + if (!(Array.isArray(o) && o.length === 2)) { + return; + } + + const [parent, member] = o; + if (typeof parent === 'string' && typeof member === 'string' + && parent && member) { + return [parent, member]; + } + + return; + } + + if (!Array.isArray(obj)) { + return; + } + + const elements: vars.HtabEntryInfo[] = []; + for (const o of obj) { + if (!(o && typeof o === 'object')) { + continue; + } + + const type = o.type; + if (typeof type !== 'string' && type) { + continue; + } + + let pair = extractParentMember(o.member); + if (!pair) { + pair = extractParentMember(o.variable); + } + + if (!pair) { + continue; + } + + elements.push({ + type, + member: pair[1], + parent: pair[0], + }); + } + + return elements; + } + + const parseSimplehashTypes = (obj: any): vars.SimplehashEntryInfo[] | undefined => { + /* + * [ + * { + * "prefix": "string", + * "type": "string" + * } + * ] + */ + + if (!Array.isArray(obj)) { + return; + } + + const elements = []; + for (const o of obj) { + if (!(typeof o === 'object' && o)) { + continue; + } + + + const prefix = o.prefix; + const type = o.type; + + if (!(prefix && typeof prefix === 'string' && + type && typeof type === 'string')) { + continue; + } + + elements.push({ + prefix, + canIterate: true, + elementType: type + } as vars.SimplehashEntryInfo); + } + + return elements; + } + + if (!(typeof configFile === 'object' && configFile)) { + return; + } + + const configVersion = Number(configFile.version); + if (!(Number.isInteger(configVersion) && 1 <= configVersion && configVersion <= 5)) { + vscode.window.showErrorMessage(`unknown version of config file: ${configFile.version}`); + return; + } + const arrayMemberParser = configVersion == 1 ? parseArraySm1 : parseArraySm2; - const arrayInfos = Array.isArray(configFile.specialMembers?.array) && configFile.specialMembers.array.length > 0 - ? configFile.specialMembers.array.forEach(arrayMemberParser) - : undefined; - - const aliasInfos = configVersion == 2 && Array.isArray(configFile.aliases) && configFile.aliases.length > 0 - ? configFile.aliases.map(parseAliasV2) - : undefined; + const arrayInfos = Array.isArray(configFile.specialMembers?.array) && + configFile.specialMembers.array.length > 0 + ? configFile.specialMembers.array.map(arrayMemberParser).filter((a: any) => a !== undefined) + : undefined; + + const aliasInfos = configVersion >= 2 && + Array.isArray(configFile.aliases) && + configFile.aliases.length > 0 + ? configFile.aliases.map(parseAliasV2).filter((a: any) => a !== undefined) + : undefined; + + const typedefs = configVersion >= 3 + ? parseTypedefs(configFile.typedefs) + : undefined; + + const customListTypes = configVersion >= 4 + ? parseListTypes(configFile.customListTypes) + : undefined; + + const htabTypes = configVersion >= 5 + ? parseHtabTypes(configFile.htab) + : undefined; + + const simpleHashTableTypes = configVersion >= 5 + ? parseSimplehashTypes(configFile.simplehash) + : undefined; return { arrayInfos, aliasInfos, + typedefs, + customListTypes, + htabTypes, + simpleHashTableTypes, + } +} + +async function promptWorkspace() { + if (!vscode.workspace.workspaceFolders) { + throw new Error('No workspaces opened'); + } + + if (vscode.workspace.workspaceFolders.length === 1) { + return vscode.workspace.workspaceFolders[0]; + } + + const name = await vscode.window.showQuickPick( + vscode.workspace.workspaceFolders.map(wf => wf.name), { + title: 'Choose workspace', + placeHolder: vscode.workspace.workspaceFolders[0].name + }); + if (!name) { + throw new Error('No workspaces chosen'); + } + + return vscode.workspace.workspaceFolders.find(wf => wf.name === name)!; +} + +async function promptExtensionName() { + const extensionName = await vscode.window.showInputBox({ + prompt: 'Enter extension name' + }); + if (!extensionName) { + throw new Error('User did not specified extension name'); + } + + const workspace = await promptWorkspace(); + return { + path: utils.getWorkspacePgSrcFile(workspace.uri, 'contrib', extensionName), + name: extensionName, + }; +} + +async function promptExtensionFlags() { + async function promptFlag(title: string) { + const result = await vscode.window.showQuickPick([ + 'Yes', 'No' + ], {title, placeHolder: 'Yes'}); + if (!result) { + throw new Error('User declined to answer'); + } + + return result === 'Yes'; + } + + async function promptString(title: string) { + const result = await vscode.window.showInputBox({ + prompt: title, + }); + + return result ?? ''; + } + + return { + c: await promptFlag('Use C sources?'), + sql: await promptFlag('Use SQL sources?'), + tap: await promptFlag('Include TAP tests?'), + regress: await promptFlag('Include regress tests?'), + comment: await promptString('Enter extension description'), + } +} + +async function bootstrapExtensionCommand() { + async function bootstrapFile(name: string, contents: string[]) { + const filePath = utils.joinPath(path, name); + await utils.writeFile(filePath, contents.join('\n')); + } + + const {path, name} = await promptExtensionName(); + + if (await utils.directoryExists(path)) { + if (!await utils.directoryEmpty(path)) { + vscode.window.showErrorMessage(`Extension ${name} directory already exists and is not empty`); + return; + } + } else { + await utils.createDirectory(path); + } + + const flags = await promptExtensionFlags(); + + /* + * Makefile + * *.control + * *.sql + * *.c + * README + */ + const makefile = []; + if (flags.c) { + makefile.push(`EXTENSION = ${name}`, + '', + `MODULE_big = ${name}`, + `OBJS = $(WIN32RES) ${name}.o`, + ''); + } + + if (flags.sql) { + makefile.push(`DATA = ${name}--0.1.0.sql`, ''); + } + + if (flags.regress) { + makefile.push(`REGRESS = init`, ''); + } + + if (flags.tap) { + makefile.push(`TAP_TESTS = 1`, ''); + } + + makefile.push( + 'ifdef USE_PGXS', + 'PG_CONFIG := pg_config', + 'PGXS := $(shell $(PG_CONFIG) --pgxs)', + 'include $(PGXS)', + 'else', + `subdir = contrib/${name}`, + 'top_builddir = ../..', + 'include $(top_builddir)/src/Makefile.global', + 'include $(top_srcdir)/contrib/contrib-global.mk', + 'endif', + '' + ); + + await bootstrapFile('Makefile', makefile); + + const control = [ + `# ${name} extension`, + "default_version = '0.1.0'" + ]; + + if (flags.comment) { + control.push(`comment = '${flags.comment}'`); + } + + if (flags.c) { + control.push(`module_pathname = '$libdir/${name}'`); + } + + control.push('relocatable = false'); + await bootstrapFile(`${name}.control`, control); + + await bootstrapFile('README', [ + `# ${name}`, + '', + flags.comment + ]); + + if (flags.c) { + await bootstrapFile(`${name}.c`, [ + '#include "postgres.h"', + '#include "fmgr.h"', + '#include "utils/builtins.h"', + '', + '#ifdef PG_MODULE_MAGIC', + 'PG_MODULE_MAGIC;', + '#endif', + '', + 'void _PG_init(void);', + 'void _PG_fini(void);', + '', + 'PG_FUNCTION_INFO_V1(hello_world);', + '', + 'Datum', + 'hello_world(PG_FUNCTION_ARGS)', + '{', + '\tPG_RETURN_TEXT_P(cstring_to_text("hello, world!"));', + '}', + '', + 'void', + '_PG_init(void)', + '{', + '}', + '', + 'void', + '_PG_fini(void)', + '{', + '}', + '' + ]); + } + + if (flags.sql) { + const sql = [ + 'CREATE FUNCTION hello_world()', + 'RETURNS text', + ]; + + if (flags.c) { + sql.push( + 'AS \'MODULE_PATHNAME\'', + 'LANGUAGE C IMMUTABLE;' + ); + } else { + sql.push( + 'AS $$', + '\tSELECT \'hello, world!\';', + '$$ LANGUAGE SQL IMMUTABLE;' + ); + } + + await bootstrapFile(`${name}--0.1.0.sql`, sql); + } + + if (flags.regress) { + const regressDir = utils.joinPath(path, 'sql'); + const expectedDir = utils.joinPath(path, 'expected'); + + await utils.createDirectory(regressDir); + await utils.createDirectory(expectedDir); + + await utils.writeFile( + utils.joinPath(regressDir, 'init.sql'), [ + `CREATE EXTENSION ${name};`, + 'SELECT hello_world() as text;' + ].join('\n')); + + await utils.writeFile( + utils.joinPath(expectedDir, 'init.out'), [ + `CREATE EXTENSION ${name};`, + 'SELECT hello_world() as text;', + ' text ', + '---------------', + ' hello, world!', + '(1 row)', + '', + '', + ].join('\n')); + } + + if (flags.tap) { + const tapDir = utils.joinPath(path, 't'); + await utils.createDirectory(tapDir); + + await utils.writeFile( + utils.joinPath(tapDir, '001_basic.pl'), [ + 'use strict;', + 'use warnings;', + '', + 'use PostgreSQL::Test::Cluster;', + 'use PostgreSQL::Test::Utils;', + 'use Test::More tests => 1;', + '', + 'my $node = PostgreSQL::Test::Cluster->new(\'main\');', + '$node->init;', + flags.c + ? `$node->append_conf(\'postgresql.conf\', qq{shared_preload_libraries=\'${name}\'});` + : '', + '$node->start;', + '', + `$node->safe_psql('postgres', q(CREATE EXTENSION ${name}));`, + "my $out = $node->safe_psql('postgres', 'SELECT hello_world();');", + "is($out, 'hello, world!', 'Unexpected string');", + '', + 'done_testing();', + '', + ].join('\n') + ); + } + + const td = await vscode.workspace.openTextDocument(utils.joinPath(path, 'Makefile')); + await vscode.window.showTextDocument(td); +} + +function addElogErrorBreakpoint() { + /* + * Check that such breakpoint already exists, otherwise + * it will be added again on new extension activation + */ + if (vscode.debug.breakpoints + .find(bp => bp instanceof vscode.FunctionBreakpoint && + bp.functionName === 'errstart')) { + return; } + + /* Breakpoint on `elog' or `ereport' with ERROR or greater */ + vscode.debug.addBreakpoints([ + new vscode.FunctionBreakpoint( + 'errstart', + false, + 'ERROR <= elevel', + ) + ]); } -export function setupConfigFiles(execCtx: vars.ExecContext, log: utils.ILogger, context: vscode.ExtensionContext) { +export function setupExtension(context: vscode.ExtensionContext, specialMembers: vars.SpecialMemberRegistry, + nodeVars: vars.NodeVarRegistry, hashTableTypes: vars.HashTableTypes, + debug: utils.IDebuggerFacade, logger: utils.ILogger, + nodesView: NodePreviewTreeViewProvider) { + + function registerCommand(name: string, command: (...args: any[]) => void) { + const disposable = vscode.commands.registerCommand(name, command); + context.subscriptions.push(disposable); + } + const processSingleConfigFile = async (pathToFile: vscode.Uri) => { let doc = undefined; try { doc = await vscode.workspace.openTextDocument(pathToFile); } catch (err: any) { - log.error(`failed to read settings file ${pathToFile.fsPath}`, err); + logger.error('failed to read settings file %s', pathToFile, err); return; } @@ -259,7 +863,13 @@ export function setupConfigFiles(execCtx: vars.ExecContext, log: utils.ILogger, try { text = doc.getText(); } catch (err: any) { - log.error(`failed to read settings file ${doc.uri.fsPath}`, err); + logger.error('failed to read settings file %s', doc.uri.fsPath, err); + return; + } + + if (text.length === 0) { + /* JSON file can be used as activation event */ + logger.debug('JSON settings file %s is empty', doc.uri.fsPath); return; } @@ -267,7 +877,7 @@ export function setupConfigFiles(execCtx: vars.ExecContext, log: utils.ILogger, try { data = JSON.parse(text); } catch (err: any) { - log.error(`failed to parse JSON settings file ${doc.uri.fsPath}`, err); + logger.error('failed to parse JSON settings file %s', doc.uri.fsPath, err); return; } @@ -275,58 +885,151 @@ export function setupConfigFiles(execCtx: vars.ExecContext, log: utils.ILogger, try { parseResult = parseConfigurationFile(data); } catch (err: any) { - log.error(`failed to parse JSON settings file ${doc.uri.fsPath}`, err); + logger.error('failed to parse JSON settings file %s', doc.uri.fsPath, err); return; } if (parseResult) { if (parseResult.arrayInfos?.length) { - log.debug(`adding ${parseResult.arrayInfos.length} array special members from config file`); - execCtx.specialMemberRegistry.addArraySpecialMembers(parseResult.arrayInfos); + logger.debug('adding %i array special members from config file', parseResult.arrayInfos.length); + specialMembers.addArraySpecialMembers(parseResult.arrayInfos); } + if (parseResult.aliasInfos?.length) { - log.debug(`adding ${parseResult.aliasInfos.length} aliases from config file`); - execCtx.nodeVarRegistry.addAliases(parseResult.aliasInfos); + logger.debug('adding %i aliases from config file', parseResult.aliasInfos.length); + nodeVars.addAliases(parseResult.aliasInfos); + } + + if (parseResult.typedefs) { + let typedefs: vscode.Uri; + if (path.isAbsolute(parseResult.typedefs)) { + typedefs = vscode.Uri.file(parseResult.typedefs); + } else { + const workspace = vscode.workspace.getWorkspaceFolder(pathToFile); + if (!workspace) { + logger.error('failed to determine workspace folder for configuration file %s', pathToFile.fsPath); + return; + } + + typedefs = utils.getWorkspacePgSrcFile(workspace.uri, parseResult.typedefs); + } + + if (await utils.fileExists(typedefs)) { + Configuration.CustomTypedefsFile = typedefs; + } else { + vscode.window.showErrorMessage(`typedefs file ${parseResult.typedefs} does not exist`); + } + } + + if (parseResult.customListTypes) { + logger.debug('adding %i custom list types', parseResult.customListTypes.length); + try { + specialMembers.addNodePtrSpecialMembers(parseResult.customListTypes); + } catch (e) { + vscode.window.showErrorMessage('failed to add custom List types'); + logger.error('error occurred during adding custom List types', e); + } + } + + if (parseResult.htabTypes) { + logger.debug('adding %i htab types', parseResult.htabTypes.length); + try { + hashTableTypes.addHTABTypes(parseResult.htabTypes); + } catch (e) { + vscode.window.showErrorMessage('failed to add custom htab types'); + logger.error('error occurred during adding custom HTAB types', e); + } + } + + if (parseResult.simpleHashTableTypes) { + logger.debug('adding %i simplehash types', parseResult.simpleHashTableTypes.length); + try { + hashTableTypes.addSimplehashTypes(parseResult.simpleHashTableTypes); + } catch (e) { + vscode.window.showErrorMessage('failed to add custom simple hash table types'); + logger.error('error occurred during adding custom simple hash table types', e); + } } } } - const processFolders = (folders: readonly vscode.WorkspaceFolder[]) => { - const propertiesFilePath = vscode.Uri.joinPath(folders[0].uri, '.vscode', Configuration.ExtensionSettingsFileName); - context.subscriptions.push(vscode.commands.registerCommand(Configuration.Commands.OpenConfigFile, async () => { - if (!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0) { - vscode.window.showInformationMessage('No workspaces found - open directory first'); - return; + const refreshConfigurationFromFolders = async (folders: readonly vscode.WorkspaceFolder[]) => { + for (const folder of folders) { + const pathToFile = utils.joinPath( + folder.uri, + '.vscode', + Configuration.ExtensionSettingsFileName); + if (await utils.fileExists(pathToFile)) { + await processSingleConfigFile(pathToFile); + } else { + logger.debug('config file %s does not exist', pathToFile.fsPath); } + } + } + + /* Refresh config files when debug session starts */ + vscode.debug.onDidStartDebugSession(async _ => { + if (vscode.workspace.workspaceFolders?.length) { + logger.info('refreshing configuration files due to debug session start') + await refreshConfigurationFromFolders(vscode.workspace.workspaceFolders); + } + }, undefined, context.subscriptions); + + /* Register command to dump variable to log */ + const pprintVarToLogCmd = async (args: any) => { + try { + await dumpVariableToLogCommand(args, logger, debug); + } catch (err: any) { + logger.error('error while dumping node to log', err); + } + }; - const propertiesFileExists = await utils.fileExists(propertiesFilePath); + const openConfigFileCmd = async () => { + if (!vscode.workspace.workspaceFolders?.length) { + vscode.window.showInformationMessage('No workspaces found - open directory first'); + return; + } + + for (const folder of vscode.workspace.workspaceFolders) { + const configFilePath = utils.joinPath( + folder.uri, + '.vscode', + Configuration.ExtensionSettingsFileName); + const propertiesFileExists = await utils.fileExists(configFilePath); /* Create default configuration file if not exists */ if (!propertiesFileExists) { - if (await utils.fsEntryExists(propertiesFilePath)) { + if (await utils.fsEntryExists(configFilePath)) { vscode.window.showErrorMessage(`Can not create ${Configuration.ExtensionSettingsFileName} - fs entry exists and not file`); return; } - log.debug(`creating ${propertiesFilePath} configuration file`); - const configDirectoryPath = vscode.Uri.joinPath(propertiesFilePath, '..'); + logger.debug('creating %s configuration file', configFilePath.fsPath); + const configDirectoryPath = utils.joinPath(configFilePath, '..'); if (!await utils.directoryExists(configDirectoryPath)) { try { - fs.mkdirSync(configDirectoryPath.fsPath); + await utils.createDirectory(configDirectoryPath); } catch (err) { - log.error(`failed to create config directory`, err); + logger.error('failed to create config directory', err); return; } } try { - fs.writeFileSync(propertiesFilePath.fsPath, JSON.stringify({ - version: 2, - specialMembers: { - array: [] - } - }, undefined, ' ')); + await utils.writeFile(configFilePath, JSON.stringify( + /* Example config file */ + { + version: 5, + specialMembers: { + array: [] + }, + aliases: [], + customListTypes: [], + htab: [], + simplehash: [] + }, + undefined, ' ')); } catch (err: any) { - log.error(`Could not write default configuration file`, err); + logger.error('Could not write default configuration file', err); vscode.window.showErrorMessage('Error creating configuration file'); return; } @@ -334,112 +1037,250 @@ export function setupConfigFiles(execCtx: vars.ExecContext, log: utils.ILogger, let doc; try { - doc = await vscode.workspace.openTextDocument(propertiesFilePath) + doc = await vscode.workspace.openTextDocument(configFilePath); } catch (err: any) { - log.error(`failed to open configuration file`, err); + logger.error('failed to open configuration file', err); return; } try { await vscode.window.showTextDocument(doc); } catch (err: any) { - log.error(`failed to show configuration file`, err); + logger.error('failed to show configuration file', err); return; } - })); - - folders.forEach(folder => { - const pathToFile = vscode.Uri.joinPath(folder.uri, '.vscode', Configuration.ExtensionSettingsFileName); - utils.fileExists(pathToFile).then(async exists => { - /* - * Track change and create events, but not delete - - * currently no mechanism to track deltas in files. - */ - let trackCreateEvent = true; - if (exists) { - trackCreateEvent = false; - await processSingleConfigFile(pathToFile); - return; - } - - const watcher = vscode.workspace.createFileSystemWatcher(pathToFile.fsPath, trackCreateEvent, false, true); - if (trackCreateEvent) { - watcher.onDidCreate(processSingleConfigFile); - } - watcher.onDidChange(processSingleConfigFile); - context.subscriptions.push(watcher); - }, () => log.debug(`settings file ${pathToFile.fsPath} does not exist`)); - }); + /* Stop at first success folder to process */ + break; + } + }; - /* Refresh config file command register */ - const refreshConfigCmdDisposable = vscode.commands.registerCommand(Configuration.Commands.RefreshConfigFile, async () => { - if (!vscode.workspace.workspaceFolders?.length) { - return; - } + const bootstrapExtensionCmd = async () => { + try { + await bootstrapExtensionCommand(); + } catch (err) { + logger.error('Failed to bootstrap extension', err); + } + } - log.info(`refreshing config file due to command execution`); - for (const folder of vscode.workspace.workspaceFolders) { - const propertiesFilePath = vscode.Uri.joinPath(folder.uri, '.vscode', Configuration.ExtensionSettingsFileName); - if (!await utils.fileExists(propertiesFilePath)) { - const answer = await vscode.window.showWarningMessage(`Config file does not exist. Create?`, 'Yes', 'No'); - if (answer !== 'Yes') { - return; - } + /* Refresh config file command register */ + const refreshConfigCmd = async () => { + if (!vscode.workspace.workspaceFolders?.length) { + return; + } - await vscode.commands.executeCommand(Configuration.Commands.OpenConfigFile); + logger.info('refreshing config file due to command execution'); + for (const folder of vscode.workspace.workspaceFolders) { + const configFilePath = utils.joinPath( + folder.uri, + '.vscode', + Configuration.ExtensionSettingsFileName); + if (!await utils.fileExists(configFilePath)) { + const answer = await vscode.window.showWarningMessage( + 'Config file does not exist. Create?', + 'Yes', 'No'); + if (answer !== 'Yes') { return; } - try { - await processSingleConfigFile(propertiesFilePath); - } catch (err: any) { - log.error(`failed to update config file`, err); - } + await vscode.commands.executeCommand(Configuration.Commands.OpenConfigFile); + return; } - }); - context.subscriptions.push(refreshConfigCmdDisposable); + try { + await processSingleConfigFile(configFilePath); + } catch (err: any) { + logger.error('failed to update config file', err); + } + } + }; + + const refreshVariablesCmd = () => { + logger.info('refreshing variables view due to command') + nodesView.refresh(); + }; + const addVariableToWatchCmd = async (args: any) => { + const expr = vars.getWatchExpressionCommandHandler(args); + if (!expr) { + return; + } + + await vscode.commands.executeCommand('debug.addToWatchExpressions', { + variable: { + evaluateName: expr + } + }); } - /* Command to create configuration file */ + registerCommand(Configuration.Commands.RefreshConfigFile, refreshConfigCmd); + registerCommand(Configuration.Commands.OpenConfigFile, openConfigFileCmd); + registerCommand(Configuration.Commands.DumpNodeToLog, pprintVarToLogCmd); + registerCommand(Configuration.Commands.RefreshPostgresVariables, refreshVariablesCmd); + registerCommand(Configuration.Commands.BootstrapExtension, bootstrapExtensionCmd); + registerCommand(Configuration.Commands.AddToWatchView, addVariableToWatchCmd); + + /* Process config files immediately */ if (vscode.workspace.workspaceFolders) { - processFolders(vscode.workspace.workspaceFolders); + refreshConfigurationFromFolders(vscode.workspace.workspaceFolders); } else { let disposable: vscode.Disposable | undefined; /* Wait for folder open */ disposable = vscode.workspace.onDidChangeWorkspaceFolders(e => { - processFolders(e.added); - /* + refreshConfigurationFromFolders(e.added); + + /* * Run only once, otherwise multiple commands will be registered - * it will spoil up everything */ disposable?.dispose(); }, context.subscriptions); } + + /* Read files with NodeTags */ + setupNodeTagFiles(logger, nodeVars, context); + addElogErrorBreakpoint(); +} + +async function setupNodeTagFiles(log: utils.ILogger, nodeVars: vars.NodeVarRegistry, + context: vscode.ExtensionContext): Promise { + + const getNodeTagFiles = () => { + const customNodeTagFiles = Configuration.getCustomNodeTagFiles(); + if (customNodeTagFiles?.length) { + return customNodeTagFiles; + } + + return [ + utils.getPgSrcFile('src', 'include', 'nodes', 'nodes.h'), + utils.getPgSrcFile('src', 'include', 'nodes', 'nodetags.h'), + ] + } + + const handleNodeTagFile = async (path: vscode.Uri) => { + if (!await utils.fileExists(path)) { + return; + } + + log.debug('processing %s NodeTags file', path.fsPath); + const document = await vscode.workspace.openTextDocument(path); + try { + const added = nodeVars.updateNodeTypesFromFile(document); + log.debug('added %i NodeTags from %s file', added, path.fsPath); + } catch (err: any) { + log.error('could not initialize node tags array', err); + } + } + + const setupSingleFolder = async (folder: vscode.WorkspaceFolder) => { + const nodeTagFiles = getNodeTagFiles(); + + for (const filePath of nodeTagFiles) { + const file = utils.joinPath(folder.uri, filePath); + await handleNodeTagFile(file); + const pattern = new vscode.RelativePattern(folder, filePath); + const watcher = vscode.workspace.createFileSystemWatcher(pattern, + false, false, + /* ignoreDeleteEvents */ true); + watcher.onDidChange(async uri => { + log.info('detected change in NodeTag file: %s', uri); + await handleNodeTagFile(uri); + }, context.subscriptions); + watcher.onDidCreate(async uri => { + log.info('detected creation of NodeTag file: %s', uri); + await handleNodeTagFile(uri); + }, context.subscriptions); + + context.subscriptions.push(watcher); + } + } + + if (vscode.workspace.workspaceFolders?.length) { + await Promise.all( + vscode.workspace.workspaceFolders.map(async folder => + await setupSingleFolder(folder) + ) + ); + } + + vscode.workspace.onDidChangeWorkspaceFolders(async e => { + for (let i = 0; i < e.added.length; i++) { + const folder = e.added[i]; + await setupSingleFolder(folder); + } + }, undefined, context.subscriptions); +} + +export function getCurrentLogLevel() { + const configValue = Configuration.getLogLevel(); + switch (configValue) { + case 'INFO': + return utils.LogLevel.Info; + case 'DEBUG': + return utils.LogLevel.Debug; + case 'WARNING': + return utils.LogLevel.Warn; + case 'ERROR': + return utils.LogLevel.Error; + case 'DISABLE': + return utils.LogLevel.Disable; + default: + return utils.LogLevel.Info; + } } export class Configuration { static ExtensionName = 'postgresql-hacker-helper'; static ExtensionPrettyName = 'PostgreSQL Hacker Helper'; static ConfigSections = { - TopLevelSection: `${this.ExtensionName}`, + TopLevelSection: this.ExtensionName, NodeTagFiles: 'nodeTagFiles', LogLevel: 'logLevel', - fullSection: (section: string) => `${this.ExtensionName}.${section}`, + PgbsdindentPath: 'pg_bsd_indentPath', + SrcPath: 'srcPath' }; static Commands = { DumpNodeToLog: `${this.ExtensionName}.dumpNodeToLog`, OpenConfigFile: `${this.ExtensionName}.openConfigurationFile`, RefreshPostgresVariables: `${this.ExtensionName}.refreshPostgresVariablesView`, RefreshConfigFile: `${this.ExtensionName}.refreshConfigFile`, + FormatterDiffView: `${this.ExtensionName}.formatterShowDiff`, + BootstrapExtension: `${this.ExtensionName}.bootstrapExtension`, + AddToWatchView: `${this.ExtensionName}.addVariableToWatch`, }; static Views = { NodePreviewTreeView: `${this.ExtensionName}.node-tree-view`, }; static ExtensionSettingsFileName = 'pgsql_hacker_helper.json'; - static Contexts = { - ExtensionActivated: `${this.ExtensionName}:activated` + /* Path to custom typedefs file in pgsql_hacker_helper.json file */ + static CustomTypedefsFile: vscode.Uri | undefined = undefined; + + static getLogLevel() { + return this.getConfig(this.ConfigSections.LogLevel); + }; + + static getCustomNodeTagFiles() { + return this.getConfig(this.ConfigSections.NodeTagFiles); + }; + + static getCustomPgbsdindentPath() { + return this.getConfig(this.ConfigSections.PgbsdindentPath); } -} \ No newline at end of file + + static getSrcPath() { + return this.getConfig(this.ConfigSections.SrcPath); + } + + static getConfig(section: string) { + const topLevelSection = this.ConfigSections.TopLevelSection + const config = vscode.workspace.getConfiguration(topLevelSection); + return config.get(section); + }; + static getFullConfigSection(section: string) { + return `${this.ConfigSections.TopLevelSection}.${section}`; + } + static setExtensionActive(status: boolean) { + const context = `${this.ExtensionName}:activated`; + vscode.commands.executeCommand('setContext', context, status); + } +} diff --git a/src/formatter.ts b/src/formatter.ts new file mode 100644 index 0000000..b344c4b --- /dev/null +++ b/src/formatter.ts @@ -0,0 +1,664 @@ +import * as vscode from 'vscode'; +import {languages} from 'vscode'; +import * as utils from './utils'; +import { Configuration } from './extension'; +import * as path from 'path'; +import * as os from 'os'; + +class LineDiff { + constructor(public isInsert: boolean, + public num: number, + public lines: string[]) + { } +} + +class LineDiffGroup { + constructor(private diffs: LineDiff[]) { }; + bake(document: vscode.TextDocument): vscode.TextEdit[] { + const edits = []; + for (const diff of this.diffs) { + if (diff.isInsert) { + /* Small trick to make all lines end with new line */ + diff.lines.push(''); + try { + edits.push(vscode.TextEdit.insert(document.lineAt(diff.num).range.start, + diff.lines.join('\n'))); + continue; + } catch (err: any) { + if (!(document.lineCount < diff.num && diff.lines[0].startsWith('/**INDENT'))) + throw err; + } + + /* + * pg_bsd_indent complains if there is no new line + * at the end of file, so we add it ourselves + */ + const lastLine = document.lineAt(document.lineCount - 1); + const lastPos = document.lineAt(document.lineCount - 1).range.start; + edits.push(vscode.TextEdit.insert(lastPos, `${lastLine.text}\n`)); + } else { + /* + * document.lineAt().range returns range of line + * NOT including new line, so just passing it + * we just clear that line - it will remain just empty. + * To handle this, we pass start character of line + * after last in this group (if). + * But handle cases, when last line - last in document (else) + */ + if (diff.num + diff.lines.length < document.lineCount) { + edits.push(vscode.TextEdit.delete(new vscode.Range( + document.lineAt(diff.num).range.start, + document.lineAt(diff.num + diff.lines.length).range.start + ))); + } else { + edits.push(vscode.TextEdit.delete(new vscode.Range( + document.lineAt(diff.num).range.start, + document.lineAt(diff.num + diff.lines.length - 1).range.end + ))); + } + continue; + } + } + + return edits; + } +} + +/* + * To parse diff output we use FSM. It has 3 states and operates like this: + * + * --- /file/from <-------- Initial State + * +++ /file/to + * @@ -X,X +X,X @@ <-------- Plain Text State + * asdfasdfasdf + * asdfasdfasdf + * asdfasdfasdf + * -asdfasdfasdf <-------- Change Group State + * -asdfasdfasdf + * +asdfasdfasdf + * asdfasdfasdf <-------- Plain Text State + * asdfasdfasdf + * asdfasdfasdf + * @@ -X,X +X,X @@ + * asdfasdfasdf + * asdfasdfasdf + * asdfasdfasdf + * +asdfasdfasdf <-------- Change Group State + * asdfasdfasdf <-------- Plain Text State + * asdfasdfasdf + * asdfasdfasdf + */ +abstract class FSMState { + constructor(protected fsm: DiffParserFSM) { } + abstract apply(start: string, line: string): void; + protected isChangeGroupSymbol(symbol: string): boolean { + return symbol == '-' || symbol == '+'; + } +} + +class InitialState extends FSMState { + constructor(fsm: DiffParserFSM) { + super(fsm); + } + + apply(start: string, line: string): void { + if (start != '@') { + return; + } + + this.fsm.state = new PlainLineState(-1, this.fsm); + this.fsm.state.apply(start, line); + } +} + +/** + * State for handling plain text with no changes or start of new chunk (@@...) + */ +class PlainLineState extends FSMState { + constructor(private line: number, fsm: DiffParserFSM) { + super(fsm); + } + apply(start: string, line: string): void { + if (this.isChangeGroupSymbol(start)) { + this.fsm.state = new ChangeGroupState(this.line, this.fsm); + this.fsm.state.apply(start, line); + return; + } + + /* + * If new hunk is started -record it's start line, + * otherwise just increment current line + */ + if (start === '@') { + /* + * Chunk start has form: + * @@ -, +, @@ ... + * + * We need to get - (without '-') + */ + const [, startFileRange,] = line.split(' ', 2); + const startLine = Number(startFileRange.slice(1).split(',')[0]) + if (Number.isNaN(startLine)) { + throw new Error(`Failed to parse start line in line: ${line}`); + } + this.line = startLine - 1; + } else { + this.line++; + } + } +} + +/** + * State for handling group of changes: consecutive lines starts with '+' or '-' + */ +class ChangeGroupState extends FSMState { + private insertLine: number; + private diffs: LineDiff[] = []; + private inDeleteGroup: boolean = false; + + constructor(private line: number, fsm: DiffParserFSM) { + super(fsm); + this.insertLine = line; + } + + apply(start: string, line: string): void { + /* End of change group - move to plain text handler */ + if (!this.isChangeGroupSymbol(start)) { + this.fsm.groups.push(new LineDiffGroup(this.diffs)); + this.fsm.state = new PlainLineState(this.line, this.fsm); + this.fsm.state.apply(start, line); + return; + } + + /* + * To correctly handle new line in change group we do following. + * Track 3 variables: + * - line - current line in *original* file (to apply changes + * in original file) + * - insertLine - line to which *new insert* should be done + * - inDeleteGroup - previous line was delete ('-') + * + * 'line' and 'insertLine' are separate because when we step on '-', + * we increment 'line', but inserting should be done at start of + * delete group (consecutive '-' lines). + * + * Change group - is a group of consecutive lines starts with '+' or '-'. + * This state must parse this group and create LineDiffGroup which + * later will be used to create delta of file change. + * + * As small optimization, we create single LineDiff for group of '+' or '-'. + * This checked as 'inDeleteGroup' must relate by start symbol group + * and diffs array must not be empty. + * In such case just add current line to last LineDiff. + * + * Interesting part is when we must create new LineDiff. + * Delete group - just create new group for current line + * and update 'insertLine' with current. + * + * Insert group - create new group with line = 'insertLine', + * because it initialized with 'line' at start we correctly + * handle first symbol of group + */ + + if (start === '-') { + /* Use '' as stub for line - we don't need it when deleting */ + if (this.diffs.length > 0 && this.inDeleteGroup) { + this.diffs[this.diffs.length - 1].lines.push(''); + } else { + this.diffs.push(new LineDiff(false, this.line, [''])); + this.inDeleteGroup = true; + this.insertLine = this.line; + } + this.line++; + } else /* start === '+' */ { + if (this.diffs.length > 0 && !this.inDeleteGroup) { + this.diffs[this.diffs.length - 1].lines.push(line.slice(1)); + } else { + this.diffs.push(new LineDiff(true, this.insertLine, [line.slice(1)])); + this.inDeleteGroup = false; + } + } + } +} + +/* + * Finite state machine is used for parsing diff output (in universal format). + * There are 3 states: + * - Initial: first state, it just proceed until reach new hunk, + * then initialize PlainText state + * - PlainText: used to handled lines without changes and new hunks (@@...) + * - ChangeGroup: process group of consecutive changes (line start with - or +) + */ + +class DiffParserFSM { + state: FSMState = new InitialState(this); + groups: LineDiffGroup[] = []; + + apply(start: string, line: string) { + this.state.apply(start, line); + } +} + +class DiffParser { + static parseContents(diff: string, document: vscode.TextDocument) { + const parser = new DiffParserFSM(); + + for (const line of diff.split(/\r?\n/)) { + const startSymbol = line[0]; + parser.apply(startSymbol, line); + } + + const edits = []; + for (const group of parser.groups) { + for (const edit of group.bake(document)) { + edits.push(edit); + } + } + return edits; + } +} + +function findSuitableWorkspace(document: vscode.TextDocument) { + if (!vscode.workspace.workspaceFolders?.length) { + throw new Error('Not workspaces opened'); + } + + for (const workspace of vscode.workspace.workspaceFolders) { + if (document.uri.path.startsWith(workspace.uri.path)) { + return workspace; + } + } + + /* Fallback with first workspace */ + return vscode.workspace.workspaceFolders[0]; +} + +class PgindentDocumentFormatterProvider implements vscode.DocumentFormattingEditProvider { + /* Flags found in pgindent */ + static pg_bsd_indentDefaultFlags = [ + '-bad', '-bap', '-bbb', '-bc', '-bl', '-cli1', '-cp33', '-cdb', + '-nce', '-d0', '-di12', '-nfc1', '-i4', '-l79', '-lp', '-lpl', + '-nip', '-npro', '-sac', '-tpg', '-ts4' + ]; + + private savedPgbsdPath?: vscode.Uri; + private savedProcessedTypedef?: vscode.Uri; + constructor(private logger: utils.ILogger) {} + + private async findExistingPgbsdindent(workspace: vscode.WorkspaceFolder) { + /* + * For pg_bsd_indent search 2 locations: + * + * - src/tools/pg_psd_indent: (PG >=16) + * - not exist: build + * - src/tools/pgindent/pg_bsd_indent (PG <16) + * - not exist: download + build + */ + let pg_bsd_indent_dir = utils.getWorkspacePgSrcFile(workspace.uri, 'src', 'tools', 'pg_bsd_indent'); + if (await utils.directoryExists(pg_bsd_indent_dir)) { + /* src/tools/pg_bsd_indent */ + let pg_bsd_indent = utils.joinPath(pg_bsd_indent_dir, 'pg_bsd_indent'); + if (await utils.fileExists(pg_bsd_indent)) { + return pg_bsd_indent; + } + + /* Try to build it */ + const response = await vscode.window.showWarningMessage( + 'Seems like pg_bsd_indent is not build yet. ' + + 'Formatting is not supported without it. ' + + 'Build?', + 'Yes', 'No'); + if (!response || response === 'No') { + throw new Error('pg_bsd_indent not found and user do not want to build it'); + } + + this.logger.info('building pg_bsd_indent in %s', pg_bsd_indent_dir.fsPath); + await utils.execShell( + 'make', ['-C', pg_bsd_indent_dir.fsPath], + {cwd: workspace.uri.fsPath}); + + return pg_bsd_indent; + } + + /* src/tools/pgindent/pg_bsd_indent */ + pg_bsd_indent_dir = utils.getWorkspacePgSrcFile(workspace.uri, + 'src', 'tools', 'pgindent', 'pg_bsd_indent'); + const pg_bsd_indent = utils.joinPath(pg_bsd_indent_dir, 'pg_bsd_indent'); + if (await utils.fileExists(pg_bsd_indent)) { + return pg_bsd_indent; + } + + const shouldClone = (!await utils.directoryExists(pg_bsd_indent_dir) || + await utils.directoryEmpty(pg_bsd_indent_dir)); + + vscode.window.showWarningMessage( + 'pg_bsd_indent is not found in pgindent. ' + + 'pg_config is required to build it. ' + + 'Enter path to pg_config'); + const userInput = await vscode.window.showInputBox({ + prompt: 'pg_config is required to build pg_bsd_indent', + password: false, + title: 'Enter pg_config path', + validateInput: async (value: string) => { + const filePath = path.isAbsolute(value) + ? vscode.Uri.file(value) + : utils.joinPath(workspace.uri, value); + if (!await utils.fileExists(filePath)) { + return 'File not found'; + } + } + }); + if (!userInput) { + throw new Error('pg_bsd_indent is not installed and user did not provide pg_config path'); + } + + const pg_configPath = path.isAbsolute(userInput) + ? vscode.Uri.file(userInput) + : utils.joinPath(workspace.uri, userInput); + + /* Clone and build pg_bsd_indent */ + if (shouldClone) { + try { + this.logger.info('cloning pg_bsd_indent repository'); + await utils.execShell( + 'git', ['clone', 'https://git.postgresql.org/git/pg_bsd_indent.git'], + {cwd: utils.getWorkspacePgSrcFile(workspace.uri, 'src', 'tools', 'pgindent').fsPath}); + } catch (error) { + throw new Error(`failed to git clone pg_bsd_indent repository: ${error}`); + } + } + + try { + this.logger.info('building pg_bsd_indent') + await utils.execShell( + 'make', ['all', `PG_CONFIG="${pg_configPath.fsPath}"`], + {cwd: pg_bsd_indent_dir.fsPath}); + return pg_bsd_indent; + } catch (error) { + throw new Error(`failed to build pg_bsd_indent after clone: ${error}`); + } + } + + private async getProcessedTypedefs(pg_bsd_indent: vscode.Uri) { + if (Configuration.CustomTypedefsFile) { + this.logger.debug('custom typedefs file is specified - using this'); + return Configuration.CustomTypedefsFile; + } + + const processedTypedef = utils.joinPath(vscode.Uri.file(os.tmpdir()), + 'pg-hacker-helper.typedefs.list'); + + if (this.savedProcessedTypedef) { + if (await utils.fileExists(this.savedProcessedTypedef)) { + return this.savedProcessedTypedef; + } + + this.savedProcessedTypedef = undefined; + } else if (await utils.fileExists(processedTypedef)) { + /* + * This file is cache in /tmp, so may be created + * in another VS Code session + */ + this.logger.debug('found cached typedefs file in tmp'); + this.savedProcessedTypedef = processedTypedef; + return processedTypedef; + } + + /* + * Add and remove some entries from `typedefs.list` file + * downloaded from buildfarm. + * + * This data did not change since PG 10 and i don't think + * it will change in near future. + */ + const rawTypedef = await this.getTypedefs(pg_bsd_indent); + const contents = await utils.readFile(rawTypedef); + const entries = new Set(contents.split('\n')); + + [ + 'ANY', 'FD_SET', 'U', 'abs', 'allocfunc', 'boolean', 'date', + 'digit', 'ilist', 'interval', 'iterator', 'other', 'pointer', + 'printfunc', 'reference', 'string', 'timestamp', 'type', 'wrap' + ].forEach(e => entries.delete(e)); + + entries.add('bool'); + entries.delete(''); + const arr = Array.from(entries.values()); + arr.sort(); + + await utils.writeFile(processedTypedef, arr.join('\n')); + this.savedProcessedTypedef = processedTypedef; + return processedTypedef; + } + + private async getPgbsdindent(workspace: vscode.WorkspaceFolder) { + if (this.savedPgbsdPath) { + return this.savedPgbsdPath; + } + + const userPgbsdindent = Configuration.getCustomPgbsdindentPath(); + if (userPgbsdindent) { + return path.isAbsolute(userPgbsdindent) + ? vscode.Uri.file(userPgbsdindent) + : utils.joinPath(workspace.uri, userPgbsdindent) + } + + return await this.findExistingPgbsdindent(workspace); + } + + private runPreIndent(contents: string): string { + function replace(regex: any, replacement: any) { + contents = contents.replace(regex, replacement) + } + + // Convert // comments to /* */ + replace(/^([ \t]*)\/\/(.*)$/gm, '$1/* $2 */'); + + // Adjust dash-protected block comments so indent won't change them + replace(/\/\* \+---/gm, '/*---X_X'); + + // Prevent indenting of code in 'extern "C"' blocks + // we replace the braces with comments which we'll reverse later + replace(/(^#ifdef[ \t]+__cplusplus.*\nextern[ \t]+"C"[ \t]*\n)\{[ \t]*$/gm, + '$1/* Open extern "C" */'); + replace(/(^#ifdef[ \t]+__cplusplus.*\n)\}[ \t]*$/gm, + '$1/* Close extern "C" */'); + + // Protect wrapping in CATALOG() + replace(/^(CATALOG\(.*)$/gm, '/*$1*/'); + + return contents; + } + + private runPostIndent(contents: string): string { + function replace(regex: any, replacement: any) { + contents = contents.replace(regex, replacement); + } + + // Restore CATALOG lines + replace(/^\/\*(CATALOG\(.*)\*\/$/gm, '$1'); + + // put back braces for extern "C" + replace(/^\/\* Open extern "C" \*\//gm, '{'); + replace(/^\/\* Close extern "C" \*\/$/gm, '}'); + + // Undo change of dash-protected block comments + replace(/\/\*---X_X/gm, '/* ---'); + + // Fix run-together comments to have a tab between them + replace(/\*\/(\/\*.*\*\/)$/gm, '*/\t$1'); + + // Use a single space before '*' in function return types + replace(/^([A-Za-z_]\S*)[ \t]+\*$/gm, '$1 *'); + + return contents; + } + + private async runPgindentInternal(document: string, + pg_bsd_indent: vscode.Uri) { + /* + * We use pg_bsd_indent directly instead of pgindent because: + * - different pgindent versions behaves differently + * - direct call to pg_bsd_indent is faster + * - pgindent creates temp files which are not removed if error + * happens - we can not track these files (main reason) + */ + + let typedefs = await this.getProcessedTypedefs(pg_bsd_indent); + const preProcessed = this.runPreIndent(document); + const {stdout: processed} = await utils.execShell( + pg_bsd_indent.fsPath, [ + ...PgindentDocumentFormatterProvider.pg_bsd_indentDefaultFlags, + `-U${typedefs.fsPath}`], + { + stdin: preProcessed, + /* + * pg_bsd_indent returns non-zero code if it encountered some + * errors, but for us they are not important. i.e. no newline + * at end of file causes to return code 1 + */ + throwOnError: false, + }); + const postProcessed = this.runPostIndent(processed); + + /* On success cache pg_bsd_indent path */ + this.savedPgbsdPath = pg_bsd_indent; + return postProcessed; + } + + private async runPgindent(document: vscode.TextDocument, + workspace: vscode.WorkspaceFolder) { + let pg_bsd_indent = await this.getPgbsdindent(workspace); + const content = document.getText(); + + try { + return await this.runPgindentInternal(content, pg_bsd_indent); + } catch (err) { + if (await utils.fileExists(pg_bsd_indent)) { + throw err; + } + } + + this.logger.info('pg_bsd_indent seems not installed. trying to install'); + this.savedPgbsdPath = undefined; + pg_bsd_indent = await this.findExistingPgbsdindent(workspace); + return await this.runPgindentInternal(content, pg_bsd_indent); + } + + private async runDiff(originalFile: vscode.Uri, indented: string) { + /* + * Exit code: + * + * 0 - no differences + * 1 - differences found + * >1 - errors occurred + */ + const {code, stdout, stderr} = await utils.execShell( + 'diff', [ '-upd', originalFile.fsPath, '-' ], + {throwOnError: false, stdin: indented}); + if (1 < code) { + throw new Error(`Failed to exec diff: ${stderr}`); + } + return stdout; + } + + private async getTypedefs(pg_bsd_indent: vscode.Uri) { + try { + const typedefsFile = utils.joinPath(pg_bsd_indent, '..', 'typedefs.list'); + if (await utils.fileExists(typedefsFile)) { + return typedefsFile; + } + + const url = 'https://buildfarm.postgresql.org/cgi-bin/typedefs.pl'; + this.logger.info('downloading typedefs file from %s', url); + await utils.execShell('wget', ['-O', typedefsFile.fsPath, url]); + + return typedefsFile; + } catch (err) { + throw new Error(`failed to download typedefs: ${err}`); + } + } + + async provideDocumentFormattingEdits(document: vscode.TextDocument, + options: vscode.FormattingOptions, + token: vscode.CancellationToken) { + this.logger.debug('formatting document: %s', document.uri.fsPath); + let indented; + try { + const workspace = findSuitableWorkspace(document); + indented = await this.runPgindent(document, workspace); + } catch (err) { + this.logger.error('failed to run pgindent', err); + return []; + } + + let diff; + try { + diff = await this.runDiff(document.uri, indented); + } catch (err) { + this.logger.error('failed to run diff for indent', err); + return []; + } + + try { + return DiffParser.parseContents(diff, document); + } catch (err) { + this.logger.error('failed to parse diff contents', err); + return []; + } + } + + async indentFileWithTemp(document: vscode.TextDocument) { + const workspace = findSuitableWorkspace(document); + const indented = await this.runPgindent(document, workspace); + const tempFile = utils.joinPath( + vscode.Uri.file(os.tmpdir()), path.basename(document.uri.fsPath)); + await utils.writeFile(tempFile, indented); + return tempFile; + } +} + +function registerDiffCommand(logger: utils.ILogger, + formatter: PgindentDocumentFormatterProvider) { + /* Preview formatter changes command */ + vscode.commands.registerCommand(Configuration.Commands.FormatterDiffView, async () => { + if (!vscode.window.activeTextEditor) { + vscode.window.showWarningMessage('Could not show diff for file - no active document opened'); + return; + } + + const document = vscode.window.activeTextEditor.document; + let parsed; + try { + parsed = await formatter.indentFileWithTemp(document); + } catch (err) { + logger.error('failed to format file %s', document.uri.fsPath, err); + vscode.window.showErrorMessage('Failed to format document. See error in logs'); + logger.focus(); + return; + } + + try { + await vscode.commands.executeCommand('vscode.diff', document.uri, parsed, 'PostgreSQL formatting') + } catch (err) { + logger.error(`failed to show diff for document %s`, document.uri.fsPath, err); + vscode.window.showErrorMessage('Failed to show diff. See error in logs'); + logger.focus(); + } finally { + if (await utils.fileExists(parsed)) { + await utils.deleteFile(parsed); + } + } + }); +} + +export async function registerFormatting(logger: utils.ILogger) { + const formatter = new PgindentDocumentFormatterProvider(logger); + for (const lang of ['c', 'h']) { + languages.registerDocumentFormattingEditProvider({ + language: lang, + }, formatter); + } + + registerDiffCommand(logger, formatter); +} diff --git a/src/test/extension.test.ts b/src/test/extension.test.ts deleted file mode 100644 index 4ca0ab4..0000000 --- a/src/test/extension.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import * as assert from 'assert'; - -// You can import and use all API from the 'vscode' module -// as well as import your extension to test it -import * as vscode from 'vscode'; -// import * as myExtension from '../../extension'; - -suite('Extension Test Suite', () => { - vscode.window.showInformationMessage('Start all tests.'); - - test('Sample test', () => { - assert.strictEqual(-1, [1, 2, 3].indexOf(5)); - assert.strictEqual(-1, [1, 2, 3].indexOf(0)); - }); -}); diff --git a/src/utils.ts b/src/utils.ts index a5dadb8..c12d387 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,9 +1,19 @@ import * as vscode from 'vscode'; import * as dap from "./dap"; +import path from 'path'; +import * as util from 'util'; +import * as fs from 'fs'; +import * as child_process from 'child_process'; +import { Configuration, NodePreviewTreeViewProvider } from './extension'; +import { VariablesRoot } from './variables'; const nullPointer = '0x0'; const pointerRegex = /^0x[0-9abcdef]+$/i; +export function isNull(value: string) { + return value === nullPointer; +} + /** * Check provided pointer value represents valid value. * That is, it can be dereferenced @@ -12,7 +22,7 @@ const pointerRegex = /^0x[0-9abcdef]+$/i; * @returns Pointer value is valid and not NULL */ export function isValidPointer(value: string) { - return pointerRegex.test(value) && value !== nullPointer; + return pointerRegex.test(value) && !isNull(value); } const identifierRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/; @@ -92,84 +102,252 @@ export function substituteStructName(type: string, target: string) { * @param variable Variable to test * @returns true if variable is raw struct */ -export function isRawStruct(variable: { parent?: {}, value: string }) { +export function isRawStruct(type: string, value: string) { /* * Check that variable is plain struct - not pointer. * Figured out - top level variables has {...} in value, but * struct members are empty strings. (For raw structs). */ - return variable.parent - ? variable.value === '' - : variable.value === '{...}'; + return value === '{...}' || (value === '' && !type.endsWith('[]')); +} + +export function isFixedSizeArray(variable: {parent?: {}, type: string, value: string}): boolean { + /* + * Find pattern: type[size] + * But not: type[] - vla is not expanded + */ + if (variable.type.length < 2) { + return false; + } + + if (variable.type[variable.type.length - 1] !== ']') { + return false; + } + + if (variable.type[variable.type.length - 2] === '[') { + return false; + } + + return true; +} + +/** + * When evaluating 'char*' member, 'result' field will be in form: `0xFFFFF "STR"`. + * This function extracts stored 'STR', otherwise null returned + * + * @param result 'result' field after evaluate + */ +export function extractStringFromResult(result: string) { + const left = result.indexOf('"'); + const right = result.lastIndexOf('"'); + if (left === -1 || left === right) { + /* No STR can be found */ + return null; + } + + return result.substring(left + 1, right); +} + +export function extractBoolFromValue(value: string) { + /* + * On older pg versions bool stored as 'char' and have format: "X '\00X'" + */ + switch (value.trim().toLowerCase()) { + case 'true': + case "1 '\\001'": + return true; + case 'false': + case "0 '\\000'": + return false; + } + + return null; +} + +/** + * Check that output from evaluation is correct enum value. + * That is it is not error message, pointer or something else. + * So, 'result' looks like real enum value. + * + * @returns 'true' if looks like enum value, 'false' otherwise + */ +export function isEnumResult(result: string) { + return isValidIdentifier(result); +} + +/** + * When evaluating 'char*' member, 'result' field will be in form: `0x00000 "STR"`. + * This function extracts stored pointer (0x00000), otherwise null returned + * + * @param result 'result' field after evaluate + */ +export function extractPtrFromStringResult(result: string) { + const space = result.indexOf(' '); + if (space === -1) { + return null; + } + + const ptr = result.substring(0, space); + if (!pointerRegex.test(ptr)) { + return null; + } + return ptr; } export interface ILogger { - debug: (message: string, error?: any) => void; - info: (message: string, error?: any) => void; - warn: (message: string, error?: any) => void; - error: (message: string, error?: any) => void; + debug: (message: string, ...args: any[]) => void; + info: (message: string, ...args: any[]) => void; + warn: (message: string, ...args: any[]) => void; + error: (message: string, ...args: any[]) => void; + focus: () => void; } +/* Start with 2 as in vscode.LogLevel */ export enum LogLevel { - Debug = 0, - Info = 1, - Warn = 2, - Error = 3, - Disable = 4, + Debug = 2, + Info = 3, + Warn = 4, + Error = 5, + Disable = 6, } -export class VsCodeLogger implements ILogger { - minLogLevel: LogLevel; +abstract class BaseLogger implements ILogger { + constructor(protected channel: vscode.OutputChannel) { } + + protected format(msg: string, args: any[]) { + if (args.length && args[args.length - 1] instanceof Error) { + const err: Error = args.pop(); + return `${util.format(msg, ...args)}\n${err.stack}`; + } else { + return util.format(msg, ...args); + } + } + + focus() { + this.channel.show(true); + } + + abstract debug(message: string, ...args: any[]): void; + abstract info(message: string, ...args: any[]): void; + abstract warn(message: string, ...args: any[]): void; + abstract error(message: string, ...args: any[]): void; +} - constructor(private readonly channel: vscode.OutputChannel, minLogLevel: LogLevel) { - this.minLogLevel = minLogLevel; +export class ObsoleteVsCodeLogger extends BaseLogger implements ILogger { + constructor( + channel: vscode.OutputChannel, + public minLogLevel: LogLevel) { + super(channel); } - logGeneric(level: LogLevel, levelStr: string, message: string, error?: any) { + logGeneric(level: LogLevel, levelStr: string, message: string, args: any[]) { if (level < this.minLogLevel) { return; } + /* + * VS Code prior to 1.74.0 does not have LogOutputChannel + * with builtin level/timing features + */ + + /* YYYY-mm-ddTHH:MM:SS.ffffZ -> YYYY-mm-dd HH:MM:SS.ffff */ + const timestamp = new Date().toISOString() + .replace('T', ' ') + .replace('Z', ''); + /* TIMESTAMP [LEVEL]: MESSAGE \n EXCEPTION */ + this.channel.append(timestamp); + this.channel.append(' ['); + this.channel.append(levelStr); + this.channel.append(']: '); + this.channel.appendLine(super.format(message, args)); + } - /* TIMESTAMP [LEVEL]: MESSAGE: EXCEPTION */ - const timestamp = new Date().toISOString().replace('T', ' ').replace('Z', ''); - let msg = `${timestamp} [${levelStr}]: ${message}`; - if (error) { - let errMsg; - if (error instanceof Error) { - errMsg = error.message; - } else if (error instanceof String) { - errMsg = error; - } else if (error.message instanceof String && error.message) { - errMsg = error.message; - } else { - errMsg = JSON.stringify(error); - } + debug(message: string, ...args: any[]) { + this.logGeneric(LogLevel.Debug, 'debug', message, args); + } + info(message: string, ...args: any[]) { + this.logGeneric(LogLevel.Info, 'info', message, args); + } + warn(message: string, ...args: any[]) { + this.logGeneric(LogLevel.Warn, 'warn', message, args); + } + error(message: string, ...args: any[]) { + this.logGeneric(LogLevel.Error, 'error', message, args); + } +} - msg += `: ${errMsg}`; - } +export class VsCodeLogger extends BaseLogger implements ILogger { + constructor(private logOutput: vscode.LogOutputChannel) { + super(logOutput); + } - this.channel.appendLine(msg); + protected canLog(level: LogLevel): boolean { + return this.logOutput.logLevel != vscode.LogLevel.Off && + this.logOutput.logLevel <= level; } - debug(message: string, error?: any) { - this.logGeneric(LogLevel.Debug, 'DEBUG', message, error); + + logGeneric(level: LogLevel, handler: any, msg: string, args: any[]) { + if (this.canLog(level)) { + handler(super.format(msg, args)); + } } - info(message: string, error?: any) { - this.logGeneric(LogLevel.Info, 'INFO', message, error); + + debug(message: string, ...args: any[]) { + this.logGeneric(LogLevel.Debug, this.logOutput.debug, message, args); } - warn(message: string, error?: any) { - this.logGeneric(LogLevel.Warn, 'WARN', message, error); + info(message: string, ...args: any[]) { + this.logGeneric(LogLevel.Info, this.logOutput.info, message, args); } - error(message: string, error?: any) { - this.logGeneric(LogLevel.Error, 'ERROR', message, error); + warn(message: string, ...args: any[]) { + this.logGeneric(LogLevel.Warn, this.logOutput.warn, message, args); + } + error(message: string, ...args: any[]) { + this.logGeneric(LogLevel.Error, this.logOutput.error, message, args); } } export interface IDebuggerFacade { readonly isInDebug: boolean; - evaluate: (expression: string, frameId: number, context?: string) => Promise; - getVariables: () => Promise; + evaluate: (expression: string, frameId: number | undefined, + context?: string) => Promise; + getVariables: (frameId: number) => Promise; getMembers: (variablesReference: number) => Promise; + getTopStackFrameId: (threadId: number) => Promise; + getCurrentFrameId: () => Promise; getSession: () => vscode.DebugSession; + getArrayVariables: (expression: string, length: number, + frameId: number | undefined) => Promise; + getFunctionName: (frameId: number) => Promise; +} + +/** + * Return `true` if evaluation operation failed. + */ +export function isFailedVar(response: dap.EvaluateResponse) { + /* + * gdb/mi has many error types for different operations. + * In common - when error occurs 'result' has message in form + * 'OPNAME: MSG': + * + * - OPNAME - name of the failed operation + * - MSG - human-readable error message + * + * When we send 'evaluate' command this VS Code converts it to + * required command and when it fails, then 'result' member + * contains error message. But if we work with variables (our logic), + * OPNAME will be '-var-create', not that command, that VS Code sent. + * + * More about: https://www.sourceware.org/gdb/current/onlinedocs/gdb.html/GDB_002fMI-Variable-Objects.html + */ + return response.result.startsWith('-var-create'); +} + +function shouldShowScope(scope: dap.Scope) { + /* + * Show only Locals - not Registers. Also do not + * use 'presentationHint' - it might be undefined + * in old versions of VS Code. + */ + return scope.name === 'Locals'; } export class VsCodeDebuggerFacade implements IDebuggerFacade, vscode.Disposable { @@ -178,8 +356,22 @@ export class VsCodeDebuggerFacade implements IDebuggerFacade, vscode.Disposable isInDebug: boolean; session: vscode.DebugSession | undefined; + /** + * Cache of function names (value) in specified frame (key). + * Invalidated each time execution continues. + */ + functionNames?: Map; + + /** + * Cached id of postgres thread. + * As pg have single-threaded/multi-process execution model + * we do not bother tracking multiple threads. + */ + threadId?: number; + constructor() { this.registrations = [ + /* Update current debug session data */ vscode.debug.onDidStartDebugSession(s => { this.session = s; this.isInDebug = true; @@ -187,13 +379,83 @@ export class VsCodeDebuggerFacade implements IDebuggerFacade, vscode.Disposable vscode.debug.onDidTerminateDebugSession(s => { this.session = undefined; this.isInDebug = false; + this.threadId = undefined; }), + + /* Invalidate function names cache */ + vscode.debug.onDidReceiveDebugSessionCustomEvent(e => { + switch (e.event) { + case 'stopped': + this.threadId = undefined; + /* fallthrough */ + case 'continued': + this.functionNames = undefined; + break; + } + }) ]; this.session = vscode.debug.activeDebugSession; this.isInDebug = vscode.debug.activeDebugSession !== undefined; } + private async getThreadId() { + if (this.threadId) { + return this.threadId; + } + + const threads: dap.ThreadsResponse = await this.getSession().customRequest('threads'); + if (!threads) { + throw new Error('Failed to obtain threads from debugger'); + } + const threadId = threads.threads[0].id; + this.threadId = threadId; + + return threadId; + } + + getArrayVariables = async (array: string, length: number, + frameId: number | undefined) => { + const expression = `(${array}), ${length}`; + const evalResponse = await this.evaluate(expression, frameId); + if (!evalResponse?.variablesReference) { + return []; + } + + return await this.getMembers(evalResponse.variablesReference); + } + + getCurrentFrameId = async () => { + /* debugFocus API */ + return (vscode.debug.activeStackItem as vscode.DebugStackFrame | undefined)?.frameId; + } + + switchToManualArrayExpansion() { + this.getArrayVariables = async function (array: string, length: number, + frameId: number | undefined) { + /* + * In old VS Code there is no array length expansion feature. + * We can not just add ', length' to expression, so evaluate each + * element manually + */ + const variables: dap.DebugVariable[] = []; + for (let i = 0; i < length; i++) { + const expression = `(${array})[${i}]`; + const evalResponse = await this.evaluate(expression, frameId); + const variable = { + evaluateName: expression, + memoryReference: evalResponse.memoryReference, + name: `[${i}]`, + type: evalResponse.type, + value: evalResponse.result, + variablesReference: evalResponse.variablesReference + } as dap.DebugVariable; + variables.push(variable); + } + return variables + } + } + getSession(): vscode.DebugSession { if (this.session !== undefined) { return this.session; @@ -208,47 +470,205 @@ export class VsCodeDebuggerFacade implements IDebuggerFacade, vscode.Disposable return this.session; } - async evaluate(expression: string, frameId: number, context?: string): Promise { - context ??= 'repl'; - return await this.getSession().customRequest('evaluate', { expression, context, frameId } as dap.EvaluateArguments); - } - - async getMembers(variablesReference: number): Promise { - const response: dap.VariablesResponse = await this.getSession().customRequest('variables', { variablesReference } as dap.VariablesArguments); - return response.variables; - } + async getFunctionName(frameId: number) { + /* First, search in cache */ + if (this.functionNames) { + const name = this.functionNames.get(frameId); + if (name !== undefined) { + return name; + } + } - async getCurrentScopes() { - const frame = vscode.debug.activeStackItem as vscode.DebugStackFrame | undefined; - if (!frame || !frame.frameId) { + const threadId = await this.getThreadId(); + + /* + * DAP returns new frameId each 'stackTrace' invocation, so we can + * not just iterate through all StackFrames and find equal frame id. + * + * I found such hack - all frames returned by 'evaluate' are in form + * 'frameId = 1000 + frameIndex' (at least I rely on it very much). + * We just need to get this single frame. + */ + const frameIndex = frameId - 1000; + + const st = await this.getStackTrace(threadId, 1, frameIndex); + if (!(st && st.stackFrames)) { return; } - return await this.getScopes(frame.frameId); + const frame = st.stackFrames[0]; + + /* Remove arguments from function name */ + const argsIdx = frame.name.indexOf('('); + if (argsIdx === -1) { + return frame.name; + } + + const name = frame.name.substring(0, argsIdx); + + /* Update cache */ + if (this.functionNames === undefined) { + this.functionNames = new Map([[frameId, name]]); + } else { + this.functionNames.set(frameId, name); + } + + return name; } - async getVariables(): Promise { - const scopes = await this.getCurrentScopes(); + async evaluate(expression: string, frameId: number | undefined, context?: string) { + context ??= 'watch'; + return await this.getSession().customRequest('evaluate', { + expression, + context, + frameId + } as dap.EvaluateArguments); + } + + async getMembers(variablesReference: number): Promise { + const response: dap.VariablesResponse = await this.getSession() + .customRequest('variables', { + variablesReference + } as dap.VariablesArguments); + return response.variables; + } + + async getVariables(frameId: number): Promise { + const scopes = await this.getScopes(frameId); if (scopes === undefined) { return []; } - return (await Promise.all(scopes - .filter(s => s.presentationHint === 'locals' || s.presentationHint === 'arguments') - .flatMap(sa => sa) - .map(s => this.getMembers(s.variablesReference)))) - .flatMap(m => m); + const variables: dap.DebugVariable[] = []; + for (const scope of scopes.filter(shouldShowScope)) { + const members = await this.getMembers(scope.variablesReference); + variables.push(...members); + } + return variables; } async getScopes(frameId: number): Promise { - const response: dap.ScopesResponse = await this.getSession().customRequest('scopes', { frameId } as dap.ScopesArguments); + const response: dap.ScopesResponse = await this.getSession() + .customRequest('scopes', { frameId } as dap.ScopesArguments); return response.scopes; } + private async getStackTrace(threadId: number, levels?: number, startFrame?: number) { + return await this.getSession().customRequest('stackTrace', { + threadId, + levels, + startFrame + } as dap.StackTraceArguments) as dap.StackTraceResponse; + } + + async getTopStackFrameId(threadId: number): Promise { + const response: dap.StackTraceResponse = await this.getStackTrace(threadId, 1); + return response.stackFrames?.[0]?.id; + } + dispose() { this.registrations.forEach(r => r.dispose()); this.registrations.length = 0; } + + switchToEventBasedRefresh(context: vscode.ExtensionContext, provider: NodePreviewTreeViewProvider) { + /* + * Prior to VS Code version 1.90 there is no debugFocus API - + * we can not track current stack frame. It is very convenient, + * because single event refreshes state and also we keep track + * of stack frame selected in debugger view. + * + * For older versions we use event based implementation - + * subscribe to debugger events and filter out needed: + * continue execution, stopped (breakpoint), terminated etc... + * + * NOTES: + * - We can not track current stack frame, so this feature is + * not available for users. + * - Support only 'cppdbg' configuration - tested only for it + */ + + let savedThreadId: undefined | number = undefined; + const disposable = vscode.debug.registerDebugAdapterTrackerFactory('cppdbg', { + createDebugAdapterTracker(_: vscode.DebugSession) { + return { + onDidSendMessage(message: dap.ProtocolMessage) { + if (message.type === 'response') { + if (message.command === 'continue') { + /* + * `Continue' command - clear + */ + provider.refresh(); + } + + return; + } + + if (message.type === 'event') { + if (message.event === 'stopped' || message.event === 'terminated') { + /* + * Hit breakpoint - show variables + */ + provider.refresh(); + savedThreadId = message.body?.threadId as number | undefined; + } + } + }, + + onWillStopSession() { + /* Debug session terminates - clear */ + provider.refresh(); + }, + } + }, + }); + context.subscriptions.push(disposable); + this.getCurrentFrameId = async () => { + /* + * We can not track selected stack frame - return last (top) + */ + if (!(this.isInDebug && savedThreadId)) { + return; + } + + return await this.getTopStackFrameId(savedThreadId); + } + } +} + +function getFileType(stats: fs.Stats) { + if (stats.isFile()) { + return vscode.FileType.File; + } + if (stats.isDirectory()) { + return vscode.FileType.Directory; + } + if (stats.isSymbolicLink()) { + return vscode.FileType.SymbolicLink; + } + + return vscode.FileType.Unknown; +} + +function statFile(uri: vscode.Uri): Thenable { + if (Features.hasWorkspaceFs()) { + return vscode.workspace.fs.stat(uri); + } else { + return new Promise((resolve, reject) => { + fs.stat(uri.fsPath, (err, stats) => { + if (err) { + reject(err); + } else { + resolve({ + ctime: stats.ctime.valueOf(), + mtime: stats.mtime.valueOf(), + size: stats.size, + type: getFileType(stats), + } as vscode.FileStat); + } + }); + }) + } } /** @@ -261,17 +681,20 @@ export class VsCodeDebuggerFacade implements IDebuggerFacade, vscode.Disposable */ export async function fileExists(path: vscode.Uri): Promise { try { - /* Only directory we can not read - files, sym. links etc.. - can read */ - const result = await vscode.workspace.fs.stat(path); - return result.type !== vscode.FileType.Directory; + const result = await statFile(path); + return !!(result.type & vscode.FileType.File); } catch { return false; } } +/** + * Check that at specified path exists some entry. + * No matter what - file or directory. Just something + */ export async function fsEntryExists(path: vscode.Uri): Promise { try { - await vscode.workspace.fs.stat(path); + await statFile(path); return true; } catch { return false; @@ -280,17 +703,316 @@ export async function fsEntryExists(path: vscode.Uri): Promise { export async function directoryExists(path: vscode.Uri) { try { - const result = await vscode.workspace.fs.stat(path); - return result.type === vscode.FileType.Directory; + const result = await statFile(path); + return !!(result.type & vscode.FileType.Directory); } catch { return false; } } +export async function createDirectory(path: vscode.Uri): Promise { + if (Features.hasWorkspaceFs()) { + return vscode.workspace.fs.createDirectory(path); + } else { + return new Promise((resolve, reject) => { + fs.mkdir(path.fsPath, null, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }) + }) + } +} + +export async function directoryEmpty(path: vscode.Uri) { + if (Features.hasWorkspaceFs()) { + const files = await vscode.workspace.fs.readDirectory(path); + return files.length === 0; + } else { + return await new Promise((resolve, reject) => { + fs.readdir(path.fsPath, (err, files) => { + if (err) { + reject(err); + } else { + resolve(files.length === 0); + } + }); + }); + } +} + +export async function copyFile(file: vscode.Uri, targetFile: vscode.Uri) { + if (Features.hasWorkspaceFs()) { + await vscode.workspace.fs.copy(file, targetFile); + } else { + return await new Promise((resolve, reject) => { + fs.copyFile(file.fsPath, targetFile.fsPath, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }) + }) + } +} + +export function createTempFileName(template: string) { + return template.replace('{}', crypto.randomUUID().toString()); +} + +export async function execShell(cmd: string, args?: string[], + options?: { cwd?: string, + env?: any, + throwOnError?: boolean, + stdin?: string } ): Promise<{code: number, stdout: string, stderr: string}> { + return await new Promise<{code: number, stdout: string, stderr: string}>((resolve, reject) => { + const {cwd, env, throwOnError, stdin} = options || {}; + const child = child_process.spawn(cmd, args, {cwd, env, shell: true, }); + const stderr: string[] = []; + const stdout: string[] = []; + + child.on('error', (err) => { + reject(err); + }); + + child.stderr?.on('data', (chunk) => { + stderr.push(chunk); + }); + child.stdout?.on('data', (chunk) => { + stdout.push(chunk); + }); + + child.on('close', (code) => { + if (code !== 0 && (throwOnError === undefined || throwOnError)) { + reject(new Error(`command failed to execute. error stack: ${stdout.join('')}`)); + } else { + resolve({ + code: code ?? 0, + stdout: stdout.join(''), + stderr: stderr.join(''), + }); + } + }); + child.on('error', (err) => { + reject(err); + }); + + if (stdin) { + child.stdin.write(stdin, (err) => { + if (err) { + reject(err); + } + }); + child.stdin.on('error', (err) => { + if (err) { + reject(err); + } + }); + } + child.stdin.end(); + + setTimeout(() => { + if (child.exitCode !== null) { + child.kill('SIGKILL'); + } + }, 60 * 1000); + }); +} + +export async function deleteFile(file: vscode.Uri) { + if (Features.hasWorkspaceFs()) { + await vscode.workspace.fs.delete(file, { useTrash: false }); + } else { + return new Promise((resolve, reject) => { + fs.unlink(file.fsPath, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }) + }); + } +} + +export function readFile(path: vscode.Uri) { + if (Features.hasWorkspaceFs()) { + return vscode.workspace.fs.readFile(path) + .then(value => new TextDecoder().decode(value)); + } else { + return new Promise((resolve, reject) => { + fs.readFile(path.fsPath, (err, value) => { + if (err) { + reject(err); + return; + } + + try { + resolve(new TextDecoder().decode(value)); + } catch (err: any) { + reject(err); + } + }) + }) + } +} + +export function writeFile(path: vscode.Uri, data: string): Thenable { + if (Features.hasWorkspaceFs()) { + return vscode.workspace.fs.writeFile(path, new TextEncoder().encode(data)); + } else { + return new Promise((resolve, reject) => { + fs.writeFile(path.fsPath, data, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }) + }) + } +} + const builtInTypes = new Set([ 'char', 'short', 'int', 'long', 'double', 'float', '_Bool', 'void', ]) export function isBuiltInType(type: string) { return builtInTypes.has(getStructNameFromType(type)); +} + +export function getWorkspacePgSrcFile(workspace: vscode.Uri, ...paths: string[]) { + const customDir = Configuration.getSrcPath(); + if (customDir) { + return joinPath(workspace, customDir, ...paths); + } + + return joinPath(workspace, ...paths); +} + +export function getPgSrcFile(...paths: string[]) { + const customDir = Configuration.getSrcPath(); + if (customDir) { + return path.join(customDir, ...paths); + } + + return path.join(...paths); +} + +/** + * Return integer representation of SemVer version string + * + * @param ver Version string + */ +export function version(ver: string): number { + /* + * Search SemVer string in form + * + * MAJOR.MINOR.PATCH + * + * where PATCH may be missing. + * + * We use regex because of suffixes that can be inside version string. + * Like: '1.90.0-insiders' or '1.89.2-prerelease'. + * So just split by '.' is not enough. + */ + const parse = /(\d+)\.(\d+)(\.(\d+))?/.exec(ver); + + if (!parse?.length) { + throw new Error(`Invalid SemVer string: ${ver}`); + } + + let result = 0; + + /* X.Y.Z - 1, 2, 4 indexes in regex */ + result += parseInt(parse[1]) * 1000000; + result += parseInt(parse[2]) * 1000; + + if (parse[4]) { + result += parseInt(parse[4]); + } + + if (Number.isNaN(result)) { + throw new Error(`Invalid SemVer string: ${ver}. Result version number is NaN`); + } + + return result; +} + +/* + * Various feature flags related to VS Code + * functionality, that depends on API + */ +let debugFocusEnabled: boolean | undefined = undefined; +let hasArrayLengthFeature: boolean | undefined = undefined; +let logOutputLanguageEnabled: boolean | undefined = undefined; +let hasWorkspaceFs: boolean | undefined = undefined; +let hasUriJoinPath: boolean | undefined = undefined; +let hasLogOutputChannel: boolean | undefined = undefined; + +export class Features { + static versionAtLeast(ver: string) { + return version(ver) <= version(vscode.version); + } + + static debugFocusEnabled() { + /* + * Easily track debugger actions (breakpoints etc) and + * selected call stack changes + */ + if (debugFocusEnabled === undefined) { + debugFocusEnabled = this.versionAtLeast('1.90.0'); + } + return debugFocusEnabled; + } + + static hasEvaluateArrayLength() { + /* Evaluate array length in debugger like `arrayPtr, length' */ + if (hasArrayLengthFeature === undefined) { + hasArrayLengthFeature = this.versionAtLeast('1.68.0'); + } + return hasArrayLengthFeature; + } + + static logOutputLanguageEnabled() { + /* Set 'log' to languageId in Output Channel */ + if (logOutputLanguageEnabled === undefined) { + logOutputLanguageEnabled = this.versionAtLeast('1.67.0'); + } + return logOutputLanguageEnabled; + } + + static hasWorkspaceFs() { + /* Has 'vscode.workspace.fs' */ + if (hasWorkspaceFs === undefined) { + hasWorkspaceFs = this.versionAtLeast('1.37.0'); + } + return hasWorkspaceFs; + } + + static hasUriJoinPath() { + if (hasUriJoinPath === undefined) { + hasUriJoinPath = this.versionAtLeast('1.45.0'); + } + return hasUriJoinPath; + } + + static hasLogOutputChannel() { + if (hasLogOutputChannel === undefined) { + hasLogOutputChannel = this.versionAtLeast('1.74.0'); + } + + return hasLogOutputChannel; + } +} + +export function joinPath(base: vscode.Uri, ...paths: string[]) { + if (Features.hasUriJoinPath()) { + return vscode.Uri.joinPath(base, ...paths); + } + + return vscode.Uri.file(path.join(base.fsPath, ...paths)); } \ No newline at end of file diff --git a/src/variables.ts b/src/variables.ts index b390eaf..d3c5173 100644 --- a/src/variables.ts +++ b/src/variables.ts @@ -2,6 +2,8 @@ import * as vscode from 'vscode'; import * as utils from "./utils"; import * as dap from "./dap"; import * as constants from './constants'; +import { getActiveResourcesInfo } from 'process'; +import { stringify } from 'querystring'; export interface AliasInfo { alias: string; @@ -9,7 +11,7 @@ export interface AliasInfo { } /** - * Registry for all known `NodeTag' enum values + * Registry for all known `NodeTag' enum values */ export class NodeVarRegistry { /** @@ -18,23 +20,36 @@ export class NodeVarRegistry { nodeTags: Set = new Set(constants.getDefaultNodeTags()); /** - * Known aliases for Node variables - `typedef' + * Known NodeTags that represents Expr nodes. + * Required for Exprs representation in tree view as expressions + */ + exprs: Set = new Set(constants.getDisplayedExprs()) + + /** + * Known aliases for Node variables - `typedef RealType* Alias' */ aliases: Map = new Map(constants.getDefaultAliases()); - /* + /* + * Known references of Bitmapset. + * Map: field_name -> BitmapsetReference + */ + bmsRefs: Map = new Map(constants.getWellKnownBitmapsetReferences()); + + /* * Update stored node types for internal usage from provided * node tag file. i.e. `nodes.h' or `nodetags.h'. */ updateNodeTypesFromFile(file: vscode.TextDocument) { let added = 0; for (let lineNo = 0; lineNo < file.lineCount; lineNo++) { - /* - * NodeTag has following representation: + /* + * NodeTag enum value has following representation: + * * [spaces] T_*tag_name* [= *number*], - * + * * We must obtain only *tag_name* part, because 'T_' prefix - * is constant and not important and *number* also not + * is constant and not important and *number* also not * important because we must focus on words, not numbers - if * there was garbage in structure, Node->type will be random numbers. * That is how we find garbage. @@ -49,7 +64,9 @@ export class NodeVarRegistry { continue; } - const tag = text.replaceAll(',', '').replace('T_', '').split(' ', 1)[0]; + const tag = text.replace(',', '') + .replace('T_', '') + .split(' ', 1)[0]; if (tag.trim() === '') { continue; } @@ -69,50 +86,54 @@ export class NodeVarRegistry { /** * Check provided type is derived from Node. That is, we can obtain * NodeTag from it. - * + * * @param type Type of variable * @returns true if provided type is derived from Node */ isNodeVar(type: string) { - /* + /* * Valid Node variable must have type in this form: * [const] [struct] NAME * - * + * * Optional `const' and `struct' keywords follows NAME - target struct name. * If NAME in our nodeTypes set - this is what we want. But also, we * should take number of pointers into account, because: - * - If this is a raw struct (no pointers) - no casting needed because + * - If this is a raw struct (no pointers) - no casting needed because * it's size (and fields) is already known - * - As for pointer - only single `*' creates valid Node* variable that we can + * - As for pointer - only single `*' creates valid Node* variable that we can * work with - * - * Also, there might be aliases - check them also + * + * Aliases must be checked at start. So do not handle them here */ let typeName = utils.getStructNameFromType(type); + + /* [const] [struct] NAME * */ if (this.nodeTags.has(typeName) && utils.getPointersCount(type) === 1) { - /* [const] [struct] NAME * */ return true; } const alias = this.aliases.get(typeName); - if (alias) { - /* typedef NAME *ALIAS */ - type = utils.substituteStructName(type, alias); - typeName = utils.getStructNameFromType(type); - return this.nodeTags.has(typeName) && utils.getPointersCount(type) === 1; + if (!alias) { + return false; } - return false; + type = type.replace(typeName, alias); + typeName = utils.getStructNameFromType(type); + return this.nodeTags.has(typeName) && utils.getPointersCount(type) === 1; } /** * Check if passed string is valid NodeTag and registered NodeTag - * + * * @param tag String to test */ isNodeTag(tag: string) { return this.nodeTags.has(tag); } + + findBmsReference(bms: BitmapSetSpecialMember) { + return this.bmsRefs.get(bms.name); + } } export interface ArraySpecialMemberInfo { @@ -121,30 +142,87 @@ export interface ArraySpecialMemberInfo { lengthExpr: string; } +export interface ListPtrSpecialMemberInfo { + /* + * Real type of List members (must be pointer or alias) + */ + type: string; + + /** + * Pair of [Struct, Member] identifying this member + */ + member?: [string, string]; + + /** + * Pair of [Function, Variable] identifying this member + */ + variable?: [string, string]; +} + export class SpecialMemberRegistry { /** * Double map: Type name -> (Member Name -> Info Object). */ arraySpecialMembers: Map>; + /** + * Double map: Member/variable name -> (Struct/Function name -> Info object). + * + * Outer key is name of member or variable. + * Inner key is name of structure or function (containing this member/variable + * respectively). + */ + listCustomPtrs: Map>; + constructor() { this.arraySpecialMembers = new Map(); + this.listCustomPtrs = new Map(); this.addArraySpecialMembers(constants.getArraySpecialMembers()); + this.addNodePtrSpecialMembers(constants.getKnownCustomListPtrs()); } addArraySpecialMembers(elements: ArraySpecialMemberInfo[]) { for (const element of elements) { const typeMap = this.arraySpecialMembers.get(element.typeName); if (typeMap === undefined) { - this.arraySpecialMembers.set(element.typeName, new Map([[element.memberName, element]])); + this.arraySpecialMembers.set(element.typeName, new Map([ + [element.memberName, element] + ])); } else { typeMap.set(element.memberName, element); } } } - getArraySpecialMember(parentType: string, memberName: string): ArraySpecialMemberInfo | undefined { - const membersMap = this.arraySpecialMembers.get(utils.getStructNameFromType(parentType)); + addNodePtrSpecialMembers(elements: ListPtrSpecialMemberInfo[]) { + const addRecord = (member: string, funcOrStruct: string, + info: ListPtrSpecialMemberInfo) => { + const map = this.listCustomPtrs.get(member); + if (map === undefined) { + this.listCustomPtrs.set(member, new Map([ + [funcOrStruct, info] + ])) + } else { + map.set(funcOrStruct, info); + } + } + + for (const e of elements) { + if (e.member) { + const [struct, member] = e.member; + addRecord(member, struct, e); + } + + if (e.variable) { + const [func, variable] = e.variable; + addRecord(variable, func, e); + } + } + } + + getArraySpecialMember(parentType: string, memberName: string) { + const parentTypeName = utils.getStructNameFromType(parentType); + const membersMap = this.arraySpecialMembers.get(parentTypeName); if (membersMap === undefined) { return; } @@ -158,14 +236,252 @@ export class SpecialMemberRegistry { } } -export interface ExecContext { +/** + * Container type to store information about HTAB hash tables. + * HTAB is generalized, so there only 2 ways to identify HTAB and it's + * stored type - variable and member of another structure. + * + * This is also generalized - "parent" can be name of function or + * name of structure, and "member" - name of variable in function + * or member of structure, accordingly. + */ +export interface HtabEntryInfo { + /** + * Type of entry in HTAB* + */ + type: string; + + /** + * Name of the HTAB* member + */ + member: string; + + /** + * Parent structure, containing HTAB* member + */ + parent: string; +} + +/** + * Container type to store information about simple hash table - simplehash. + * + * Main reason to introduce it - is to track 'iteration' facility. + * Due to compiler optimizations (unused symbol pruning) iterator type + * and iteration functions can be removed if they are not used. + * + * For this purpose 'canIterate' flag was introduced. Initially, it set to 'true' + * and then set to 'false' if error like 'unable to create variable' appears - + * it serves as a signal, that there is no means to implement iteration. + * + * You may think, that we can monkey-patch iteration functions/types, but no. + * Main reason is that 'start_iterate' and 'iterate' logic is quite complex + * and *iteration state* stored in each entry of hash table (state member), + * so it heavily depends on internal structure layout and doing monkey-patching + * can break everything. + * + * We can overcome this by rewriting everything here (in extension). + * Currently, I do not want to do that, but maybe in future I'll change my mind. + */ +export interface SimplehashEntryInfo { + /* + * 'SH_PREFIX' defined when declaring/defining hash table + */ + prefix: string; + + /* + * Type of element stored in this hash table. + * Should be a pointer type, because no checks and adding pointer performed. + */ + elementType: string; + + /* + * Flag, indicating that 'start_iterate' and 'iterate' functions + * and 'iterator' struct exist. + */ + canIterate: boolean; +} + +export class HashTableTypes { + /** + * Map (member name -> (parent struct name -> type info structure)) + */ + htab: Map>; + + /** + * Map (prefix -> entry type). + */ + simplehash: Map; + + constructor() { + this.htab = new Map(); + this.simplehash = new Map(); + this.addHTABTypes(constants.getWellKnownHTABTypes()); + this.addSimplehashTypes(constants.getWellKnownSimpleHashTableTypes()); + } + + addHTABTypes(elements: HtabEntryInfo[]) { + for (const element of elements) { + const map = this.htab.get(element.member); + if (map === undefined) { + this.htab.set(element.member, new Map([[element.parent, element]])); + } else { + /* + * Don't bother with duplicate types - this is normal situation, + * because when configuration file read second+ time all elements + * from is are re-added, so duplicates WILL be encountered. + */ + map.set(element.parent, element); + } + } + } + + addSimplehashTypes(elements: SimplehashEntryInfo[]) { + for (const e of elements) { + this.simplehash.set(e.prefix, e); + } + } + + findSimpleHashTableType(type: string) { + const struct = utils.getStructNameFromType(type); + const prefix = SimplehashMember.getPrefix(struct); + if (!prefix) { + return undefined; + } + + return this.simplehash.get(prefix); + } +} + +/** + * Context of current execution. + */ +export class ExecContext { + /** + * Registry about NodeTag variables information + */ nodeVarRegistry: NodeVarRegistry; + + /** + * Registry with information of Special Members + */ specialMemberRegistry: SpecialMemberRegistry; + + /** + * Types of entries, that different HTAB store (dynahash.c) + */ + hashTableTypes: HashTableTypes; + + /** + * Facade for debugger interface (TAP) + */ debug: utils.IDebuggerFacade; + + /** + * Flag, indicating that this version of PostgreSQL + * has common class for 'String', 'Integer' and other + * value structures. + * Updated at runtime in 'ValueVariable'. + * + * Initialized with `false` and updated during runtime + */ + hasValueStruct = false; + + /** + * Flag, indicating that this version of PostgreSQL + * has `palloc` implementation as function, otherwise + * it is macro and we must use `MemoryContextAlloc`. + * + * Initialized with `true` and updated during runtime + */ + hasPalloc = true; + + + /** + * 'MemoryContextData' struct has 'allowInCritSection' + * member. It must be checked during memory allocation. + * + * Introduced in 9.5 version + */ + hasAllowInCritSection = true; + + /** + * This postgres version has 'bms_is_valid_set' function + * used to validate Bitmapset variable. + * Without such check next invocations of Bitmapset + * functions will crash backend (because of 'Assert's). + */ + hasBmsIsValidSet = true; + + /** + * This postgres version has 'bms_next_member' function. + * It is used to get members of Bitmapset faster than + * old version (by copying existing one and popping data + * from it + palloc/pfree). + */ + hasBmsNextMember = true; + + /** + * Bitmapset in old pg versions do not have separate T_Bitmapset + * node tag. + * This is required to check whether Bitmapset is valid + * for further operations (function invocations), otherwise + * we can get SEGFAULT. + */ + hasBmsNodeTag = true; + + /** + * Has `get_attname` function. + * + * It is used when formatting `Var` representation. + * This function is preferred, because allows not to throw ERROR + * if failed to get attribute. + */ + hasGetAttname = true; + + constructor(nodeVarRegistry: NodeVarRegistry, specialMemberRegistry: SpecialMemberRegistry, + debug: utils.IDebuggerFacade, hashTableTypes: HashTableTypes) { + this.nodeVarRegistry = nodeVarRegistry; + this.specialMemberRegistry = specialMemberRegistry; + this.hashTableTypes = hashTableTypes; + this.debug = debug; + } +} + +/** + * Special value for frameId used by ephemeral variables: + * they do not need to evaluate anything. + * + * Examples: VariablesRoot, ScalarVariable, etc... + */ +const invalidFrameId = -1; + +/** + * Check that caught exception can be safely ignored + * and not shown to user. + * This is applied in end-point functions like 'getTreeItem' + * or 'getChildren'. + * + * @param error Error object caught using 'try' + */ +function isExpectedError(error: any) { + /* + * Calls to debugger with some evaluations might be time consumptive + * and user will perform step before we end up computation. + * In such case, we will get exception with messages like: + * - "Cannot evaluate expression on the specified stack frame." + * - "Unable to perform this action because the process is running." + * + * I do not know whether these messages are translated, so + * just checking 'error.message' does not look like a solid solution. + * In the end, we just catch all VS Code exceptions (they have + * 'CodeExpectedError' in name, at least exceptions with messages + * above). + */ + return error && (error instanceof EvaluationError || error?.name === 'CodeExpectedError'); } export abstract class Variable { - /** + /** * Raw variable name (variable/struct member) */ name: string; @@ -176,165 +492,534 @@ export abstract class Variable { type: string; /** - * Evaluate value of variable. - * May be empty for structs (no pointers) + * Evaluate value of variable. Have different meaning for different types of variables: + * + * - Empty for raw structures + * - Actual values for primitives (integers, floats) + * - Pointer value for pointers (for 'char *' it has actual string at the end) */ value: string; /** * Parent of this variable. - * May be undefined for usual variables, and + * May be undefined for usual variables, and * must be defined if current element - member */ parent?: Variable; - constructor(name: string, value: string, type: string, parent?: Variable) { + /* + * Cached variables. + * If undefined - `getChildren` was not called; + * If length == 0 - no children (scalar variable) + */ + children: Variable[] | undefined; + + /** + * Execution context for current session. + */ + context: ExecContext; + + /** + * Logger + */ + logger: utils.ILogger; + + /** + * Number of frame, this variable belongs + */ + frameId: number; + + /** + * Shortcut for `this.context.debug` + */ + get debug() { + return this.context.debug; + } + + constructor(name: string, value: string, type: string, context: ExecContext, frameId: number, parent: Variable | undefined, logger: utils.ILogger) { this.parent = parent; this.name = name; this.value = value; this.type = type; + this.context = context; + this.logger = logger; + this.frameId = frameId; } /** * Get children of this variable - * - * @param debug Debugger facade + * * @returns Array of child variables or undefined if no children */ - abstract getChildren(context: ExecContext): Promise; + async getChildren(): Promise { + try { + if (this.children != undefined) { + /* + * return `undefined` if no children - scalar variable + */ + return this.children.length + ? this.children + : undefined; + } + + const children = await this.doGetChildren(); + if (children) { + this.children = children; + } else { + this.children = []; + } + return children; + } catch (error: any) { + this.logger.debug('failed to get children for %s', this.name, error); + if (isExpectedError(error)) { + return; + } else { + throw error; + } + } + } + + abstract doGetChildren(): Promise; protected isExpandable() { - return (utils.isValidPointer(this.value) && !utils.isBuiltInType(this.type)) || - utils.isRawStruct(this); + /* Pointer to struct */ + if (utils.isValidPointer(this.value)) { + return true; + } + + /* Do not deref NULL */ + if (utils.isNull(this.value)) { + return false; + } + + /* Embedded or top level structs */ + if (utils.isRawStruct(this.type, this.value)) { + return true; + } + + /* Fixed size array: type[size] */ + if (utils.isFixedSizeArray(this)) { + return true; + } + + return false; + } + + protected async getDescription() { + return this.value; } /** * Create {@link vscode.TreeItem TreeItem} for variables view */ - getTreeItem(): vscode.TreeItem { - return { - label: `${this.name}: ${this.type} = `, - description: this.value, - collapsibleState: this.isExpandable() - ? vscode.TreeItemCollapsibleState.Collapsed - : vscode.TreeItemCollapsibleState.None, + async getTreeItem(): Promise { + try { + return { + label: `${this.name}: ${this.type}`, + description: await this.getDescription(), + collapsibleState: this.isExpandable() + ? vscode.TreeItemCollapsibleState.Collapsed + : vscode.TreeItemCollapsibleState.None, + } + } catch (error: any) { + this.logger.debug('failed get TreeItem for %s', this.name, error); + + if (isExpectedError(error)) { + /* Placeholder */ + return {}; + } else { + throw error; + } } } - static async createVariable(debugVariable: dap.DebugVariable, frameId: number, context: ExecContext, logger: utils.ILogger, parent?: RealVariable): Promise { - /* - * We pass RealVariable - not generic Variable, - * because if we want to use this function - if means + /** + * Utility function to handle type aliases. + * This is required to properly handle other types. + * + * For example, `MemoryContext' - alias for `MemoryContextData *' + * and it does not have is's own NodeTag. So when performing + * cast we get subtle error because we cast to type `AllocSetContext' + * (without pointer). + */ + private static getRealType(debugVariable: dap.DebugVariable, context: ExecContext) { + const structName = utils.getStructNameFromType(debugVariable.type); + const alias = context.nodeVarRegistry.aliases.get(structName); + if (!alias) { + return debugVariable.type; + } + + const resultType = utils.substituteStructName(debugVariable.type, alias); + return resultType; + } + + static async create(debugVariable: dap.DebugVariable, frameId: number, + context: ExecContext, logger: utils.ILogger, + parent?: Variable): Promise { + /* + * We pass RealVariable - not generic Variable, + * because if we want to use this function - if means * we create it using debugger interface and this variable * is real */ - if (utils.isRawStruct(debugVariable) || !utils.isValidPointer(debugVariable.value)) { - return new RealVariable({ - ...debugVariable, - frameId, - parent - }, logger); + const args: RealVariableArgs = { + ...debugVariable, + frameId, + parent, + context, + logger, + }; + + const realType = Variable.getRealType(debugVariable, context); + if (utils.isRawStruct(realType, debugVariable.value) || /* Raw struct, i.e. allocated on stack */ + !utils.isValidPointer(debugVariable.value)) { /* NULL */ + if (utils.isNull(debugVariable.value) && + debugVariable.type.endsWith('List *')) { + /* + * Empty List is NIL == NULL == '0x0' + * + * Also 'endsWith' checks for 'const List *' + */ + return new ListNodeVariable('List', args); + } + + return new RealVariable(args); + } + + /* + * PostgreSQL versions prior 16 do not have Bitmapset Node. + * So handle Bitmapset (with Relids) here. + */ + if (BitmapSetSpecialMember.isBitmapsetType(realType)) { + return new BitmapSetSpecialMember(args); } /* NodeTag variables: Node, List, Bitmapset etc.. */ - if (context.nodeVarRegistry.isNodeVar(debugVariable.type)) { - const nodeTagVar = await NodeTagVariable.createNodeTagVariable(debugVariable, frameId, context, logger, parent); + if (context.nodeVarRegistry.isNodeVar(realType)) { + const nodeTagVar = await NodeVariable.create(debugVariable, frameId, + context, logger, parent); if (nodeTagVar) { return nodeTagVar; } } /* Special members */ - if (parent?.type) { - const specialMember = context.specialMemberRegistry.getArraySpecialMember(parent.type, debugVariable.name); + if (parent?.type && parent instanceof RealVariable) { + const specialMember = context.specialMemberRegistry + .getArraySpecialMember(parent.type, debugVariable.name); if (specialMember) { return new ArraySpecialMember(parent, specialMember, { ...debugVariable, - frameId: parent.frameId, + frameId: frameId, parent: parent, - }, logger) as RealVariable; + context, + logger + }) as RealVariable; + } + } + + /* HTAB* */ + if (utils.getPointersCount(realType) === 1 && + utils.getStructNameFromType(realType) === 'HTAB') { + return new HTABSpecialMember(args); + } + + /* Simple hash table */ + if (SimplehashMember.looksLikeSimpleHashTable(realType)) { + const entry = context.hashTableTypes.findSimpleHashTableType(realType); + if (entry) { + return new SimplehashMember(entry, args); } } /* At the end - it is simple variable */ - return new RealVariable({ - ...debugVariable, - frameId, - parent - }, logger); + return new RealVariable(args); } - static async getVariables(variablesReference: number, frameId: number, context: ExecContext, logger: utils.ILogger, parent?: RealVariable): Promise { + static async getVariables(variablesReference: number, frameId: number, + context: ExecContext, logger: utils.ILogger, + parent?: RealVariable): Promise { const debugVariables = await context.debug.getMembers(variablesReference); if (!debugVariables) { return; } - return (await Promise.all(debugVariables.map(variable => Variable.createVariable(variable, frameId, context, logger, parent)))) - .filter(x => x) as Variable[]; + const variables = await Promise.all(debugVariables.map(variable => + Variable.create(variable, frameId, context, logger, parent)) + ); + return variables.filter(x => x !== undefined); } -} -class ScalarVariable extends Variable { - constructor(name: string, value: string, type: string, parent?: Variable) { - super(name, value, type, parent); - } + static async mapVariables(debugVariables: dap.DebugVariable[], + frameId: number, + context: ExecContext, + logger: utils.ILogger, + parent?: RealVariable): Promise { + const variables = await (Promise.all(debugVariables.map(v => + Variable.create(v, frameId, context, logger, parent)) + )); - async getChildren(context: ExecContext): Promise { - return; + return variables.filter(v => v !== undefined); } -} - -interface RealVariableArgs { - evaluateName: string; - memoryReference: string; - name: string; - type: string; - value: string; - variablesReference: number; - frameId: number; - parent?: Variable; -} - -/** - * Base class for all real variables in variables view. - * There may be artificial variables - they just exist. - */ -export class RealVariable extends Variable { - protected readonly logger: utils.ILogger; - - /** - * Expression to access variable - */ - evaluateName: string; - - /** - * Memory address of variable value - */ - memoryReference: string; /** - * Number to use in requests to work with DAP. - * I.e. get subvariables + * Format expression to be inserted in 'Watch' view to evaluate. + * + * @returns Expression to be evaluated in 'Watch' view */ - variablesReference: number; + getWatchExpression(): string | null { + return null; + } /** - * Id of frame, where we should access this variable + * call `palloc` with specified size (can be expression). + * before, it performs some checks and can throw EvaluationError + * if they fail. */ - frameId: number; + async palloc(size: string) { + /* + * Memory allocation is a very sensitive operation. + */ + if (!await this.isSafeToAllocateMemory()) { + throw new EvaluationError('It is not safe to allocate memory now'); + } - constructor(args: RealVariableArgs, logger: utils.ILogger) { - super(args.name, args.value, args.type, args.parent); - this.logger = logger; - this.evaluateName = args.evaluateName; - this.memoryReference = args.memoryReference; - this.variablesReference = args.variablesReference; - this.frameId = args.frameId; - this.parent = args.parent; - } + if (this.context.hasPalloc) { + const result = await this.evaluate(`palloc(${size})`); - getRealVariableArgs(): RealVariableArgs { + /* + * I will not allocate huge amounts of memory - only small *state* structures, + * and expect, that there is always enough memory to allocate it. + * + * So, only invalid situation - this is old version of PostgreSQL, + * so `palloc` implemented as macro and we need to invoke `MemoryContextAlloc` + * directly. + */ + if (utils.isValidPointer(result.result)) { + return result.result; + } + } + + const result = await this.evaluate(`MemoryContextAlloc(CurrentMemoryContext, ${size})`); + if (utils.isValidPointer(result.result)) { + this.context.hasPalloc = false; + return result.result; + } + + throw new EvaluationError(`failed to allocate memory using MemoryContextAlloc: ${result.result}`); + } + + private async isSafeToAllocateMemory() { + const isValidMemoryContextTag = (tag: string) => { + /* + * Different versions has different algorithms (tags) + * for memory allocations. + * We check all of them, without knowledge of pg version. + * + * In comments you will see version when it was introduced + * (AllocSetContext was here forever). + */ + switch (tag) { + case 'T_AllocSetContext': + case 'T_SlabContext': /* 10 */ + case 'T_GenerationContext': /* 11 */ + case 'T_BumpContext': /* 17 */ + return true; + default: + /* This is T_Invalid or something else */ + return false; + } + } + /* + * Memory allocation is very sensitive operation. + * Allocation occurs in CurrentMemoryContext (directly or by `palloc`). + * + * During this operation we have to perform some checks: + * 1. MemoryContextIsValid() + * 2. AssertNotInCriticalSection() + * + * If we do not perform them by ourselves the whole backend may + * crash, because these checks will fail. + * + * I try to reduce amount of debugger calls, so use single expression. + * It combines both MemoryContextIsValid() and AssertNotInCriticalSection(). + */ + + if (this.context.hasAllowInCritSection) { + const checkExpr = `(CurrentMemoryContext == ((void *)0)) + ? ((NodeTag) T_Invalid) + : (CritSectionCount == 0 || CurrentMemoryContext->allowInCritSection) + ? ((NodeTag) ((Node *)CurrentMemoryContext)->type) + : ((NodeTag) T_Invalid)`; + const tag = await this.evaluate(checkExpr); + + if (isValidMemoryContextTag(tag.result)) { + return true; + } + + /* + * Here we check not 'isFailedVar' because in case of + * unknown member it gives another error, like + * 'There is no member ...'. + * + * So to check not passed really, just check returned + * data is NodeTag, then check not passed, otherwise + * we might have old version -> switch to it. + */ + if (tag.result.startsWith('T_')) { + return false; + } + } + + const checkExpr = `(CurrentMemoryContext == ((void *)0)) + ? ((NodeTag) T_Invalid) + : ((NodeTag) ((Node *)CurrentMemoryContext)->type)`; + + const tag = await this.evaluate(checkExpr); + if (isValidMemoryContextTag(tag.result)) { + this.context.hasAllowInCritSection = false; + return true; + } + + if (tag.result.startsWith('T_')) { + this.context.hasAllowInCritSection = false; + return false; + } + + throw new EvaluationError(`failed to determine MemoryContext validity: ${tag.result}`); + } + + /** + * call `pfree` with specified pointer + */ + async pfree(pointer: string) { + if (!utils.isNull(pointer)) + await this.evaluate(`pfree((void *)${pointer})`); + } + + protected async evaluate(expr: string) { + return await this.debug.evaluate(expr, this.frameId); + } +} + +/* + * Special class to store top level variables, extracted from this frame. + * Used as container for top-level variables. + * + * Now used to find 'PlannerInfo' or 'Query' in all current variables. + */ +export class VariablesRoot extends Variable { + static variableRootName = '$variables root$' + + constructor(public topLevelVariables: Variable[], context: ExecContext, logger: utils.ILogger) { + super(VariablesRoot.variableRootName, '', '', context, invalidFrameId, undefined, logger); + } + + async doGetChildren(): Promise { + return undefined; + } +} + +class ScalarVariable extends Variable { + tooltip?: string; + constructor(name: string, value: string, type: string, context: ExecContext, logger: utils.ILogger, parent?: Variable, tooltip?: string) { + super(name, value, type, context, invalidFrameId, parent, logger); + this.tooltip = tooltip; + } + + async doGetChildren(): Promise { + return; + } + + async getTreeItem() { + const item = await super.getTreeItem(); + item.tooltip = this.tooltip; + return item; + } +} + +interface RealVariableArgs { + evaluateName: string; + memoryReference?: string; + name: string; + type: string; + value: string; + variablesReference: number; + frameId: number; + parent?: Variable; + context: ExecContext; + logger: utils.ILogger; +} + +/** + * Generic class to specify error occurred during debugger + * evaluation or error in logic after that + */ +class EvaluationError extends Error { + /** + * Evaluation error message, not exception message + */ + evalError?: string; + + constructor(message: string, evalError?: string) { + if (evalError) { + super(`${message}: ${evalError}`); + } else { + super(message); + } + this.evalError = evalError; + } +} + +/** + * Specified member was not found in some variable's members + */ +class NoMemberFoundError extends EvaluationError { + constructor(readonly member: string) { + super(`member ${member} does not exists`); + } +} + +/** + * Evaluation produced unexpected results. + */ +class UnexpectedOutputError extends EvaluationError { } + +/** + * Base class for all *real* variables (members or variables + * obtained using 'evaluate' or as members of structs). + */ +export class RealVariable extends Variable { + /** + * Expression to access variable + */ + evaluateName: string; + + /** + * Memory address of variable value + */ + memoryReference?: string; + + /** + * Number to use in requests to work with DAP. + * I.e. get subvariables + */ + variablesReference: number; + + /** + * Cached *real* members of this variable + */ + members?: Variable[]; + + constructor(args: RealVariableArgs) { + super(args.name, args.value, args.type, args.context, args.frameId, args.parent, args.logger); + this.evaluateName = args.evaluateName; + this.memoryReference = args.memoryReference; + this.variablesReference = args.variablesReference; + this.parent = args.parent; + } + + getRealVariableArgs(): RealVariableArgs { return { evaluateName: this.evaluateName, memoryReference: this.memoryReference, @@ -344,31 +1029,274 @@ export class RealVariable extends Variable { variablesReference: this.variablesReference, frameId: this.frameId, parent: this.parent, + context: this.context, + logger: this.logger, } } /** * Check that {@link value value} is valid pointer value */ - protected isValidPointer() { + isValidPointer() { return utils.isValidPointer(this.value); } /** - * Base implementation which just get variables using + * Base implementation which just get variables using * {@link variablesReference variablesReference } field */ - async getChildren(context: ExecContext): Promise { - return Variable.getVariables(this.variablesReference, this.frameId, context, this.logger, this); + async doGetChildren(): Promise { + if (this.members !== undefined) { + return this.members; + } + + this.members = await this.getRealMembers(); + return this.members; + } + + /** + * Function, used to get only members of this variable - without any artificial members. + * This is required in situations, when getting children from the code to + * prevent infinite loops. + * + * NOTE: code is the same as in 'doGetChildren' to prevent future errors, + * if someday i decide to override default implementation of one + * of these functions (work in both sides) + */ + async getRealMembers(): Promise { + if (this.members !== undefined) { + return this.members; + } + + this.members = await this.doGetRealMembers(); + return this.members; + } + + protected async doGetRealMembers() { + return await Variable.getVariables(this.variablesReference, this.frameId, + this.context, this.logger, this); + } + + protected async getArrayMembers(expression: string, length: number) { + const variables = await this.debug.getArrayVariables(expression, + length, this.frameId); + return await Variable.mapVariables(variables, this.frameId, this.context, + this.logger, this); + } + + /** + * Get *real* member of this var `this->member`. + * Prefer this method as more optimized. + * + * @param member member name of this var + * @returns Variable that represent member of this var + * @throws `NoMemberFoundError` if no such member found + * @throws `EvaluationError` if failed to get members of this variable + */ + async getMember(member: string) { + /* + * Use `getRealMember`, not `getChildren` in order to + * prevent infinite loops when getting member + * of one var from another. + */ + const members = await this.getRealMembers(); + if (members === undefined) { + throw new EvaluationError(`failed to get members of "${this.type} ${this.name}"`); + } + + const m = members.find(v => v.name === member); + if (m === undefined) { + throw new NoMemberFoundError(member); + } + + return m; + } + + getRealType() { + return this.type; + } + + async getRealMember(member: string) { + const m = await this.getMember(member); + if (m instanceof RealVariable) { + return m; + } + + throw new EvaluationError(`member "${member}" is not RealVariable`); + } + + /** + * Get elements of member `this->member`. + * You should use this function, because NIL is valid + * List representation, but this extension treats it as + * RealVariable, not ListNodeTagVariable. + * + * @param member member name of this var + * @returns Elements of list array + */ + async getListMemberElements(member: string) { + const m = await this.getMember(member); + if (m instanceof ListNodeVariable) { + const elements = await m.getListElements(); + if (elements === undefined) { + throw new UnexpectedOutputError(`failed to get elements from List member ${member}`); + } + return elements; + } + + /* NIL means 0x0, so List will be RealVariable */ + if (utils.isNull(m.value)) { + return []; + } + + throw new UnexpectedOutputError(`member ${member} is not valid List`); + } + + /** + * Get raw 'value' field of `this->member`. + * + * @param member member name of this var + * @returns 'value' field + */ + async getMemberValue(member: string) { + const m = await this.getMember(member); + return m.value; + } + + /** + * Get string value of `char *` member `this->member`. + * If that was NULL, then `null` returned. + * + * @param member member name of this var + * @returns string value of member + */ + async getMemberValueCharString(member: string) { + const value = await this.getMemberValue(member); + const str = utils.extractStringFromResult(value); + if (str !== null) { + return str; + } + if (utils.isNull(value)) { + return null; + } + + throw new UnexpectedOutputError(`member ${member} output is not valid char string`, value); + } + + /** + * Get value of enum member `this->member`. + * If failed throws UnexpectedOutputError. + * + * NOTE: var does not know, what valid enum values for this type are, + * so it returns anything, that looks like valid enum value. + * + * @param member member name of this var + * @returns Enum value of this member as string + */ + async getMemberValueEnum(member: string) { + const value = await this.getMemberValue(member); + if (!utils.isEnumResult(value)) { + throw new UnexpectedOutputError(`member ${member} output is not enum`, value); + } + return value; + } + + /** + * Get bool value of `this->member`. + * If failed throw UnexpectedOutputError. + * + * @param member member name of this var + * @returns Bool value of member + */ + async getMemberValueBool(member: string) { + const value = await this.getMemberValue(member); + const result = utils.extractBoolFromValue(value); + if (result === null) { + throw new UnexpectedOutputError(`member ${member} output is not bool`, value); + } + return result; + } + + /** + * Get number value of `this->member`. + * If failed throws UnexpectedOutputError. + * + * @param member member name of this var + * @returns Number value of this member + */ + async getMemberValueNumber(member: string) { + const value = await this.getMemberValue(member); + const num = Number(value); + if (Number.isNaN(num)) { + throw new UnexpectedOutputError(`member ${member} output is not number`, value); + } + return num; + } + + protected formatWatchExpression(myType: string) { + if (this.parent instanceof VariablesRoot) { + /* Top level variable */ + if (utils.isRawStruct(myType, this.value)) { + /* No way to evaluate raw structs as they just lie on stack */ + return this.name; + } else if (utils.isValidPointer(this.value)) { + return `(${myType})${this.value}`; + } + } + else if (this.parent instanceof ListElementsMember || + this.parent instanceof LinkedListElementsMember) { + /* Pointer element of List, not int/Oid/TransactionId... */ + if (utils.isValidPointer(this.value)) { + return `(${myType})${this.value}`; + } + } else if (this.parent instanceof ArraySpecialMember) { + if (utils.isValidPointer(this.value)) { + return `(${myType})${this.value}` + } + } else if (this.parent instanceof RealVariable) { + /* Member of real structure */ + const typeModifier = this.type === myType ? '' : `(${myType})`; + if (utils.isRawStruct(this.parent.type, this.parent.value)) { + if (utils.isFixedSizeArray(this.parent) && utils.isValidPointer(this.value)) { + return `(${myType})${this.value}`; + } else { + return `${typeModifier}${this.parent.evaluateName}.${this.name}`; + } + } else if (utils.isValidPointer(this.parent.value)) { + return `${typeModifier}((${this.parent.getRealType()})${this.parent.value})->${this.name}`; + } + } else { + /* Child of pseudo-member */ + if (utils.isRawStruct(myType, this.value)) { + return this.evaluateName; + } else if (utils.isValidPointer(this.value)) { + return `(${myType})${this.value}` + } + } + + return null; + } + + getWatchExpression() { + return this.formatWatchExpression(this.type); } } +/* + * Some constants from source code. + * Using them in such way is quite safe, because they haven't + * changed for many years (and I do not think will be changed + * in near future). + */ +const InvalidOid = 0; +const InvalidAttrNumber = 0; + /** * Variable/member with `NodeTag' assigned. - * We should examine it to get real NodeTag because it + * We should examine it to get real NodeTag because it * may be different from declared type. */ -export class NodeTagVariable extends RealVariable { +export class NodeVariable extends RealVariable { /** * Real tag of node without 'T_' prefix. * @example AggPath @@ -377,17 +1305,17 @@ export class NodeTagVariable extends RealVariable { /** * Real type of Node variable. May be equal to declared type if NodeTags - * are equal. - * - * Evaluated lazily - use {@link getRealType getRealType()} function to + * are equal. + * + * Evaluated lazily - use {@link getRealType getRealType()} function to * get value - * + * * @example `OpExpr *' was `Node *' */ - realType: string | undefined; + realType?: string; - constructor(realNodeTag: string, args: RealVariableArgs, logger: utils.ILogger) { - super(args, logger); + constructor(realNodeTag: string, args: RealVariableArgs) { + super(args); this.realNodeTag = realNodeTag.replace('T_', ''); } @@ -397,7 +1325,16 @@ export class NodeTagVariable extends RealVariable { return this.type; } - return utils.substituteStructName(this.type, this.realNodeTag); + /* + * Also try find aliases for some NodeTags + */ + let type = this.type; + const alias = this.context.nodeVarRegistry.aliases.get(tagFromType); + if (alias) { + type = utils.substituteStructName(type, alias); + } + + return utils.substituteStructName(type, this.realNodeTag); } getRealType(): string { @@ -419,41 +1356,113 @@ export class NodeTagVariable extends RealVariable { return this.isValidPointer(); } - getTreeItem(): vscode.TreeItem { - return { - label: this.tagsMatch() - ? `${this.name}: ${this.type} = ` - : `${this.name}: ${this.type} [${this.realNodeTag}] = `, - description: this.value, - collapsibleState: this.isExpandable() - ? vscode.TreeItemCollapsibleState.Collapsed - : vscode.TreeItemCollapsibleState.None, - }; + async getTreeItem() { + try { + return { + label: this.tagsMatch() + ? `${this.name}: ${this.type}` + : `${this.name}: ${this.type} [${this.realNodeTag}]`, + description: await this.getDescription(), + collapsibleState: this.isExpandable() + ? vscode.TreeItemCollapsibleState.Collapsed + : vscode.TreeItemCollapsibleState.None, + }; + } catch (e) { + this.logger.debug('failed to get TreeItem for %s', this.name, e); + if (isExpectedError(e)) { + return {}; + } else { + throw e; + } + } } - async castToRealTag(debug: utils.IDebuggerFacade) { - /* - * We should substitute current type with target, because + protected async checkTagMatch() { + if (!this.tagsMatch()) { + await this.castToTag(this.realNodeTag); + } + } + + protected async castToType(type: string) { + const newVarExpression = `((${type})${this.evaluateName})`; + const response = await this.debug.evaluate(newVarExpression, this.frameId); + if (utils.isFailedVar(response)) { + /* Error - do not apply cast */ + this.logger.debug('failed to cast type "%s" to tag "%s": %s', + this.type, type, response.result); + return response; + } + + this.variablesReference = response.variablesReference; + + /* + * No need to update 'type' member - type in variables view + * already present and we rely on 'realNodeTag' member + */ + return response; + } + + protected async castToTag(tag: string) { + /* + * We should substitute current type with target, because * there may be qualifiers such `struct' or `const' */ - const resultType = utils.substituteStructName(this.type, this.realNodeTag); - const newVarExpression = `((${resultType})${this.evaluateName})`; - const response = await debug.evaluate(newVarExpression, this.frameId); - this.variablesReference = response.variablesReference; + const resultType = utils.substituteStructName(this.getRealType(), tag); + return await this.castToType(resultType); } - async getChildren(context: ExecContext): Promise { - if (!this.tagsMatch()) { - await this.castToRealTag(context.debug); + async doGetChildren() { + await this.checkTagMatch(); + + let members = await super.doGetChildren(); + + if (members?.length) { + return members; + } + + /* + * If declared type has `struct' qualifier, we + * can fail cast, because of invalid type specifier. + * i.e. declared - `struct Path*' and real node tag + * is `T_NestPath'. This will create `struct NestPath*', + * but in versions prior to 14 NestPath is typedef + * for another struct, so there is no struct NestPath. + */ + if (this.type.indexOf('struct') !== -1) { + const structLessType = this.type.replace('struct', ''); + await this.castToType(structLessType); + members = await this.getRealMembers(); + } + return members; + } + + protected async doGetRealMembers() { + await this.checkTagMatch(); + + let members = await super.doGetRealMembers(); + + if (members?.length) { + return members; } - const debugVariables = await context.debug.getMembers(this.variablesReference); - return (await Promise.all(debugVariables.map(dv => Variable.createVariable(dv, this.frameId, context, this.logger, this as RealVariable)))) - .filter(d => d !== undefined); + /* + * If declared type has `struct' qualifier, we + * can fail cast, because of invalid type specifier. + * i.e. declared - `struct Path*' and real node tag + * is `T_NestPath'. This will create `struct NestPath*', + * but in versions prior to 14 NestPath is typedef + * for another struct, so there is no struct NestPath. + */ + if (this.type.indexOf('struct') !== -1) { + const structLessType = this.type.replace('struct', ''); + await this.castToType(structLessType); + members = await super.doGetRealMembers(); + } + return members; } static isValidNodeTag(tag: string) { - /* + /* * Valid NodeTag must contain only alphabetical characters. * Note: it does not contain 'T_' prefix - we strip it always. */ @@ -464,28 +1473,34 @@ export class NodeTagVariable extends RealVariable { return utils.getStructNameFromType(type); } - static async getRealNodeTag(variable: dap.DebugVariable, frameId: number, context: ExecContext) { - const response = await context.debug.evaluate(`((Node*)(${variable.evaluateName}))->type`, frameId); - let realTag = response.result?.replace('T_', ''); - if (!this.isValidNodeTag(realTag)) { - return; + static async create(variable: dap.DebugVariable, frameId: number, + context: ExecContext, logger: utils.ILogger, + parent?: Variable): Promise { + const getRealNodeTag = async () => { + const nodeTagExpression = `((Node*)(${variable.value}))->type`; + const response = await context.debug.evaluate(nodeTagExpression, frameId); + let realTag = response.result?.replace('T_', ''); + if (!this.isValidNodeTag(realTag)) { + return; + } + return realTag; } - return realTag; - } - static async createNodeTagVariable(variable: dap.DebugVariable, frameId: number, context: ExecContext, logger: utils.ILogger, parent?: Variable) { if (!context.nodeVarRegistry.isNodeVar(variable.type)) { return; } - let realTag = await this.getRealNodeTag(variable, frameId, context); + let realTag = await getRealNodeTag(); if (!realTag) { return; } + const args: RealVariableArgs = { ...variable, frameId, parent, + context, + logger, }; realTag = realTag.replace('T_', ''); @@ -498,276 +1513,2720 @@ export class NodeTagVariable extends RealVariable { case 'OidList': case 'XidList': case 'IntList': - return new ListNodeTagVariable(realTag, args, logger); + return new ListNodeVariable(realTag, args); } } /* Bitmapset */ - if (BitmapSetSpecialMember.isBmsType(variable.type)) { - return new BitmapSetSpecialMember(logger, { - ...variable, - frameId, - parent - }); + if (realTag === 'Bitmapset') { + return new BitmapSetSpecialMember(args); } - return new NodeTagVariable(realTag, { - ...variable, - frameId, - parent, - }, logger); + /* Expressions with it's representation */ + if (context.nodeVarRegistry.exprs.has(realTag)) { + if (realTag === 'TargetEntry') { + return new TargetEntryVariable(args); + } + + return new ExprNodeVariable(realTag, args); + } + + /* Display expressions in EquivalenceMember and RestrictInfo */ + if (realTag === 'EquivalenceMember') { + return new DisplayExprReprVariable(realTag, 'em_expr', args); + } + + if (realTag === 'RestrictInfo') { + return new DisplayExprReprVariable(realTag, 'clause', args); + } + + /* Check this is a tag of 'Value' */ + if (realTag === 'String' || + realTag === 'Integer' || + realTag === 'Float' || + realTag === 'Boolean' || + realTag === 'BitString') { + return new ValueVariable(realTag, args); + } + + return new NodeVariable(realTag, args); + } + + getWatchExpression() { + return this.formatWatchExpression(this.computeRealType()); + } +} + +/** + * Used only inside ExprNodeVariable in order not to pass huge type specification. + * Created as container to postpone 'rtable' evaluation. + */ +class RangeTableContainer { + /** + * Flag indicating, that search of rtable already occurred. + * 'rtable' can be undefined because we could not find it. + */ + rtableSearched: boolean = false; + + /** + * Found 'rtable' amoung variables. Before updating/using + * this field check `rtableSearched` if this member has + * actual value. + */ + rtable: NodeVariable[] | undefined; +} + +/** + * Subtypes of Expr node, that can be displayed with text representation of it's expression + */ +class ExprNodeVariable extends NodeVariable { + /** + * String representation of expression. + */ + protected repr?: string; + + /** + * Evaluate expression and parse string from result. + * If result is not correct string result output, then null returned. + */ + private async evalStringResult(expr: string) { + const result = await this.evaluate(expr); + return utils.extractStringFromResult(result.result); + } + + /** + * Run `get_func_name(this->oidMember)` and get output as string. + */ + private async getFuncName(oidMember: string) { + /* First check oid is valid, otherwise ERROR is thrown */ + const oid = await this.getMemberValueNumber(oidMember); + if (oid === InvalidOid) { + return null; + } + + const result = await this.evaluate(`get_func_name((Oid) ${oid})`); + if (utils.isFailedVar(result)) { + return null; + } + + const str = utils.extractStringFromResult(result.result); + if (str === null) { + return null; + } + + const ptr = utils.extractPtrFromStringResult(result.result); + if (ptr) { + await this.pfree(ptr); + } + return str; + } + + /** + * Run `get_opname(this->oidMember)` and get output as string. + */ + private async getOpName(oidMember: string) { + const oid = await this.getMemberValueNumber(oidMember); + if (oid === InvalidOid) { + return null; + } + const result = await this.evaluate(`get_opname((Oid)${oid})`); + if (utils.isFailedVar(result)) { + return null; + } + + const str = utils.extractStringFromResult(result.result); + if (str === null) { + return null; + } + + const ptr = utils.extractPtrFromStringResult(result.result); + if (ptr) { + await this.pfree(ptr); + } + + return str; + } + + /** + * Get elements of member 'this->member' and return list + * of repr for each element + */ + private async getListMemberElementsReprs(member: string, rtable: RangeTableContainer) { + const elements = await this.getListMemberElements(member); + + const reprs = []; + for (const elem of elements) { + reprs.push(await this.getReprPlaceholder(elem, rtable)); + } + + return reprs; + } + + /** + * Get repr of 'this->member' + */ + private async getMemberRepr(member: string, rtable: RangeTableContainer) { + const exprMember = await this.getMember(member); + return await this.getReprPlaceholder(exprMember, rtable); + } + + /** + * These are used as placeholders for repr, when we had + * error during evaluation. This is done to give more + * context, so developer can understand what the expression is. + */ + private static exprPlaceholders = new Map([ + ['Aggref', 'AGGREF'], + ['AlternativeSubPlan', 'ALT_SUBPLAN'], + ['ArrayCoerceExpr', 'ARRAY_COERCE'], + ['ArrayExpr', 'ARRAY[]'], + ['ArrayRef', 'ARRAY_REF'], + ['BoolExpr', 'BOOL_EXPR'], + ['BooleanTest', 'BOOL_TEST'], + ['CaseExpr', 'CASE'], + ['CaseTestExpr', 'CASE_TEST'], + ['CaseWhen', 'CASE_WHEN'], + ['CoalesceExpr', 'COALESCE'], + ['CoerceToDomain', 'COERCE_DOMAIN'], + ['CoerceToDomainValue', 'COERCE_DOMAIN_VAL'], + ['CoerceViaIO', 'COERCE_IO'], + ['CollateExpr', 'COLLATE'], + ['Const', 'CONST'], + ['ConvertRowtypeExpr', 'CONVERT_ROWTYPE'], + ['CurrentOfExpr', 'CURRENT_OF'], + ['DistinctExpr', 'DISTINCT'], + ['FieldSelect', 'FIELD_SELECT'], + ['FieldStore', 'FIELD_STORE'], + ['FuncExpr', 'FUNC()'], + ['GroupingFunc', 'GROUPING'], + ['InferenceElem', 'INFER_ELEM'], + ['JsonConstructorExpr', 'JSON_CTOR'], + ['JsonExpr', 'JSON'], + ['JsonValueExpr', 'JSON_VALUE'], + ['MergeSupportFunc', 'MERGE_SUPPORT'], + ['MinMaxExpr', 'MIN_MAX'], + ['NamedArgExpr', 'NAMED_ARG'], + ['NextValueExpr', 'NEXTVAL'], + ['NullIfExpr', 'NULL_IF'], + ['NullTest', 'NULL_TEST'], + ['OpExpr', 'OP_EXPR'], + ['Param', 'PARAM'], + ['RelabelType', 'RELABEL_TYPE'], + ['RowCompareExpr', 'ROW_COMPARE'], + ['RowExpr', 'ROW()'], + ['SQLValueFunctionOp', 'SQL_VAL_FUNC()'], + ['ScalarArrayOpExpr', 'SCALAR_ARRAY_OP'], + ['SetToDefault', 'SET_DEFAULT'], + ['SubLink', 'SUB_LINK'], + ['SubPlan', 'SUB_PLAN'], + ['SubscriptingRef', 'SUBSCRIPT'], + ['Var', 'VAR'], + ['WindowFunc', 'WINDOW'], + ['WindowFuncRunCondition', 'WINDOW_F_RUN_COND'], + ['XmlExpr', 'XML'], + ['XmlExprOp', 'XML_OP'], + ]); + + /* + * Get placeholder in expression tree for given variable + */ + private getExprPlaceholder(variable: Variable) { + /* + * When some variable appears in Expr, but we + * do not have logic to format representation this + * function is called to fullfil this with some + * meaningful word/placeholder. + * + * Ordinarily, there will be other Exprs, for + * which we do not have implementation + */ + + if (!(variable instanceof NodeVariable)) { + return 'EXPR'; + } + + return ExprNodeVariable.exprPlaceholders.get(variable.realNodeTag) ?? 'EXPR'; + } + + /** + * Auxiliary function to get repr of Variable with + * max details if failed. This is + */ + private async getReprPlaceholder(variable: Variable, rtable: RangeTableContainer) { + if (variable instanceof ExprNodeVariable) { + return await variable.getReprInternal(rtable); + } else { + return this.getExprPlaceholder(variable); + } + } + + private async formatVarExpr(rtable: RangeTableContainer) { + const varno = await this.getMemberValueNumber('varno'); + + if (varno === -1 || varno === 65000) { + return 'INNER.???'; + } + + if (varno === -2 || varno === 65001) { + return 'OUTER.???'; + } + + if (varno === -3 || varno === 65002) { + return 'INDEX.???'; + } + + if (!rtable.rtableSearched) { + if (!rtable.rtable) { + rtable.rtable = await this.findRtable() as NodeVariable[] | undefined; + rtable.rtableSearched = true; + } + } + + if (!rtable.rtable) { + return '???.???'; + } + + if (!(varno > 0 && varno <= rtable.rtable.length)) { + /* This was an Assert */ + throw new EvaluationError('failed to get RTEs from range table'); + } + + /* + * We can safely get `relname` (eref->aliasname), but that's + * not true for `attname`. + * + * We can use `get_rte_attribute_name` function, but + * main drawback is that it throws ERROR if failed to find + * one. + * You may think that this is valid, but not during development + * when you are creating a patch and modifying Query/Subquery + * such, that they can interleave each other. It can lead + * to `get_rte_attribute_name` throwing an ERROR. + * + * Fortunately, this function is simple enough and here + * we just copy it's logic. + */ + + const rte = rtable.rtable[varno - 1]; + + const get_rte_attribute_name = async () => { + /* Copy of `get_rte_attribute_name` logic */ + + const varattno = await this.getMemberValueNumber('varattno'); + if (varattno === InvalidAttrNumber) { + return '*'; + } + + if (varattno < InvalidAttrNumber) { + return '???'; + } + + const alias = await rte.getRealMember('alias'); + if (alias.isValidPointer()) { + const aliasColnames = await alias.getListMemberElements('colnames'); + + if (varattno <= aliasColnames.length) { + const colname = aliasColnames[varattno - 1]; + if (colname instanceof ValueVariable) { + return await colname.getStringValue() ?? '???'; + } + } + } + + const rtePtr = `((RangeTblEntry *)${this.value})`; + + if (this.context.hasGetAttname) { + const getAttnameExpr = `${rtePtr}->rtekind == RTE_RELATION && ${rtePtr}->relid != ${InvalidOid}`; + const useGetAttname = utils.extractBoolFromValue((await this.evaluate(getAttnameExpr)).result); + if (useGetAttname) { + /* Call this with `true` last - do not throw error if no such attribute found */ + const r = await this.evaluate(`get_attname(${rtePtr}->relid, ${varattno}, true)`); + const attname = utils.extractStringFromResult(r.result); + if (attname !== null) { + return attname; + } + + if (utils.isFailedVar(r)) { + this.context.hasGetAttname = false; + } + } + } + + const eref = await rte.getRealMember('eref'); + if (eref.isValidPointer()) { + const erefColnames = await eref.getListMemberElements('colnames'); + if (varattno <= erefColnames.length) { + const colname = erefColnames[varattno - 1]; + if (colname instanceof ValueVariable) { + return await colname.getStringValue() ?? '???'; + } + } + } + + return '???'; + } + + /* 'rte.value' will be pointer to RTE struct */ + const relname = await this.evalStringResult(`((RangeTblEntry *)${rte.value})->eref->aliasname`) ?? '???'; + const attname = await get_rte_attribute_name(); + + return `${relname}.${attname}`; + } + + private async formatConst(rtable: RangeTableContainer) { + const evalOid = async (expr: string) => { + const res = await this.evaluate(expr); + const oid = Number(res.result); + if (Number.isNaN(oid)) { + throw new EvaluationError(`failed to get Oid from expr: ${expr}`, res.result); + } + + return oid; + } + + const evalStrWithPtr = async (expr: string) => { + const result = await this.debug.evaluate(expr, this.frameId); + const str = utils.extractStringFromResult(result.result); + if (str === null) { + throw new EvaluationError(`failed to get string from expr: ${expr}`, result.result); + } + + const ptr = utils.extractPtrFromStringResult(result.result); + if (ptr === null) { + throw new EvaluationError(`failed to get pointer from expr: ${expr}`, result.result); + } + return [str, ptr]; + } + + const legacyOidOutputFunctionCall = async (funcOid: number) => { + /* + * Older systems do not have OidOutputFunctionCall(). + * But, luckily, it's very simple to write it by our selves. + */ + + const fmgrInfo = await this.palloc('sizeof(FmgrInfo)'); + /* Init FmgrInfo */ + await this.evaluate(`fmgr_info(${funcOid}, (void *)${fmgrInfo})`); + + /* Call function */ + const [str, ptr] = await evalStrWithPtr(`(char *)((Pointer) FunctionCall1(((void *)${fmgrInfo}), ((Const *)${this.value})->constvalue))`); + await this.pfree(ptr); + return str; + } + + if (await this.getMemberValueBool('constisnull')) { + return 'NULL'; + } + + const tupoutput = await this.palloc('sizeof(Oid)'); + const tupIsVarlena = await this.palloc('sizeof(Oid)'); + + /* + * Older system have 4 param - tupOIParam. + * We pass it also even on modern systems - anyway only thing + * we want is 'tupoutput'. + * Hope, debugger will not invalidate the stack after that... + */ + const tupIOParam = await this.palloc('sizeof(Oid)'); + + /* + * WARN: I do not why, but you MUST cast pointers as 'void *', + * not 'Oid *' or '_Bool *'. + * Otherwise, passed pointers will have some offset + * (*orig_value* + offset), so written values will + * be stored in random place. + */ + await this.evaluate(`getTypeOutputInfo(((Const *)${this.value})->consttype, ((void *)${tupoutput}), ((void *)${tupIOParam}), ((void *)${tupIsVarlena}))`); + + const funcOid = await evalOid(`*((Oid *)${tupoutput})`); + if (funcOid === InvalidOid) { + /* Invalid function */ + return '???'; + } + + let repr; + try { + const [str, ptr] = await evalStrWithPtr(`OidOutputFunctionCall(${funcOid}, ((Const *)${this.value})->constvalue)`); + await this.pfree(ptr); + repr = str; + } catch (e) { + if (!(e instanceof EvaluationError)) { + throw e; + } + + repr = await legacyOidOutputFunctionCall(funcOid); + } + + await this.pfree(tupoutput); + await this.pfree(tupIsVarlena); + await this.pfree(tupIOParam); + + return repr; + } + + private async formatOpExpr(rtable: RangeTableContainer) { + const opname = await this.getOpName('opno') ?? '(invalid op)'; + const args = await this.getListMemberElements('args'); + if (args.length === 0) { + throw new UnexpectedOutputError('OpExpr contains no args'); + } + + let data; + if (args.length > 1) { + data = [ + await this.getReprPlaceholder(args[0], rtable), + opname, + await this.getReprPlaceholder(args[1], rtable), + ] + } else { + data = [ + opname, + await this.getReprPlaceholder(args[0], rtable) + ] + } + + return data.join(' '); + } + + private async formatFuncExpr(rtable: RangeTableContainer) { + const funcname = await this.getFuncName('funcid') ?? '(invalid func)'; + + const args = await this.getListMemberElements('args'); + + const coerceType = await this.getMemberValueEnum('funcformat'); + + switch (coerceType) { + case 'COERCE_EXPLICIT_CALL': + case 'COERCE_SQL_SYNTAX': + case 'COERCE_DONTCARE': + /* + * It's hard to represent COERCE_SQL_SYNTAX, because there are + * multiple SQL features with different features (like + * EXTRACT(x FROM y)) and most of them depend on Oid's of + * types. + * Example you can see in src/backend/utils/adt/ruleutils.c. + * So i decided to simplify it to level of just function call + */ + const argsExpressions: string[] = []; + for (const arg of args) { + argsExpressions.push(await this.getReprPlaceholder(arg, rtable)); + } + + return `${funcname}(${argsExpressions.join(', ')})`; + case 'COERCE_EXPLICIT_CAST': + const argRepr = await this.getReprPlaceholder(args[0], rtable); + return `${argRepr}::${funcname}`; + case 'COERCE_IMPLICIT_CAST': + /* User did not request explicit cast, so show as simple expr */ + return await this.getReprPlaceholder(args[0], rtable); + } + return '???'; + } + + private async formatAggref(rtable: RangeTableContainer) { + const funcname = await this.getFuncName('aggfnoid') ?? '(invalid func)'; + + const reprs = await this.getListMemberElementsReprs('args', rtable); + + let args; + if (reprs.length === 0) { + /* If agg function called with '*', then 'args' is NIL */ + args = '*'; + } else { + args = reprs.join(', '); + } + + + return `${funcname}(${args})`; + } + + private async formatTargetEntry(rtable: RangeTableContainer) { + /* NOTE: keep return type annotation, because now compiler can not + * handle such recursion correctly + */ + const expr = await this.getMember('expr'); + return await this.getReprPlaceholder(expr, rtable); + } + + private async formatScalarArrayOpExpr(rtable: RangeTableContainer) { + const opname = await this.getOpName('opno') ?? '(invalid op)'; + + const useOr = await this.getMemberValueBool('useOr'); + const args = await this.getListMemberElements('args'); + if (args.length !== 2) { + throw new EvaluationError(`ScalarArrayOpExpr should contain 2 arguments, given: ${args.length}`); + } + + const [scalar, array] = args; + const scalarRepr = await this.getReprPlaceholder(scalar, rtable); + const arrayRepr = await this.getReprPlaceholder(array, rtable); + const funcname = useOr ? 'ANY' : 'ALL'; + + return `${scalarRepr} ${opname} ${funcname}(${arrayRepr})`; + } + + private async formatBoolExpr(rtable: RangeTableContainer) { + const boolOp = await this.getMemberValueEnum('boolop') + const args = await this.getListMemberElements('args'); + + if (boolOp === 'NOT_EXPR') { + const exprRepr = await this.getReprPlaceholder(args[0], rtable); + return `NOT ${exprRepr}`; + } + + const argsReprs = []; + for (const arg of args) { + argsReprs.push(await this.getReprPlaceholder(arg, rtable)); + } + + let joinExpr; + switch (boolOp) { + case 'AND_EXPR': + joinExpr = ' AND '; + break; + case 'OR_EXPR': + joinExpr = ' OR '; + break; + default: + joinExpr = ' ??? '; + break; + } + + return argsReprs.join(joinExpr); + } + + private async formatCoalesceExpr(rtable: RangeTableContainer) { + const args = await this.getListMemberElements('args'); + const argsReprs = []; + for (const arg of args) { + argsReprs.push(await this.getReprPlaceholder(arg, rtable)); + } + + return `COALESCE(${argsReprs.join(', ')})`; + } + + private async formatNullTest(rtable: RangeTableContainer) { + const expr = await this.getMember('arg'); + const innerRepr = await this.getReprPlaceholder(expr, rtable); + + const testType = await this.getMemberValueEnum('nulltesttype'); + let testSql; + switch (testType) { + case 'IS_NULL': + testSql = 'IS NULL'; + break; + case 'IS_NOT_NULL': + testSql = 'IS NOT NULL'; + break; + default: + testSql = '???'; + break; + } + return `${innerRepr} ${testSql}`; + } + + private async formatBooleanTest(rtable: RangeTableContainer) { + const arg = await this.getMember('arg'); + const innerRepr = await this.getReprPlaceholder(arg, rtable); + + const testType = await this.getMemberValueEnum('booltesttype'); + let test; + switch (testType) { + case 'IS_TRUE': + test = 'IS TRUE'; + break; + case 'IS_NOT_TRUE': + test = 'IS NOT TRUE'; + break; + case 'IS_FALSE': + test = 'IS FALSE'; + break; + case 'IS_NOT_FALSE': + test = 'IS NOT FALSE'; + break; + case 'IS_UNKNOWN': + test = 'IS NULL'; + break; + case 'IS_NOT_UNKNOWN': + test = 'IS NOT NULL'; + break; + default: + test = 'IS ???'; + break; + } + + return `${innerRepr} ${test}`; + } + + private async formatArrayExpr(rtable: RangeTableContainer) { + const reprs = await this.getListMemberElementsReprs('elements', rtable); + return `ARRAY[${reprs.join(', ')}]`; + } + + private async formatSqlValueFunction(rtable: RangeTableContainer) { + const getTypmod = async () => { + return await this.getMemberValueNumber('typmod'); + } + const funcOp = await this.getMemberValueEnum('op'); + let funcname; + switch (funcOp) { + case 'SVFOP_CURRENT_DATE': + funcname = 'CURRENT_DATE'; + break; + case 'SVFOP_CURRENT_TIME': + funcname = 'CURRENT_TIME'; + break; + case 'SVFOP_CURRENT_TIME_N': + funcname = `CURRENT_TIME(${await getTypmod()})`; + break; + case 'SVFOP_CURRENT_TIMESTAMP': + funcname = 'CURRENT_TIMESTAMP'; + break; + case 'SVFOP_CURRENT_TIMESTAMP_N': + funcname = `CURRENT_TIMESTAMP(${await getTypmod()})`; + break; + case 'SVFOP_LOCALTIME': + funcname = 'LOCALTIME'; + break; + case 'SVFOP_LOCALTIME_N': + funcname = `LOCALTIME(${await getTypmod()})`; + break; + case 'SVFOP_LOCALTIMESTAMP': + funcname = 'LOCALTIMESTAMP'; + break; + case 'SVFOP_LOCALTIMESTAMP_N': + funcname = `LOCALTIMESTAMP(${await getTypmod()})`; + break; + case 'SVFOP_CURRENT_ROLE': + funcname = 'CURRENT_ROLE'; + break; + case 'SVFOP_CURRENT_USER': + funcname = 'CURRENT_USER'; + break; + case 'SVFOP_USER': + funcname = 'USER'; + break; + case 'SVFOP_SESSION_USER': + funcname = 'SESSION_USER'; + break; + case 'SVFOP_CURRENT_CATALOG': + funcname = 'CURRENT_CATALOG'; + break; + case 'SVFOP_CURRENT_SCHEMA': + funcname = 'CURRENT_SCHEMA'; + break; + default: + funcname = '???'; + break; + } + + return funcname; + } + + private async formatMinMaxExpr(rtable: RangeTableContainer) { + const op = await this.getMemberValueEnum('op'); + const argsReprs = await this.getListMemberElementsReprs('args', rtable); + + let funcname; + switch (op) { + case 'IS_GREATEST': + funcname = 'GREATEST'; + break; + case 'IS_LEAST': + funcname = 'LEAST'; + break; + default: + funcname = '???'; + break; + } + + return `${funcname}(${argsReprs.join(', ')})`; + } + + private async formatRowExpr(rtable: RangeTableContainer) { + const reprs = await this.getListMemberElementsReprs('args', rtable); + return `ROW(${reprs.join(', ')})`; + } + + private async formatDistinctExpr(rtable: RangeTableContainer) { + const reprs = await this.getListMemberElementsReprs('args', rtable); + if (reprs.length != 2) { + throw new EvaluationError('should be 2 arguments for DistinctExpr'); + } + + const [left, right] = reprs; + return `${left} IS DISTINCT FROM ${right}`; + } + + private async formatNullIfExpr(rtable: RangeTableContainer) { + const reprs = await this.getListMemberElementsReprs('args', rtable); + if (reprs.length != 2) { + throw new EvaluationError('should be 2 arguments for NullIf'); + } + + const [left, right] = reprs; + return `NULLIF(${left}, ${right})`; + } + + private async formatNamedArgExpr(rtable: RangeTableContainer) { + const arg = await this.getMemberRepr('arg', rtable); + const name = await this.getMemberValueCharString('name'); + return `${name} => ${arg}`; + } + + private async formatGroupingFunc(rtable: RangeTableContainer) { + const reprs = await this.getListMemberElementsReprs('args', rtable); + return `GROUPING(${reprs.join(', ')})`; + } + + private async formatWindowFunc(rtable: RangeTableContainer) { + const funcname = await this.getFuncName('winfnoid') ?? '(invalid func)'; + const reprs = await this.getListMemberElementsReprs('args', rtable); + let repr = `${funcname}(${reprs.join(', ')})` + try { + const filterRepr = await this.getMemberRepr('aggfilter', rtable); + repr += ` FILTER (${filterRepr})`; + } catch (e) { + if (!(e instanceof EvaluationError)) { + throw e; + } + } + + return repr; + } + + private async formatSubscriptingRef(rtable: RangeTableContainer) { + const exprRepr = await this.getMemberRepr('refexpr', rtable); + const upperIndices = await this.getListMemberElements('refupperindexpr'); + let lowerIndices = null; + try { + lowerIndices = await this.getListMemberElements('reflowerindexpr'); + } catch (e) { + if (!(e instanceof EvaluationError)) { + throw e; + } + } + + const indicesReprs = []; + if (lowerIndices !== null) { + for (let i = 0; i < upperIndices.length; i++) { + const upper = upperIndices[i]; + const lower = lowerIndices[i]; + let index = '['; + if (!utils.isNull(lower.value)) { + index += await this.getReprPlaceholder(lower, rtable); + } + index += ':'; + if (!utils.isNull(upper.value)) { + index += await this.getReprPlaceholder(upper, rtable); + } + index += ']'; + indicesReprs.push(index); + } + } else { + for (let i = 0; i < upperIndices.length; i++) { + const upper = upperIndices[i]; + const index = await this.getReprPlaceholder(upper, rtable); + indicesReprs.push(`[${index}]`); + } + } + + return `(${exprRepr}${indicesReprs.join('')})`; + } + + private async formatXmlExpr(rtable: RangeTableContainer) { + const getArgNameListOfStrings = async () => { + /* Get List of T_String elements and take their 'sval' values */ + const list = await this.getListMemberElements('arg_names'); + const values = []; + for (const entry of list) { + if (entry instanceof ValueVariable) { + try { + values.push(await entry.getStringValue() ?? 'NULL'); + } catch (e) { + if (e instanceof EvaluationError) { + this.logger.debug('error during getting string value from ValueVariable', e); + values.push('???'); + } else { + throw e; + } + } + } else if (entry instanceof ExprNodeVariable) { + values.push(await entry.getReprInternal(rtable)); + } else { + values.push('???'); + } + } + + return values; + } + + const xmlOp = await this.getMemberValueEnum('op'); + switch (xmlOp) { + case 'IS_XMLELEMENT': + { + let namedArgs: string[] | null; + let argNames: string[] | null; + try { + namedArgs = await this.getListMemberElementsReprs('named_args', rtable); + argNames = await getArgNameListOfStrings(); + } catch (e) { + if (e instanceof EvaluationError) { + namedArgs = null; + argNames = null; + } else { + throw e; + } + } + let args: string[] | null; + try { + args = await this.getListMemberElementsReprs('args', rtable); + } catch (e) { + if (e instanceof EvaluationError) { + args = null; + } else { + throw e; + } + } + const name = await this.getMemberValueCharString('name'); + let repr = `XMLELEMENT(name ${name ?? 'NULL'}`; + if (namedArgs && argNames && namedArgs.length === argNames.length) { + let xmlattributes = []; + for (let i = 0; i < namedArgs.length; i++) { + const arg = namedArgs[i]; + const name = argNames[i]; + xmlattributes.push(`${arg} AS ${name}`); + } + repr += `, XMLATTRIBUTES(${xmlattributes.join(', ')})`; + } + + if (args) { + repr += `, ${args.join(', ')}`; + } + repr += ')'; + return repr; + } + case 'IS_XMLFOREST': + { + let namedArgs: string[] | null; + let argNames: string[] | null; + try { + namedArgs = await this.getListMemberElementsReprs('named_args', rtable); + argNames = await getArgNameListOfStrings(); + } catch (e) { + if (e instanceof EvaluationError) { + namedArgs = null; + argNames = null; + } else { + throw e; + } + } + let repr = 'XMLFOREST('; + if (namedArgs && argNames && namedArgs.length === argNames.length) { + let xmlattributes = []; + for (let i = 0; i < namedArgs.length; i++) { + const arg = namedArgs[i]; + const name = argNames[i]; + xmlattributes.push(`${arg} AS ${name}`); + } + repr += `${xmlattributes.join(', ')}`; + } + repr += ')'; + return repr; + } + case 'IS_XMLCONCAT': + { + let args: string[] | null; + try { + args = await this.getListMemberElementsReprs('args', rtable); + } catch (e) { + if (e instanceof EvaluationError) { + args = null; + } else { + throw e; + } + } + + let repr = 'XMLCONCAT('; + if (args) { + repr += args.join(', '); + } + repr += ')'; + return repr; + } + case 'IS_XMLPARSE': + { + const option = await this.getMemberValueEnum('xmloption'); + const args = await this.getListMemberElementsReprs('args', rtable); + if (!args) { + return 'XMLPARSE()'; + } + + const data = args[0]; + return `XMLPARSE(${option === 'XMLOPTION_DOCUMENT' ? 'DOCUMENT' : 'CONTENT'} ${data})`; + } + case 'IS_XMLPI': + { + const name = await this.getMemberValueCharString('name'); + const args = await this.getListMemberElementsReprs('args', rtable); + let repr = `XMLPI(NAME ${name}`; + if (args) { + repr += `, ${args.join(', ')}`; + } + repr += ')'; + return repr; + } + case 'IS_XMLROOT': + { + const args = await this.getListMemberElementsReprs('args', rtable); + let repr = 'XMLROOT('; + if (1 <= args.length) { + repr += args[0]; + } + + if (2 <= args.length) { + repr += `, ${args[1]}`; + } + + if (3 <= args.length) { + repr += `, ${args[2]}`; + } + + repr += ')'; + return repr; + } + case 'IS_XMLSERIALIZE': + { + const option = await this.getMemberValueEnum('xmloption'); + const args = await this.getListMemberElementsReprs('args', rtable); + const indent = await this.getMemberValueBool('indent'); + let repr = 'XMLSERIALIZE('; + if (args) { + repr += option === 'XMLOPTION_DOCUMENT' ? 'DOCUMENT ' : 'CONTENT '; + repr += args[0]; + } + + if (indent) { + repr += ' INDENT'; + } + repr += ')'; + return repr; + } + break; + case 'IS_DOCUMENT': + { + const args = await this.getListMemberElementsReprs('args', rtable); + if (args) { + return `${args[0]} IS DOCUMENT`; + } else { + return '??? IS DOCUMENT'; + } + } + } + return '???'; + } + + private async formatSubLink(rtable: RangeTableContainer) { + const type = await this.getMemberValueEnum('subLinkType'); + if (type === 'EXISTS_SUBLINK') { + return 'EXISTS(...)'; + } + + if (type === 'CTE_SUBLINK') { + return 'CTE(...)'; + } + + if (type === 'EXPR_SUBLINK' || type === 'MULTIEXPR_SUBLINK') { + return '(...)'; + } + + if (type === 'ARRAY_SUBLINK') { + return 'ARRAY(...)'; + } + + const getOpExprLeftRepr = async (v: Variable) => { + /* + * This function is used to obtain first argument from OpExpr. + * Mimics `get_leftop` semantics. + */ + if (!(v instanceof NodeVariable && v.realNodeTag === 'OpExpr')) { + return '???'; + } + + const elements = await v.getListMemberElements('args') + if (elements.length) { + const left = elements[0]; + if (left instanceof ExprNodeVariable) { + return await left.getReprInternal(rtable); + } + } + + return '???'; + } + + const testexpr = await this.getMember('testexpr'); + if (!(testexpr instanceof NodeVariable)) { + throw new EvaluationError('Failed to get SubLink->testexpr'); + } + + /* + * Depending on attribute count we might have: + * - OpExpr - single attribute + * - BoolExpr - mulitple OpExprs (in same form as OpExpr) + * - RowCompareExpr - list of attributes + */ + let leftReprs: string[]; + if (testexpr.realNodeTag === 'OpExpr') { + leftReprs = [await getOpExprLeftRepr(testexpr)]; + } else if (testexpr.realNodeTag === 'BoolExpr') { + const elements = await testexpr.getListMemberElements('args') + const reprs: string[] = []; + for (const e of elements) { + reprs.push(await getOpExprLeftRepr(e)); + } + + leftReprs = reprs; + } else { + /* testexpr.realNodeTag === 'RowCompareExpr' */ + + /* For RowCompareExpr in SubLink we will have all Param in 'rargs' */ + const largs = await testexpr.getListMemberElements('largs'); + const reprs = []; + for (const arg of largs) { + reprs.push(await this.getReprPlaceholder(arg, rtable)); + } + leftReprs = reprs; + } + + /* SubLink->operName[0]->sval */ + let opname = '???'; + const elements = await this.getListMemberElements('operName'); + if (elements?.length && elements[0] instanceof ValueVariable) { + opname = await elements[0].getStringValue() ?? '???' + } + + /* Maybe, there are no reprs in array, so 'join' seems safe here */ + const leftRepr = leftReprs.length > 1 || leftReprs.length === 0 + ? `ROW(${leftReprs.join(', ')})` + : leftReprs[0]; + + let funcname; + switch (type) { + case 'ALL_SUBLINK': + funcname = 'ALL'; + break; + case 'ANY_SUBLINK': + funcname = 'ANY'; + break; + case 'ROWCOMPARE_SUBLINK': + funcname = ''; + break; + default: + funcname = '???'; + break; + } + return `${leftRepr} ${opname} ${funcname}(...)`; + } + + private async formatRowCompareExpr(rtable: RangeTableContainer) { + const getReprs = async (arr: string[], member: string) => { + const elements = await this.getListMemberElementsReprs(member, rtable); + for (const e of elements) { + arr.push(e); + } + } + + const compareType = await this.getMemberValueEnum('rctype'); + const leftReprs: string[] = []; + const rightReprs: string[] = []; + + await getReprs(leftReprs, 'largs'); + await getReprs(rightReprs, 'rargs'); + + let opname; + switch (compareType) { + case 'ROWCOMPARE_LT': + opname = '<' + break; + case 'ROWCOMPARE_LE': + opname = '<='; + break; + case 'ROWCOMPARE_EQ': + opname = '='; + break; + case 'ROWCOMPARE_GE': + opname = '>='; + break; + case 'ROWCOMPARE_GT': + opname = '>'; + break; + case 'ROWCOMPARE_NE': + opname = '<>'; + break; + default: + opname = '???'; + break; + } + + return `ROW(${leftReprs.join(', ')}) ${opname} ROW(${rightReprs.join(', ')})`; + } + + private async delegateFormatToMember(member: string, rtable: RangeTableContainer) { + /* + * Repr of some exprs is same as repr of their field. + * For such cases use this function in order not to + * product many other functions. + */ + return await this.getMemberRepr(member, rtable); + } + + private async formatParam(rtable: RangeTableContainer) { + const paramNum = await this.getMemberValueNumber('paramid'); + return `PARAM$${paramNum}`; + } + + private async formatJsonExpr(rtable: RangeTableContainer) { + const op = await this.getMemberValueEnum('op'); + switch (op) { + case 'JSON_EXISTS_OP': + return 'JSON_EXISTS(...)'; + case 'JSON_QUERY_OP': + return 'JSON_QUERY(...)'; + case 'JSON_VALUE_OP': + return 'JSON_VALUE(...)'; + case 'JSON_TABLE_OP': + return 'JSON_TABLE(...)' + default: + const trailing = op.lastIndexOf('_OP'); + if (trailing === -1) { + return `${op}(...)` + } + return `${op.substring(0, trailing)}(...)`; + } + } + + private async formatJsonConstructorExpr(rtable: RangeTableContainer) { + const ctorType = await this.getMemberValueEnum('type'); + const args = await this.getListMemberElementsReprs('args', rtable); + if (ctorType === 'JSCTOR_JSON_OBJECTAGG' || ctorType === 'JSCTOR_JSON_ARRAYAGG') { + /* + * At runtime these function are rewritten and extracting + * arguments from actual FuncExpr/WindowExpr to recreate + * function repr "as it was meant" seems overhead. + * So show already rewritten function - we can do it already. + */ + return await this.getMemberRepr('func', rtable); + } + + let funcname; + switch (ctorType) { + case 'JSCTOR_JSON_OBJECT': + funcname = 'JSON_OBJECT'; + break; + case 'JSCTOR_JSON_ARRAY': + funcname = 'JSON_ARRAY'; + break; + case 'JSCTOR_JSON_PARSE': + funcname = 'JSON'; + break; + case 'JSCTOR_JSON_SCALAR': + funcname = 'JSON_SCALAR'; + break; + case 'JSCTOR_JSON_SERIALIZE': + funcname = 'JSON_SERIALIZE'; + break; + default: + { + const idx = ctorType.indexOf('JSCTOR_'); + if (idx !== -1) { + funcname = ctorType.substring(7); + } else { + funcname = ctorType; + } + } + break; + } + + let argsRepr; + if (ctorType === 'JSCTOR_JSON_OBJECT') { + let comma = false; + argsRepr = ''; + for (let i = 0; i < args.length - 1; i++) { + const arg = args[i]; + argsRepr += arg; + argsRepr += comma ? ', ' : ' : '; + comma = !comma; + } + + argsRepr += args[args.length - 1]; + } else { + argsRepr = args.join(', '); + } + + return `${funcname}(${argsRepr})`; + } + + private async formatJsonIsPredicate(rtable: RangeTableContainer) { + const expr = await this.getMemberRepr('expr', rtable); + const jsonType = await this.getMemberValueEnum('item_type'); + switch (jsonType) { + case 'JS_TYPE_ANY': + return `${expr} IS JSON`; + case 'JS_TYPE_OBJECT': + return `${expr} IS JSON OBJECT`; + case 'JS_TYPE_ARRAY': + return `${expr} IS JSON ARRAY`; + case 'JS_TYPE_SCALAR': + return `${expr} IS JSON SCALAR`; + default: + return `${expr} IS JSON ???`; + } + } + + private async formatWindowFuncRunCondition(rtable: RangeTableContainer) { + const wfuncLeft = await this.getMemberValueBool('wfunc_left'); + const expr = await this.getMemberRepr('arg', rtable); + const opname = await this.getOpName('opno') ?? '(invalid op)'; + let left, right; + if (wfuncLeft) { + left = 'WINDOW'; + right = expr; + } else { + left = expr; + right = 'WINDOW'; + } + + return `${left} ${opname} ${right}`; + } + + private async formatCaseWhen(rtable: RangeTableContainer) { + const when = await this.getMemberRepr('expr', rtable); + const then = await this.getMemberRepr('result', rtable); + return `WHEN ${when} THEN ${then}`; + } + + private async formatFieldSelect(rtable: RangeTableContainer) { + /* + * This is hard to determine name of field using only + * attribute number - there are many manipulations should occur. + * For example, see src/backend/utils/adt/ruleutils.c:get_name_for_var_field. + * + * For now, just print container expr and '???' as field. + * I think, in the end developers will understand which field is used. + */ + const expr = await this.getMemberRepr('arg', rtable); + return `${expr}.???`; + } + + private async formatFieldStore(rtable: RangeTableContainer) { + const expr = await this.getMemberRepr('arg', rtable); + return `${expr}.??? = ???`; + } + + private async formatCurrentOfExpr(rtable: RangeTableContainer) { + const sval = await this.getMemberValueCharString('cursor_name'); + return `CURRENT OF ${sval === null ? 'NULL' : sval}`; + } + + private async formatExpr(rtable: RangeTableContainer): Promise { + /* + * WARN: if you add/remove something here do not forget to update + * src/constants.ts:getDisplayedExprs + */ + try { + /* + * Values sorted in order of appearing frequency. + * P.S. Of course in my opinion, no stats collected. + */ + switch (this.realNodeTag) { + case 'Var': + return await this.formatVarExpr(rtable); + case 'Const': + return await this.formatConst(rtable); + case 'OpExpr': + return await this.formatOpExpr(rtable); + case 'FuncExpr': + return await this.formatFuncExpr(rtable); + case 'Aggref': + return await this.formatAggref(rtable); + case 'TargetEntry': + return await this.formatTargetEntry(rtable); + case 'ScalarArrayOpExpr': + return await this.formatScalarArrayOpExpr(rtable); + case 'BoolExpr': + return await this.formatBoolExpr(rtable); + case 'BooleanTest': + return await this.formatBooleanTest(rtable); + case 'CoalesceExpr': + return await this.formatCoalesceExpr(rtable); + case 'Param': + return await this.formatParam(rtable); + case 'NullTest': + return await this.formatNullTest(rtable); + case 'ArrayExpr': + return await this.formatArrayExpr(rtable); + case 'SQLValueFunction': + return await this.formatSqlValueFunction(rtable); + case 'MinMaxExpr': + return await this.formatMinMaxExpr(rtable); + case 'RowExpr': + return await this.formatRowExpr(rtable); + case 'DistinctExpr': + return await this.formatDistinctExpr(rtable); + case 'NullIfExpr': + return await this.formatNullIfExpr(rtable); + case 'NamedArgExpr': + return await this.formatNamedArgExpr(rtable); + case 'GroupingFunc': + return await this.formatGroupingFunc(rtable); + case 'WindowFunc': + return await this.formatWindowFunc(rtable); + case 'SubscriptingRef': + case 'ArrayRef' /* old style 'SubscripingRef' */: + return await this.formatSubscriptingRef(rtable); + case 'XmlExpr': + return await this.formatXmlExpr(rtable); + case 'SubLink': + return await this.formatSubLink(rtable); + case 'RowCompareExpr': + return await this.formatRowCompareExpr(rtable); + case 'ArrayCoerceExpr': + return await this.delegateFormatToMember('arg', rtable); + case 'CoerseToDomain': + return await this.delegateFormatToMember('arg', rtable); + case 'ConvertRowtypeExpr': + return await this.delegateFormatToMember('arg', rtable); + case 'CollateExpr': + return await this.delegateFormatToMember('arg', rtable); + case 'CoerceViaIO': + return await this.delegateFormatToMember('arg', rtable); + case 'RelabelType': + return await this.delegateFormatToMember('arg', rtable); + case 'JsonExpr': + return await this.formatJsonExpr(rtable); + case 'JsonValueExpr': + return await this.delegateFormatToMember('raw_expr', rtable); + case 'JsonConstructorExpr': + return await this.formatJsonConstructorExpr(rtable); + case 'JsonIsPredicate': + return await this.formatJsonIsPredicate(rtable); + case 'WindowFuncRunCondition': + return await this.formatWindowFuncRunCondition(rtable); + case 'CaseWhen': + return await this.formatCaseWhen(rtable); + case 'FieldSelect': + return await this.formatFieldSelect(rtable); + case 'FieldStore': + return await this.formatFieldStore(rtable); + case 'CurrentOfExpr': + return await this.formatCurrentOfExpr(rtable); + case 'InferenceElem': + return await this.delegateFormatToMember('expr', rtable); + + /* + * Some Exprs i will not add, i.e.: + * - SubPlan - too bulky, to extract some data + * - AlternativeSubPlan - same as above + * - CaseExpr - too big for small field in editor + * + * For such, we have placeholders. I think, that's enough. + */ + } + } catch (error) { + if (!(error instanceof EvaluationError)) { + throw error; + } + + this.logger.debug('failed repr for %s', this.realNodeTag, error); + } + return this.getExprPlaceholder(this); + } + + /* + * Entry point to get text representation of Expr during + * recursive repr evaluation. + * This is speed up, because of already found 'rtable' passing. + */ + private async getReprInternal(rtable: RangeTableContainer) { + if (this.repr) { + return this.repr; + } + + const repr = await this.formatExpr(rtable); + this.repr = repr; + return repr; + } + + /** + * Global entry point to get text representation of Expression. + * + * @returns text representation of Expr node + */ + async getRepr() { + if (this.repr) { + return this.repr; + } + + const rtable = new RangeTableContainer(); + return await this.getReprInternal(rtable); + } + + private async findRtable() { + /* + * We can go in 3 ways: + * + * 1. PlannderInfo->parse (Query)->rtable + * 2. Query->rtable + * 3. PlannedStmt->rtable + */ + let node = this.parent; + while (node) { + if (node instanceof VariablesRoot) { + node = node.topLevelVariables.find(v => + ((v instanceof NodeVariable && + (v.realNodeTag === 'PlannerInfo' || + v.realNodeTag === 'Query' || + v.realNodeTag === 'PlannedStmt')))); + if (!node) { + /* No more variables */ + return; + } + + break; + } else if (node instanceof NodeVariable && + (node.realNodeTag === 'PlannerInfo' || + node.realNodeTag === 'Query' || + node.realNodeTag === 'PlannedStmt')) { + break; + } + + node = node.parent; + } + + if (!(node && node instanceof NodeVariable)) { + return; + } + + let rtable; + switch (node.realNodeTag) { + case 'Query': + /* Query->rtable */ + rtable = await node.getListMemberElements('rtable'); + break; + case 'PlannerInfo': + /* PlannerInfo->parse->rtable */ + const parse = await node.getMember('parse'); + if (!(parse && parse instanceof NodeVariable)) { + break; + } + + rtable = await parse.getListMemberElements('rtable'); + break; + case 'PlannedStmt': + /* PlannedStmt->rtable */ + rtable = node.getListMemberElements('rtable'); + break; + default: + this.logger.warn('got unexpected NodeTag in findRtable: %s', node.realNodeTag); + return; + } + + if (rtable === undefined) { + return; + } + + return rtable; + } + + async doGetChildren() { + const expr = await this.getRepr(); + if (!expr) { + return await super.doGetChildren(); + } + + /* Add representation field first in a row */ + const exprVariable = new ScalarVariable('$expr$', expr, '', this.context, + this.logger, this, expr) + const children = await super.doGetChildren() ?? []; + children.unshift(exprVariable); + return children; + } +} + +/** + * Simple wrapper around 'Expr' containing variable, + * which must display it's repr in description member. + * + * Used for 'EquivalenceMember' and 'RestrictInfo'. + */ +class DisplayExprReprVariable extends NodeVariable { + /** + * 'Expr' member which representation is shown + */ + readonly exprMember: string; + + constructor(tag: string, exprMember: string, args: RealVariableArgs) { + super(tag, args); + this.exprMember = exprMember; + } + + async getDescription() { + const exprMember = await this.getMember(this.exprMember); + if (exprMember instanceof ExprNodeVariable) { + return await exprMember.getRepr(); + } + + return ''; + } +} + +/** + * Special case for 'TargetEntry' to display it's repr + * in description. + * It can not be moved to 'DisplayExprReprVariable' because + * it is Expr and can be used in 'ExprVariable. + * Also I do not want to move such logic to 'ExprVariable', + * because repr evaluation is resource-intensive operation + * and UI just blocks. + */ +class TargetEntryVariable extends ExprNodeVariable { + constructor(args: RealVariableArgs) { + super('TargetEntry', args); + } + + async getDescription() { + const repr = await this.getRepr(); + if (!repr) { + return await super.getDescription(); + } + + return repr; + } +} + +class ListElementsMember extends RealVariable { + /* + * Members of this list + */ + members: Variable[] | undefined; + + /** + * Member of ListCell to use. + * @example int_value, oid_value + */ + cellValue: string; + + /** + * Real type of stored data + * @example int, Oid, Node * (or custom) + */ + listCellType: string; + + /** + * Parent List variable to which we belong + */ + listParent: ListNodeVariable; + + constructor(listParent: ListNodeVariable, cellValue: string, listCellType: string, + args: RealVariableArgs) { + super(args); + this.listParent = listParent; + this.cellValue = cellValue; + this.listCellType = listCellType; + } + + async getTreeItem() { + return { + label: '$elements$', + collapsibleState: this.listParent.isEmpty() + ? vscode.TreeItemCollapsibleState.None + : vscode.TreeItemCollapsibleState.Collapsed, + }; + } + + async getPointerElements() { + const length = await this.listParent.getListLength(); + if (!length) { + return; + } + + const listType = this.listParent.getMemberExpression('elements'); + const expression = `(${this.listCellType}*)(${listType})`; + return super.getArrayMembers(expression, length); + } + + async getIntegerElements() { + const length = await this.listParent.getListLength(); + if (!length) { + return; + } + + /* + * We can not just cast `elements' to int* or Oid* + * due to padding in `union'. For these we iterate + * each element and evaluate each item independently + */ + const elements: RealVariable[] = []; + for (let i = 0; i < length; i++) { + const expression = `(${this.evaluateName})[${i}].${this.cellValue}`; + const response = await this.debug.evaluate(expression, this.frameId); + elements.push(new RealVariable({ + name: `[${i}]` /* array elements behaviour */, + type: this.listCellType, + evaluateName: expression, + variablesReference: response.variablesReference, + value: response.result, + memoryReference: response.memoryReference, + frameId: this.frameId, + context: this.context, + logger: this.logger, + parent: this, + })); + } + + return elements; + } + + async doGetChildren() { + if (this.members !== undefined) { + return this.members; + } + + this.members = await (this.listParent.realNodeTag === 'List' + ? this.getPointerElements() + : this.getIntegerElements()); + + return this.members; + } + + protected isExpandable(): boolean { + return true; + } +} + +/* + * Show elements of List for Linked List implementation (head/tail). + * Suitable for Postgres version prior to 13. + */ +class LinkedListElementsMember extends Variable { + /* Members of this List */ + members: Variable[] | undefined; + + /** + * Member of ListCell to use. + * @example int_value, oid_value, ptr_value, xid_value + */ + cellValue: string; + + /** + * Real type of stored data + * @example int, Oid, Node *, Xid + */ + realType: string; + + /** + * List structure we observing + */ + listParent: ListNodeVariable; + + constructor(listParent: ListNodeVariable, cellValue: string, + realType: string, context: ExecContext) { + super('$elements$', '', '', context, listParent.frameId, listParent, listParent.logger); + this.listParent = listParent; + this.cellValue = cellValue; + this.realType = realType; + } + + async getLinkedListElements() { + /* + * Traverse through linked list until we get NULL + * and read each element from List manually. + * So we do not need to evaluate length. + */ + const elements: dap.DebugVariable[] = []; + const headExpression = this.listParent.getMemberExpression('head'); + let evaluateName = headExpression; + let cell = await this.debug.evaluate(headExpression, this.frameId); + let i = 0; + do { + const valueExpression = `(${this.realType})((${evaluateName})->data.${this.cellValue})`; + const response = await this.debug.evaluate(valueExpression, this.frameId); + elements.push({ + name: `[${i}]`, + value: response.result, + type: this.realType, + evaluateName: valueExpression, + variablesReference: response.variablesReference, + memoryReference: response.memoryReference, + }); + evaluateName = `${evaluateName}->next`; + cell = await this.debug.evaluate(evaluateName, this.frameId); + ++i; + } while (!utils.isNull(cell.result)); + + return await Variable.mapVariables(elements, this.frameId, this.context, + this.logger, this.listParent); + } + + async doGetChildren() { + if (this.members !== undefined) { + return this.members; + } + + this.members = await this.getLinkedListElements(); + return this.members; + } + + protected isExpandable(): boolean { + return true; + } +} + +/** + * Special class to represent various Lists: Node, int, Oid, Xid... + */ +export class ListNodeVariable extends NodeVariable { + /* Special member, that manages elements of this List */ + listElements?: ListElementsMember | LinkedListElementsMember; + + constructor(nodeTag: string, args: RealVariableArgs) { + super(nodeTag, args); + } + + getMemberExpression(member: string) { + return `((${this.getRealType()})${this.value})->${member}` + } + + isEmpty() { + return utils.isNull(this.value); + } + + protected isExpandable(): boolean { + return !this.isEmpty(); + } + + private async findTypeForPtr() { + /* + * Usually (i.e. in planner) ptr value is a node variable (Node *), + * but actually it can be any pointer. + * + * All `List`s hold Nodes, but sometimes it can be custom data. + * These special cases can be identified by: + * + * 1. Function name + variable name (if this is top level variable) + * 2. Structure name + member name (if this is a member of structure) + */ + + if (!this.parent) { + /* + * All valid Variable objects must have 'parent' set + * except special case 'VariablesRoot', but we are 'List', + * not 'VariablesRoot'. + */ + return 'Node *'; + } + + let map = this.context.specialMemberRegistry.listCustomPtrs.get(this.name); + if (!map) { + return 'Node *'; + } + + /* Check only 1 case - they are mutually exclusive */ + if (this.parent instanceof VariablesRoot) { + const func = await this.debug.getFunctionName(this.frameId); + if (func) { + const info = map.get(func); + if (info) { + return info.type; + } + } + } else { + const parentType = utils.getStructNameFromType(this.parent.type); + const info = map.get(parentType); + if (info) { + return info.type; + } + } + + return 'Node *'; + } + + private async createArrayNodeElementsMember(elementsMember: RealVariable) { + /* Default safe values */ + let cellValue = 'int_value'; + let realType = 'int'; + + switch (this.realNodeTag) { + case 'List': + cellValue = 'ptr_value'; + realType = await this.findTypeForPtr(); + break; + case 'IntList': + break; + case 'OidList': + cellValue = 'oid_value'; + realType = 'Oid'; + break; + case 'XidList': + cellValue = 'xid_value'; + realType = 'TransactionId'; + break; + default: + this.logger.warn('failed to determine List tag for %s->elements. using int value', + this.name); + break; + } + + return new ListElementsMember(this, cellValue, realType, { + ...elementsMember.getRealVariableArgs(), + frameId: this.frameId, + parent: this, + context: this.context, + logger: this.logger + }); + } + + private async createLinkedListNodeElementsMember() { + /* Default safe values */ + let cellValue = 'int_value'; + let realType = 'int'; + + switch (this.realNodeTag) { + case 'List': + cellValue = 'ptr_value'; + realType = await this.findTypeForPtr(); + break; + case 'IntList': + break; + case 'OidList': + cellValue = 'oid_value'; + realType = 'Oid'; + break; + case 'XidList': + cellValue = 'xid_value'; + realType = 'TransactionId'; + break; + default: + this.logger.warn('failed to determine List tag for %s->elements. using int value', + this.name); + break; + } + + return new LinkedListElementsMember(this, cellValue, realType, + this.context); + } + + override computeRealType(): string { + const declaredTag = utils.getStructNameFromType(this.type); + if (declaredTag !== 'List') { + return utils.substituteStructName(this.type, 'List'); + } + return this.type; + } + + private async castToList() { + const realType = this.getRealType(); + const castExpression = `(${realType}) (${this.evaluateName})`; + const response = await this.debug.evaluate(castExpression, this.frameId); + if (!Number.isInteger(response.variablesReference)) { + this.logger.warn('failed to cast %s to List*: %s', + this.evaluateName, response.result); + return; + } + + /* Also update type - it will be used */ + this.variablesReference = response.variablesReference; + } + + async doGetChildren() { + if (this.isEmpty()) { + /* Just show empty members */ + return await this.doGetRealMembers(); + } + + if (!this.tagsMatch()) { + await this.castToList(); + } + + const m = await this.doGetRealMembers(); + if (!m) { + return m; + } + + const e = m.find(v => v.name === 'elements'); + if (!e) { + this.listElements = await this.createLinkedListNodeElementsMember(); + return [ + ...m.filter(v => v.name !== 'head' && v.name !== 'tail'), + this.listElements + ]; + } + + if (!(e && e instanceof RealVariable)) { + return m; + } + + this.listElements = await this.createArrayNodeElementsMember(e); + return [ + ...m.filter(v => v.name !== 'elements' && v.name !== 'initial_elements'), + this.listElements, + ]; + } + + async getListLength() { + if (this.isEmpty()) { + return 0; + } + + const lengthExpression = this.getMemberExpression('length'); + const evalResult = await this.debug.evaluate(lengthExpression, this.frameId); + const length = Number(evalResult.result); + if (Number.isNaN(length)) { + this.logger.warn('failed to obtain list size for %s', this.name); + return; + } + return length; + } + + async getListElements() { + if (this.isEmpty()) { + return []; + } + + if (!this.listElements) { + /* Initialize members */ + await this.getChildren(); + if (!this.listElements) { + /* Failed to initialize */ + return; + } + } + + return await this.listElements.getChildren(); + } +} + + +export class ArraySpecialMember extends RealVariable { + /** + * Expression to evaluate to obtain array length. + * Appended to target struct from right. + * First element is length member name, but after + * can be correction expressions i.e. '+ 1'. + */ + info: ArraySpecialMemberInfo; + parent: RealVariable; + + constructor(parent: RealVariable, info: ArraySpecialMemberInfo, + args: RealVariableArgs) { + super(args); + this.info = info; + this.parent = parent; + } + + async doGetRealMembers() { + const lengthExpr = `(${this.parent.evaluateName})->${this.info.lengthExpr}`; + const evalResult = await this.evaluate(lengthExpr); + const length = Number(evalResult.result); + if (Number.isNaN(length)) { + this.logger.warn('failed to obtain array size using expr "%s" for (%s)->%s', + lengthExpr, this.type, this.name); + return await super.doGetRealMembers(); + } + + if (length === 0) { + return await super.doGetRealMembers(); + } + + const memberExpr = `(${this.parent.evaluateName})->${this.info.memberName}`; + const debugVariables = await this.debug.getArrayVariables(memberExpr, + length, this.frameId); + return await Variable.mapVariables(debugVariables, this.frameId, this.context, + this.logger, this); + } +} + +/* + * Bitmapset* variable + */ +class BitmapSetSpecialMember extends NodeVariable { + /* + * List of functions that we are using for bitmapset evaluation. + * We need to ensure, that no breakpoints set on them, otherwise + * we encounter infinite loop + */ + private static evaluationUsedFunctions = [ + 'bms_next_member', + 'bms_first_member', + 'bms_is_valid_set' + ] + + constructor(args: RealVariableArgs) { + super('Bitmapset', args,); + } + + async isValidSet(): Promise { + /* + * First, validate NodeTag. BitmapSetSpecialMember could be + * created using dumb type check, without actual NodeTag + * checking. So we do it here + */ + if (this.context.hasBmsNodeTag) { + const tag = await this.evaluate(`((Bitmapset *)${this.value})->type`); + if (tag.result !== 'T_Bitmapset') { + if (!utils.isValidIdentifier(tag.result)) { + /* Do not track NodeTag anymore and perform check again */ + this.context.hasBmsNodeTag = false; + return await this.isValidSet(); + } else { + /* They do not match */ + return false; + } + } + } else { + /* + * If we do not have NodeTag, then try to check that we can deref + * pointer (means that pointer is valid). + * 'nwords' member is only available option in this case. + * If output is empty, then pointer is invalid. + * + * Also, pointer may give valid (at first glance) result, + * but it contains garbage and value will be too large - we + * check this too. 50 seems big enough to start worrying about. + */ + const result = await this.evaluate(`((Bitmapset *)${this.value})->nwords`); + const nwords = Number(result.result); + if (!(result.result && Number.isInteger(nwords) && nwords < 50)) { + return false; + } + } + + if (this.context.hasBmsIsValidSet) { + const expression = `bms_is_valid_set((Bitmapset *)${this.value})`; + const response = await this.evaluate(expression); + if (utils.isFailedVar(response)) { + /* + * `bms_is_valid_set' introduced in 17. + * On other versions `type` member will be not set (undefined). + * We assume it is valid, because for NULL variables we do not + * create Variable instances. + */ + this.context.hasBmsIsValidSet = false; + return true; + } + + return utils.extractBoolFromValue(response.result) ?? false; + } + + return true; + } + + safeToObserve() { + /* + * Fastest way I found is just to iterate all breakpoints and check + * - no bp in bitmapset.c source code for line breakpoints + * - no bp for bms_next_member function for function breakpoints + * + * I have found only these 2 subclasses of breakpoints. + * Seems that it is enough. + */ + for (const bp of vscode.debug.breakpoints) { + if (!bp.enabled) { + continue; + } + + if (bp instanceof vscode.SourceBreakpoint) { + if (bp.location.uri.path.endsWith('bitmapset.c')) { + this.logger.info('found breakpoint at bitmapset.c - set elements not shown'); + return false; + } + } else if (bp instanceof vscode.FunctionBreakpoint) { + /* + * Need to check functions that are called to get set elements + */ + if (BitmapSetSpecialMember.evaluationUsedFunctions.indexOf(bp.functionName) !== -1) { + this.logger.info('found breakpoint at %s - bms elements not shown', + bp.functionName); + return false; + } + } + } + return true; + } + + + async getSetElements(): Promise { + /* + * Must check we do not have breakpoints set in `bms_next_member`. + * Otherwise, we will get infinite recursion and backend will crash. + */ + if (!this.safeToObserve()) { + return; + } + + /* + * We MUST check validity of set, because, otherwise, + * `Assert` will fail or SEGFAULT si thrown and whole + * backend will crash + */ + if (!await this.isValidSet()) { + return; + } + + /* + * Most likely, we use new Bitmapset API, but fallback with old-styled + */ + let result; + if (this.context.hasBmsNextMember) { + result = await this.getSetElementsNextMember(); + if (result !== undefined) { + return result; + } + } + + result = await this.getSetElementsFirstMember(); + if (result !== undefined) { + this.context.hasBmsNextMember = false; + } + + return result; + } + + private async getSetElementsNextMember(): Promise { + /* + * Current style (from 9.3) of reading Bitmapset values: + * + * Bitmapset *bms; + * int x = -1; + * while ((x = bms_next_member(bms, x)) > 0) + * { + * ... + * } + */ + + let number = -1; + const numbers = []; + do { + const expression = `bms_next_member((Bitmapset *)${this.value}, ${number})`; + const response = await this.evaluate(expression); + number = Number(response.result); + if (Number.isNaN(number)) { + this.logger.warn('failed to get set elements for %s', this.name); + return; + } + + if (number < 0) { + break; + } + + numbers.push(number); + } while (number >= 0); + + return numbers; + } + + private async getSetElementsFirstMember(): Promise { + /* + * Old style (prior to 9.2) of reading Bitmapset values: + * + * Bitmapset *bms; + * Bitmapset *tmp; + * tmp = bms_copy(bms); + * + * int x; + * while ((x = bms_first_member(tmp)) > 0) + * { + * ... + * } + * + * pfree(tmp); + */ + const e = await this.evaluate(`bms_copy(${this.evaluateName})`); + const bms = e.result; + if (!utils.isValidPointer(bms)) { + if (utils.isNull(bms)) { + return; + } + + this.logger.warn('error during "bms_copy" evaluation: %s', e.result); + return; + } + + let number = -1; + const numbers = []; + do { + const expression = `bms_first_member((Bitmapset*)${bms})`; + const response = await this.evaluate(expression); + number = Number(response.result); + if (Number.isNaN(number)) { + this.logger.warn('failed to get set elements for "%s": %s', + this.name, response.result); + return; + } + + if (number < 0) { + break; + } + + numbers.push(number); + } while (number >= 0); + + await this.pfree(bms); + + return numbers; + } + + async getBmsRef() { + if (!this.parent) { + return; + } + + const ref = this.context.nodeVarRegistry.findBmsReference(this); + if (!ref) { + return; + } + + let type; + if (this.parent instanceof NodeVariable) { + type = this.parent.getRealType(); + } else { + type = this.parent.type; + } + if (!(utils.getStructNameFromType(type) === ref.type && + utils.getPointersCount(type) === 1)) { + return; + } + + return ref; + } + + async doGetChildren() { + /* All existing members */ + const members = await Variable.getVariables(this.variablesReference, + this.frameId, this.context, + this.logger, this); + if (!members) { + return members; + } + + /* + Set elements */ + const setMembers = await this.getSetElements(); + if (setMembers === undefined) { + return members.filter(v => v.name !== 'words'); + } + + const ref = await this.getBmsRef(); + + members.push(new ScalarVariable('$length$', setMembers.length.toString(), + 'int', this.context, this.logger, this)); + members.push(new BitmapSetSpecialMember.BmsArrayVariable(this, setMembers, ref)); + + return members.filter(v => v.name !== 'words'); + } + + static BmsElementVariable = class extends Variable { + /* + * `value` as number. needed for refs + */ + relid: number; + + bmsParent: BitmapSetSpecialMember; + + /* + * Which objects this Bitmapset references + */ + ref?: constants.BitmapsetReference; + + constructor(index: number, + parent: Variable, + bmsParent: BitmapSetSpecialMember, + value: number, + context: ExecContext, + ref: constants.BitmapsetReference | undefined) { + super(`[${index}]`, value.toString(), 'int', context, parent.frameId, parent, parent.logger); + this.relid = value; + this.bmsParent = bmsParent; + this.ref = ref; + } + + findStartElement(ref: constants.BitmapsetReference) { + if (ref.start === 'Self') { + return this.bmsParent.parent; + } else if (this.ref!.start === 'Parent') { + return this.bmsParent.parent?.parent; + } + + /* Find PlannerInfo in parents */ + let parent = this.bmsParent.parent; + + while (parent) { + if (parent.type.indexOf('PlannerInfo') !== -1 && + parent instanceof NodeVariable && + parent.realNodeTag === 'PlannerInfo') { + return parent; + } + + /* + * If this is last variable, it must be 'VariablesRoot'. + * As last chance, find 'PlannerInfo' in declared variables, + * not direct parent. + */ + if (!parent.parent) { + if (parent.name === VariablesRoot.variableRootName && + parent instanceof VariablesRoot) { + for (const v of parent.topLevelVariables) { + if (v instanceof NodeVariable && + v.realNodeTag === 'PlannerInfo') { + return v; + } + } + } + } + + parent = parent.parent; + } + + return undefined; + } + + async findReferenceFields() { + if (!this.ref) { + return; + } + + const root = this.findStartElement(this.ref); + if (!root) { + return; + } + + const resultFields: [Variable, number?][] = []; + + for (const path of this.ref.paths) { + let variable: Variable = root; + for (const p of path.path) { + let member; + + /* Separation made for speed performance */ + if (variable instanceof RealVariable) { + try { + member = await variable.getMember(p); + } catch (e) { + if (!(e instanceof EvaluationError)) { + throw e; + } + + member = undefined; + } + } else { + const members = await variable.getChildren(); + if (members) + member = members.find((v) => v.name === p); + } + + if (!member) { + break; + } + + variable = member; + } + + if (variable) { + resultFields.push([variable, path.indexDelta]); + } + } + + if (resultFields.length) { + return resultFields; + } + return; + } + + async getArrayElement(field: Variable, indexDelta?: number) { + const index = this.relid + (indexDelta ?? 0); + + if (field instanceof ListNodeVariable) { + const members = await field.getListElements(); + if (members && index < members.length) { + return members[index]; + } + } else if (field instanceof ArraySpecialMember) { + const members = await field.getChildren(); + if (members && index < members.length) { + return members[index]; + } + } else if (field instanceof RealVariable) { + if (field.type === 'List *') { + /* Empty List* will be created as RealVariable */ + return; + } + const expr = `(${field.evaluateName})[${index}]`; + const result = await this.debug.evaluate(expr, this.bmsParent.frameId); + if (result.result) { + return await Variable.create({ + ...result, + name: `ref(${field.name})`, + value: result.result, + evaluateName: expr + }, this.bmsParent.frameId, this.context, this.bmsParent.logger, this); + } + } + } + + async doGetChildren(): Promise { + if (!this.ref) { + return; + } + + const fields = await this.findReferenceFields(); + + if (!fields) { + return; + } + + const values = []; + for (const [field, delta] of fields) { + const value = await this.getArrayElement(field, delta); + if (value) { + values.push(value) + } + } + + return values.length ? values : undefined; + } + + protected isExpandable(): boolean { + return this.ref !== undefined; + } + } + + static BmsArrayVariable = class extends Variable { + setElements: number[]; + bmsParent: BitmapSetSpecialMember; + constructor(parent: BitmapSetSpecialMember, + setElements: number[], + private ref?: constants.BitmapsetReference) { + super('$elements$', '', '', parent.context, parent.frameId, parent, parent.logger); + this.setElements = setElements; + this.bmsParent = parent; + } + + async doGetChildren(): Promise { + return this.setElements.map((se, i) => new BitmapSetSpecialMember.BmsElementVariable(i, this, this.bmsParent, se, this.context, this.ref)) + } + + protected isExpandable(): boolean { + return true; + } + + async getTreeItem() { + return { + label: '$elements$', + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + } as vscode.TreeItem; + } + } + + static isBitmapsetType(type: string) { + const typename = utils.getStructNameFromType(type); + if (typename === 'Bitmapset') { + /* Bitmapset* */ + return utils.getPointersCount(type) === 1; + } else if (typename === 'Relids') { + /* Relids */ + return utils.getPointersCount(type) === 0; + } + return false; } } /** - * Special class to represent various Lists: Node, int, Oid, Xid... + * Represents Integer, String, Boolean, Float or BitString nodes. + * In older systems there was single 'Value' struct for them, + * but now separate. + * This class contains logic for handling both cases */ -export class ListNodeTagVariable extends NodeTagVariable { - constructor(nodeTag: string, args: RealVariableArgs, logger: utils.ILogger) { - super(nodeTag, args, logger); - } - - protected isExpandable(): boolean { - return true; +class ValueVariable extends NodeVariable { + isString() { + return this.realNodeTag === 'String'; } - createNodeElementsMember(dv: dap.DebugVariable) { - /* Default safe values */ - let cellValue = 'int_value'; - let realType = 'int'; + protected async checkTagMatch() { + const structName = utils.getStructNameFromType(this.type); - switch (this.realNodeTag) { - case 'List': - cellValue = 'ptr_value'; - realType = 'Node *'; - break; - case 'IntList': - break; - case 'OidList': - cellValue = 'oid_value'; - realType = 'Oid'; - break; - case 'XidList': - cellValue = 'xid_value'; - realType = 'TransactionId'; - break; - default: - this.logger.warn(`failed to determine List tag for ${this.name}->elements. using int value`); - break; + if (structName === this.realNodeTag || structName === 'Value') { + /* + * If tag equal to it's tag, so it's already have + * valid type and no need to cast. + * + * 'Value' is not a tag, but in this case we do not + * need to do anything too - already right type. + */ + return; } - return new ListNodeTagVariable.ListElementsMember(this, cellValue, realType, this.logger, { - ...dv, - frameId: this.frameId, - parent: this, - }); - } + if (!this.context.hasValueStruct) { + /* Try cast struct to corresponding tag */ + const result = await this.castToTag(this.realNodeTag); + if (!utils.isFailedVar(result)) { + /* Success */ + return; + } - override computeRealType(): string { - const declaredTag = utils.getStructNameFromType(this.type); - if (declaredTag !== 'List') { - return utils.substituteStructName(this.type, 'List'); + this.logger.debug('failed to cast type "%s" to tag "%s": %s', + this.type, this.realNodeTag, result.result); } - return this.type; - } - private async castToList(context: ExecContext) { - const realType = this.getRealType(); - const response = await context.debug.evaluate(`(${realType}) (${this.evaluateName})`, this.frameId); - if (!Number.isInteger(response.variablesReference)) { - this.logger.warn(`failed to cast ${this.evaluateName} to List*: ${response.result}`); + const result = await this.castToTag('Value'); + /* Try cast to 'Value' structure */ + if (!utils.isFailedVar(result)) { + /* On success update flag indicating we have 'Value' */ + this.context.hasValueStruct = true; return; } - /* Also update type - it will be used */ - this.variablesReference = response.variablesReference; + this.logger.debug('failed to cast type "%s" to tag "Value": %s', + this.type, result.result); } - async getChildren(context: ExecContext) { - if (!this.tagsMatch()) { - await this.castToList(context); + async doGetChildren() { + const children = await super.doGetChildren(); + if (!(children && this.context.hasValueStruct)) { + /* For modern structures no need to show real values */ + return children; } - const debugVariables = await context.debug.getMembers(this.variablesReference); - if (!debugVariables) { - return; + const val = children.find(v => v.name === 'val'); + if (!val) { + return children; } - /* Replace `elements' variable with special case */ - const members: Variable[] = []; - for (let i = 0; i < debugVariables.length; i++) { - const dv = debugVariables[i]; - if (dv.name === 'elements') { - members.push(this.createNodeElementsMember(dv)); - } else { - const v = await Variable.createVariable(dv, this.frameId, context, this.logger, this as RealVariable); - if (v) { - members.push(v) + const valMembers = await val.getChildren(); + if (!valMembers) { + return children; + } + + let value: string; + switch (this.realNodeTag) { + case 'String': + case 'BitString': + case 'Float': + /* read str value */ + const str = valMembers.find(v => v.name === 'str'); + if (!str) { + return children; } - } + value = str.value; + break; + case 'Integer': + case 'Boolean': + /* read int value */ + const ival = valMembers.find(v => v.name === 'ival'); + if (!ival) { + return children; + } + value = ival.value; + break; + case 'Null': + /* idk if this can happen, but anyway */ + value = 'NULL'; + break; + default: + return children; } - return members; + return [ + new ScalarVariable('$value$', value, + '' /* no type for this */, + this.context, this.logger, this), + ...children.filter(v => v.name !== 'val'), + ] } /** - * Show `elements' member of List struct of Node* values + * Get string value if node is T_String. + * + * @returns `string` value or `null` if it was NULL + * @throws EvaluationError if current Node is not T_String or errors + * during evalution occured */ - private static ListElementsMember = class extends RealVariable { - /** - * Member of ListCell to use. - * @example int_value, oid_value - */ - cellValue: string; - - /** - * Real type of stored data - * @example int, Oid - */ - realType: string; - - listParent: ListNodeTagVariable; - - constructor(listParent: ListNodeTagVariable, cellValue: string, realType: string, logger: utils.ILogger, args: RealVariableArgs) { - super(args, logger); - this.listParent = listParent; - this.cellValue = cellValue; - this.realType = realType; + async getStringValue() { + if (!this.isString()) { + throw new EvaluationError(`current ValueVariable is not String: ${this.realNodeTag}`); } - private async getListLength(context: ExecContext) { - const lengthExpression = `((${this.listParent.getRealType()})${this.listParent.memoryReference})->length`; - const length = Number((await context.debug.evaluate(lengthExpression, this.listParent.frameId)).result); - if (Number.isNaN(length)) { - this.logger.warn(`failed to obtain list size for ${this.listParent.name}`); - return; - } - return length; + const children = await this.getRealMembers(); + if (!children) { + throw new EvaluationError('failed to get children of ValueVariable'); } - async getNodeElements(context: ExecContext) { - const length = await this.getListLength(context); - if (!length) { - return; + /* It must be known by this time */ + if (this.context.hasValueStruct) { + const val = children.find(v => v.name === 'val'); + if (!val) { + throw new EvaluationError('member Value->val not found'); } - const expression = `(Node **)(((${this.listParent.getRealType()})${this.listParent.memoryReference})->elements), ${length}`; - const response = await context.debug.evaluate(expression, this.frameId); - return await Variable.getVariables(response.variablesReference, this.frameId, context, this.logger, this); - } - - async getIntegerElements(context: ExecContext) { - const length = await this.getListLength(context); - if (!length) { - return; + const members = await val.getChildren(); + if (!members) { + throw new EvaluationError('failed to get members of Value->val union'); } - /* - * We can not just cast `elements' to int* or Oid* - * due to padding in `union'. For these we iterate - * each element and evaluate each item independently - */ - const elements: RealVariable[] = []; - for (let i = 0; i < length; i++) { - const expression = `(${this.evaluateName})[${i}].${this.cellValue}`; - const response = await context.debug.evaluate(expression, this.frameId); - /* mimic array elements behaviour */ - elements.push(new RealVariable({ - name: `[${i}]`, - type: this.realType, - evaluateName: expression, - variablesReference: response.variablesReference, - value: response.result, - memoryReference: response.memoryReference, - frameId: this.frameId, - parent: this - }, this.logger)); + const str = members.find(v => v.name === 'str'); + if (!str) { + throw new EvaluationError('member Value->val.str not found'); } - return elements; - } - - async getChildren(context: ExecContext) { - return this.listParent.realNodeTag === 'List' - ? this.getNodeElements(context) - : this.getIntegerElements(context); - } + return utils.extractStringFromResult(str.value); + } else { + const sval = children.find(v => v.name === 'sval'); + if (!sval) { + throw new EvaluationError('member String->sval not found'); + } - protected isExpandable(): boolean { - return true; + return utils.extractStringFromResult(sval.value); } } } - -export class ArraySpecialMember extends RealVariable { - /** - * Expression to evaluate to obtain array length. - * Appended to target struct from right. - * First element is length member name, but after - * can be correction expressions i.e. '+ 1'. - */ - info: ArraySpecialMemberInfo; - parent: RealVariable; - - constructor(parent: RealVariable, info: ArraySpecialMemberInfo, args: RealVariableArgs, logger: utils.ILogger) { - super(args, logger); - this.info = info; - this.parent = parent; - } - - formatLengthExpression() { - return `(${this.parent.evaluateName})->${this.info.lengthExpr}`; - } - - formatMemberExpression() { - return `(${this.parent.evaluateName})->${this.info.memberName}`; - } - - async getChildren(context: ExecContext) { - const arrayLength = Number((await context.debug.evaluate(this.formatLengthExpression(), this.frameId)).result); - if (Number.isNaN(arrayLength)) { - this.logger.warn(`failed to obtain array size using ${this.formatLengthExpression()}`); +/** + * Represents Hash Table (HTAB) variable. + */ +class HTABSpecialMember extends RealVariable { + private static evaluationUsedFunctions = [ + 'hash_seq_init', + 'hash_seq_search', + 'hash_seq_term', + ]; + + async findEntryType(): Promise { + const map = this.context.hashTableTypes.htab.get(this.name); + if (!map) { return; } - if (arrayLength === 0) { + if (!this.parent) { return; } - const response = await context.debug.evaluate(`${this.formatMemberExpression()}, ${arrayLength}`, this.frameId); - const debugVariables = await context.debug.getMembers(response.variablesReference); - return (await Promise.all(debugVariables.map(dv => Variable.createVariable(dv, this.frameId, context, this.logger, this as RealVariable)))) - .filter(x => x !== undefined); - } -} - -class BitmapSetSpecialMember extends NodeTagVariable { - constructor(logger: utils.ILogger, args: RealVariableArgs) { - super('Bitmapset', args, logger); - } - - async isValidSet(debug: utils.IDebuggerFacade) { - const response = await debug.evaluate(`bms_is_valid_set(${this.evaluateName})`, this.frameId); - if (!response.type) { - /* `bms_is_valid_set' introduced in 17 */ - return true; + let parent; + if (this.parent instanceof VariablesRoot) { + parent = await this.debug.getFunctionName(this.frameId); + if (!parent) { + return; + } + } else { + parent = utils.getStructNameFromType(this.parent.type); } - return response.result === 'true'; - } - safeToObserve() { - if (vscode.debug.breakpoints.length === 0) { - return true; + const info = map.get(parent); + if (!info) { + return; } - /* - * Fastest way I found is just to iterate all breakpoints and check - * - no bp in bitmapset.c source code for line breakpoints - * - no bp for bms_next_member function for function breakpoints - * - * XXX I have found only these 2 classes of breakpoints. - */ + return info.type; + } + + safeToObserve(): boolean { for (const bp of vscode.debug.breakpoints) { if (!bp.enabled) { continue; @@ -775,187 +4234,364 @@ class BitmapSetSpecialMember extends NodeTagVariable { if (bp instanceof vscode.SourceBreakpoint) { if (bp.location.uri.path.endsWith('bitmapset.c')) { - this.logger.debug('found breakpoint at bitmapset.c - set elements not shown'); + this.logger.info('found breakpoint at bitmapset.c - set elements not shown'); return false; } } else if (bp instanceof vscode.FunctionBreakpoint) { - /* Need to check only bms_next_member */ - if (bp.functionName === 'bms_next_member') { - this.logger.debug('found breakpoint at bms_next_member - set elements not shown'); + /* + * Need to check functions that are called to get set elements + */ + if (HTABSpecialMember.evaluationUsedFunctions.indexOf(bp.functionName) !== -1) { + this.logger.info('found breakpoint at %s - bms elements not shown', + bp.functionName); return false; } } } + return true; } + async doGetChildren(): Promise { + const members = await super.doGetChildren(); + if (!members) { + return members; + } - async getSetMembers(debug: utils.IDebuggerFacade): Promise { - /* - * Must check we do not have breakpoints set in `bms_next_member`. - * Otherwise, we will get infinite recursion and backend will crash. - */ + /* Hope, validity check is not necessary */ if (!this.safeToObserve()) { - return; + return members; } - /* - * We MUST check validity of set, because otherwise - * `Assert` will fail and whole backend will crash - */ - if (!await this.isValidSet(debug)) { - return; + const entryType = await this.findEntryType(); + if (!entryType) { + return members; } - let number = -1; - const numbers = []; - do { - const response = await debug.evaluate(`bms_next_member(${this.evaluateName}, ${number})`, this.frameId); - number = Number(response.result); - if (Number.isNaN(number)) { - this.logger.warn(`failed to get set elements for ${this.name}`); - return; - } - if (number < 0) { - break; - } + members.push(new HTABElementsMember(this, entryType)); + return members; + } +} - numbers.push(number); - } while (number > 0); +/** + * Represents array of stored entries of Hash Table. + * Loaded lazily when member is expanded. + */ +class HTABElementsMember extends Variable { + /* + * Parent HTAB + */ + htab: HTABSpecialMember; - return numbers; - } + /** + * Type of entry of HTAB + */ + entryType: string; - async getChildren(context: ExecContext) { - /* All existing members */ - const members = await Variable.getVariables(this.variablesReference, this.frameId, context, this.logger, this); - if (members === undefined || members.length === 0) { - return; - } + constructor(htab: HTABSpecialMember, entryType: string) { + super('$elements$', '', '', htab.context, htab.frameId, htab, htab.logger); + this.htab = htab; + this.entryType = entryType; + } - /* Set members */ - const setMembers = await this.getSetMembers(context.debug); - if (setMembers === undefined) { - return members; + async getTreeItem() { + return { + label: '$elements$', + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, } + } - members.push(new ScalarVariable('$length$', setMembers.length.toString(), 'int', this)); - members.push(new BitmapSetSpecialMember.BmsArrayVariable(this, setMembers)); - return members; + private async createHashSeqStatus(): Promise { + /* + * HASH_SEQ_STATUS *status = palloc(sizeof(HASH_SEQ_STATUS)); + * hash_seq_init(status, htab); + */ + const memory = await this.palloc('sizeof(HASH_SEQ_STATUS)'); + await this.evaluate(`hash_seq_init((HASH_SEQ_STATUS *)${memory}, (HTAB *)${this.htab.value})`); + return memory; } - static isBmsType(type: string) { + private async finalizeHashSeqStatus(hashSeqStatus: string) { /* - * Valid bms always have single pointer, since it - * uses vla: no pointers - not allowed, more than 1 - * is not valid bms type (i.e. it is array) + * hash_seq_term(status); + * pfree(status); */ - switch (utils.getPointersCount(type)) { - case 0: - /* Relids */ - return utils.getStructNameFromType(type) === 'Relids'; - case 1: - /* Bitmapset * */ - return utils.getStructNameFromType(type) === 'Bitmapset'; - } - return false; + await this.evaluate(`hash_seq_term((HASH_SEQ_STATUS *)${hashSeqStatus})`); + await this.pfree(hashSeqStatus); } - static BmsElementVariable = class extends Variable { - constructor(index: number, value: number, parent: Variable) { - super(`[${index}]`, value.toString(), 'int', parent); + private async getNextHashEntry(hashSeqStatus: string): Promise { + const result = await this.evaluate(`hash_seq_search((HASH_SEQ_STATUS *)${hashSeqStatus})`); + if (!result) { + throw new EvaluationError('failed to get next hash table entry'); } - async getChildren(context: ExecContext): Promise { - return; + if (utils.isValidPointer(result.result)) { + return result.result; } - protected isExpandable(): boolean { - return false; + if (utils.isNull(result.result)) { + return undefined; } + + throw new EvaluationError(`Failed to get next hash table entry: ${result.result}`); } - static BmsArrayVariable = class extends Variable { - setElements: number[]; - constructor(parent: BitmapSetSpecialMember, setElements: number[]) { - super('$elements$', '', '', parent); - this.setElements = setElements; + async doGetChildren(): Promise { + const variables: Variable[] = []; + const hashSeqStatus = await this.createHashSeqStatus(); + + let entry; + while ((entry = await this.getNextHashEntry(hashSeqStatus))) { + const result = await this.evaluate(`(${this.entryType})${entry}`); + + /* user can specify non-existent type */ + if (utils.isFailedVar(result)) { + this.logger.warn('Failed to create variable with type %s: %s', this.entryType, result.result); + await this.finalizeHashSeqStatus(hashSeqStatus); + return undefined; + } + + let variable; + try { + variable = await Variable.create({ + ...result, + name: `${variables.length}`, + value: result.result, + evaluateName: `((${this.entryType})${entry})`, + }, this.frameId, this.context, this.logger, this); + } catch (error) { + await this.finalizeHashSeqStatus(hashSeqStatus); + throw error; + } + + if (variable) { + variables.push(variable); + } } - async getChildren(context: ExecContext): Promise { - return this.setElements.map((se, i) => new BitmapSetSpecialMember.BmsElementVariable(i, se, this)) + await this.pfree(hashSeqStatus); + + return variables; + } +} + +/** + * Represents simplehash hash table, created using '#include "lib/simplehash.h"' + */ +class SimplehashMember extends RealVariable { + /* + * Stores information about simple hash table: prefix for identifier names, + * type of entry and flag, indicating if it has facility to iterate over it. + */ + entry: SimplehashEntryInfo; + + get prefix(): string { + return this.entry.prefix; + } + + get elementType(): string { + return this.entry.elementType; + } + + constructor(entry: SimplehashEntryInfo, args: RealVariableArgs) { + super(args); + this.entry = entry; + } + + async doGetChildren(): Promise { + const members = await super.doGetChildren(); + if (!members) { + return members; } - protected isExpandable(): boolean { - return true; + members.push(new SimplehashElementsMember(this)); + return members; + } + + static looksLikeSimpleHashTable(type: string) { + const index = type.indexOf('_hash'); + + /* + * If there is no '_hash' in typename then + * this is definitely not simple hash table + */ + if (index === -1) { + return false; } - getTreeItem(): vscode.TreeItem { - return { - label: '$elements$', - collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, - } + /* + * Check this is last part of typename, i.e. not part of whole typename + */ + if (type.length < index + '_hash'.length) { + /* + * I assume, every hash table object is a pointer type, + * not allocated on stack. + */ + return false; } + + /* + * Next character after '_hash' must be non alphanumerical, + * so typename actually ends with '_hash'. + * In real life only available continuation is space or star. + */ + const nextChar = type[index + '_hash'.length]; + return nextChar === ' ' || nextChar === '*'; } -} + static getPrefix(struct: string) { + if (!struct.endsWith('_hash')) { + return undefined; + } -export function getWellKnownSpecialMembers(): ArraySpecialMemberInfo[] { - return constants.getArraySpecialMembers(); + return struct.substring(0, struct.length - '_hash'.length); + } } /** - * Create {@link NodeTagVariable SpecialMember} object with required type - * from parsed JSON object in settings file. - * If there is error occured (i.e. invalid configuration) - it will throw - * exception with message describing error. - * - * @param object parsed JSON object of special member from setting file + * Represents members of simplehash variable, stored in $elements$ */ -export function createArraySpecialMemberInfo(object: any): ArraySpecialMemberInfo { - let typeName = object.nodeTag; - if (!typeName) { - throw new Error("nodeTag field not provided"); +class SimplehashElementsMember extends Variable { + /* + * Parent simple hash table + */ + hashTable: SimplehashMember; + + constructor(hashTable: SimplehashMember) { + super('$elements$', '', '', hashTable.context, hashTable.frameId, hashTable, hashTable.logger); + this.hashTable = hashTable; } - if (typeof typeName !== 'string') { - throw new Error(`nodeTag type must be string, given: ${typeof typeName}`); + async getTreeItem(): Promise { + /* Show only '$elements$' */ + return { + label: '$elements$', + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + } } - typeName = typeName.trim().replace('T_', ''); + /* + * Cached identifier names for function and types + */ + hashTableType?: string = undefined; + iteratorType?: string = undefined; + iteratorFunction?: string = undefined; - /* NodeTag used also as type name, so it must be valid identifier */ - if (!utils.isValidIdentifier(typeName)) { - throw new Error(`nodeTag must be valid identifier. given: ${object.nodeTag}`); + private getHashTableType() { + return this.hashTableType ??= `${this.hashTable.prefix}_hash`; } - let arrayMemberName = object.memberName; - if (!arrayMemberName) { - throw new Error(`memberName field not provided for type with NodeTag: ${object.nodeTag}`); + private getIteratorFunction() { + return this.iteratorFunction ??= `${this.hashTable.prefix}_iterate`; } - if (typeof arrayMemberName !== 'string') { - throw new Error(`memberName field must be string for type with NodeTag: ${object.nodeTag}`); + private getIteratorType() { + return this.iteratorType ??= `${this.hashTable.prefix}_iterator`; } - arrayMemberName = arrayMemberName.trim(); - if (!utils.isValidIdentifier(arrayMemberName)) { - throw new Error(`memberName field ${arrayMemberName} is not valid identifier - contains invalid characters`) - } + /* + * Allocate memory for iterator struct and invoke initialization function on it. + */ + async createIterator() { + const iteratorType = this.getIteratorType(); + const hashTableType = this.getHashTableType(); + + /* + * Using 'sizeof' can be first filter to define wheter it has any iteration facility. + */ + let value; + try { + value = await this.palloc(`sizeof(${iteratorType})`); + } catch (error) { + if (error instanceof EvaluationError) { + this.hashTable.entry.canIterate = false; + return undefined; + } + throw error; + } + + /* 'start_iterate' seems not important to cache for optimization */ + const result = await this.evaluate(`${this.hashTable.prefix}_start_iterate((${hashTableType} *) ${this.hashTable.value}, (${iteratorType} *)${value})`) + if (utils.isFailedVar(result)) { + await this.pfree(value); + this.hashTable.entry.canIterate = false; + return undefined; + } - let lengthExpr = object.lengthExpression; - if (!lengthExpr) { - throw new Error(`lengthExpression not provided for: ${object.nodeTag}->${arrayMemberName}`); + return value; } - if (typeof lengthExpr !== 'string') { - throw new Error(`lengthExpression field must be string for: ${object.nodeTag}->${arrayMemberName}`); + async iterate(iterator: string, current: number) { + const iterFunction = this.getIteratorFunction(); + const hashTableType = this.getHashTableType(); + const iteratorType = this.getIteratorType(); + const result = await this.evaluate(`(${this.hashTable.elementType}) ${iterFunction}((${hashTableType} *) ${this.hashTable.value}, (${iteratorType} *)${iterator})`); + if (utils.isNull(result.result)) { + return undefined; + } + + if (utils.isFailedVar(result)) { + this.hashTable.entry.canIterate = false; + return undefined; + } + + try { + return await Variable.create({ + ...result, + name: `${current}`, + value: result.result, + evaluateName: `((${this.hashTable.elementType} *)${result.result})`, + }, this.frameId, this.context, this.logger, this); + } catch (error) { + await this.pfree(iterator); + throw error; + } } - lengthExpr = lengthExpr.trim(); - if (!lengthExpr) { - throw new Error('lengthExpression can not be empty string'); + async doGetChildren(): Promise { + /* + * Iteration pattern: + * + * SH_ITERATOR iterator; + * SH_ELEMENT_TYPE element; + * SH_START_ITERATE(table, &iterator); + * + * while ((element = SH_ITERATE(table, &iterator)) != NULL) + * { + * // Processing + * } + * + * + * NOTE: in contrast to HTAB there is no need to call to terminate + * iteration before end of iteration. + */ + + const iterator = await this.createIterator(); + if (!iterator) { + return; + } + + const variables = []; + let id = 0; + let variable; + while ((variable = await this.iterate(iterator, id))) { + ++id; + variables.push(variable); + } + + await this.pfree(iterator); + return variables; } +} - return { typeName, memberName: arrayMemberName, lengthExpr }; -} \ No newline at end of file +/** + * Get expression to fill in 'Watch' window in Debug view container. + * + * @param variable Instance of variable user clicked on + */ +export function getWatchExpressionCommandHandler(variable: any) { + return variable instanceof Variable + ? variable.getWatchExpression() + : null; +} diff --git a/tsconfig.json b/tsconfig.json index 6954702..51d5733 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { "compilerOptions": { "module": "Node16", - "target": "ES2022", + "target": "ES2015", "outDir": "out", "lib": [ - "ES2022" + "ES2015" ], "sourceMap": true, "rootDir": "src",