diff --git a/.github/workflows/dotnet-efcore-tests.yaml b/.github/workflows/dotnet-efcore-tests.yaml
new file mode 100644
index 00000000..4d8e04da
--- /dev/null
+++ b/.github/workflows/dotnet-efcore-tests.yaml
@@ -0,0 +1,35 @@
+name: .NET EF Core Tests
+
+on:
+ push:
+ branches: [master]
+ paths:
+ - 'dotnet/**'
+ pull_request:
+ branches: [master]
+ paths:
+ - 'dotnet/**'
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Restore dependencies
+ run: dotnet restore
+ working-directory: dotnet/sqlcommenter-efcore
+
+ - name: Build
+ run: dotnet build --no-restore
+ working-directory: dotnet/sqlcommenter-efcore
+
+ - name: Test
+ run: dotnet test --no-build --verbosity normal
+ working-directory: dotnet/sqlcommenter-efcore
diff --git a/.github/workflows/nodejs-drizzle-tests.yaml b/.github/workflows/nodejs-drizzle-tests.yaml
new file mode 100644
index 00000000..aadc6718
--- /dev/null
+++ b/.github/workflows/nodejs-drizzle-tests.yaml
@@ -0,0 +1,28 @@
+name: Nodejs Drizzle Tests
+
+on:
+ push:
+ branches:
+ - master
+ paths:
+ - nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/**
+ pull_request:
+ paths:
+ - nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/**
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "24"
+ cache: npm
+ cache-dependency-path: nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/package-lock.json
+ - run: npm install
+ working-directory: ./nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle
+ - run: npm run build
+ working-directory: ./nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle
+ - run: npm test
+ working-directory: ./nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle
diff --git a/.github/workflows/nodejs-mikroorm-tests.yaml b/.github/workflows/nodejs-mikroorm-tests.yaml
new file mode 100644
index 00000000..737045da
--- /dev/null
+++ b/.github/workflows/nodejs-mikroorm-tests.yaml
@@ -0,0 +1,28 @@
+name: Nodejs MikroORM Tests
+
+on:
+ push:
+ branches:
+ - master
+ paths:
+ - nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/**
+ pull_request:
+ paths:
+ - nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/**
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "24"
+ cache: npm
+ cache-dependency-path: nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/package-lock.json
+ - run: npm install
+ working-directory: ./nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm
+ - run: npm run build
+ working-directory: ./nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm
+ - run: npm test
+ working-directory: ./nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm
diff --git a/.github/workflows/nodejs-typeorm-tests.yaml b/.github/workflows/nodejs-typeorm-tests.yaml
new file mode 100644
index 00000000..69a5b050
--- /dev/null
+++ b/.github/workflows/nodejs-typeorm-tests.yaml
@@ -0,0 +1,28 @@
+name: Nodejs TypeORM Tests
+
+on:
+ push:
+ branches:
+ - master
+ paths:
+ - nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/**
+ pull_request:
+ paths:
+ - nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/**
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "24"
+ cache: npm
+ cache-dependency-path: nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package-lock.json
+ - run: npm install
+ working-directory: ./nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm
+ - run: npm run build
+ working-directory: ./nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm
+ - run: npm test
+ working-directory: ./nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm
diff --git a/.gitignore b/.gitignore
index c792cffb..7422d0d9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,12 @@
php/sqlcommenter-php/packages/sqlcommenter-laravel/vendor/*
.idea/**
.DS_Store
+
+# .NET
+bin/
+obj/
+*.user
+*.suo
+.vs/
+*.nupkg
+*.snupkg
diff --git a/README.md b/README.md
index 4246d7c5..f07096c7 100644
--- a/README.md
+++ b/README.md
@@ -18,5 +18,10 @@ Contains all the various `sqlcommenter-*` implementations.
- [X] [Node.js](nodejs/sqlcommenter-nodejs/README.md)
- [X] [Knex.js](nodejs/sqlcommenter-nodejs/packages/sqlcommenter-knex/README.md)
- [X] [Sequelize.js](nodejs/sqlcommenter-nodejs/packages/sqlcommenter-sequelize/README.md)
+ - [X] [Drizzle](nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/README.md)
+ - [X] [TypeORM](nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/README.md)
+ - [X] [MikroORM](nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/README.md)
- [X] Php
- [X] [Laravel](php/sqlcommenter-php/packages/sqlcommenter-laravel)
+- [X] .NET
+ - [X] [EF Core](dotnet/sqlcommenter-efcore/README.md)
diff --git a/dotnet/sqlcommenter-efcore/.editorconfig b/dotnet/sqlcommenter-efcore/.editorconfig
new file mode 100644
index 00000000..95cae9a5
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/.editorconfig
@@ -0,0 +1,18 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{csproj,props,targets}]
+indent_size = 2
+
+[*.{json,yml,yaml}]
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/dotnet/sqlcommenter-efcore/.gitignore b/dotnet/sqlcommenter-efcore/.gitignore
new file mode 100644
index 00000000..85dc857b
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/.gitignore
@@ -0,0 +1,7 @@
+bin/
+obj/
+*.user
+*.suo
+.vs/
+*.nupkg
+*.snupkg
diff --git a/dotnet/sqlcommenter-efcore/LICENSE b/dotnet/sqlcommenter-efcore/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/dotnet/sqlcommenter-efcore/README.md b/dotnet/sqlcommenter-efcore/README.md
new file mode 100644
index 00000000..d47b073c
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/README.md
@@ -0,0 +1,69 @@
+# QueryDoctor.SqlCommenter.EFCore
+
+EF Core interceptor that appends [SQLCommenter](https://google.github.io/sqlcommenter/)-formatted comments to SQL queries for query attribution and observability.
+
+## Installation
+
+```bash
+dotnet add package QueryDoctor.SqlCommenter.EFCore
+```
+
+## Quick Start
+
+```csharp
+// In your DbContext configuration or Program.cs
+options.UseSqlServer(connectionString).AddSqlCommenter();
+```
+
+This will automatically append comments to all SQL queries:
+
+```sql
+SELECT * FROM "Users" WHERE "Id" = @p0 /*action='GetUser',controller='Users',db_driver='efcore',file='Controllers%2FUsersController.cs%3A42%3A1',framework='efcore%3A8.0',method='GET'*/
+```
+
+## Tags
+
+| Tag | Description | Example |
+|-----|-------------|---------|
+| `action` | Controller action or method name | `GetUser` |
+| `controller` | Controller name (without suffix) | `Users` |
+| `db_driver` | Database driver identifier | `efcore` |
+| `file` | Source file location (path:line:column) | `Controllers/UsersController.cs:42:1` |
+| `framework` | EF Core version | `efcore:8.0` |
+| `method` | HTTP method | `GET` |
+
+## Configuration
+
+```csharp
+options.UseSqlServer(connectionString).AddSqlCommenter(o =>
+{
+ o.Enabled = true; // Enable/disable (default: true)
+ o.EnableStackInspection = true; // Auto-detect caller info (default: true)
+ o.MaxStackDepth = 30; // Max stack frames to inspect (default: 30)
+ o.IncludeFrameworkVersion = true; // Include EF Core version (default: true)
+});
+```
+
+## Explicit Context
+
+For precise control, use `QueryTaggingContext.SetContext()` instead of stack inspection:
+
+```csharp
+using (QueryTaggingContext.SetContext(action: "GetUser", controller: "Users"))
+{
+ var user = await context.Users.FindAsync(id);
+}
+```
+
+Explicit context always takes priority over stack inspection. Caller file path, line number, and member name are automatically captured via `[CallerFilePath]`, `[CallerLineNumber]`, and `[CallerMemberName]`.
+
+## Spec Compliance
+
+- Keys are sorted lexicographically
+- Values are URL-encoded and wrapped in single quotes
+- SQL with existing comments (`/*` or `*/`) is not modified
+- Errors during tagging never fail the query
+
+## License
+
+Apache-2.0
diff --git a/dotnet/sqlcommenter-efcore/sqlcommenter-efcore.sln b/dotnet/sqlcommenter-efcore/sqlcommenter-efcore.sln
new file mode 100644
index 00000000..fe132516
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/sqlcommenter-efcore.sln
@@ -0,0 +1,36 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C1494A41-1586-4710-86EB-17134121B934}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QueryDoctor.SqlCommenter.EFCore", "src\QueryDoctor.SqlCommenter.EFCore\QueryDoctor.SqlCommenter.EFCore.csproj", "{7FD1588E-7B09-4401-9FA0-61E6D6BC1018}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{78DB0588-93C0-4BB5-8810-A5DA2D88D0FA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QueryDoctor.SqlCommenter.EFCore.Tests", "test\QueryDoctor.SqlCommenter.EFCore.Tests\QueryDoctor.SqlCommenter.EFCore.Tests.csproj", "{B42680B7-4FBF-4B32-A66D-9781014C232D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7FD1588E-7B09-4401-9FA0-61E6D6BC1018}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7FD1588E-7B09-4401-9FA0-61E6D6BC1018}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7FD1588E-7B09-4401-9FA0-61E6D6BC1018}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7FD1588E-7B09-4401-9FA0-61E6D6BC1018}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B42680B7-4FBF-4B32-A66D-9781014C232D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B42680B7-4FBF-4B32-A66D-9781014C232D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B42680B7-4FBF-4B32-A66D-9781014C232D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B42680B7-4FBF-4B32-A66D-9781014C232D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {7FD1588E-7B09-4401-9FA0-61E6D6BC1018} = {C1494A41-1586-4710-86EB-17134121B934}
+ {B42680B7-4FBF-4B32-A66D-9781014C232D} = {78DB0588-93C0-4BB5-8810-A5DA2D88D0FA}
+ EndGlobalSection
+EndGlobal
diff --git a/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/DbContextOptionsBuilderExtensions.cs b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/DbContextOptionsBuilderExtensions.cs
new file mode 100644
index 00000000..c5315ffe
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/DbContextOptionsBuilderExtensions.cs
@@ -0,0 +1,25 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace QueryDoctor.SqlCommenter.EFCore;
+
+///
+/// Extension methods for to register the SQLCommenter interceptor.
+///
+public static class DbContextOptionsBuilderExtensions
+{
+ ///
+ /// Adds the SQLCommenter interceptor to the DbContext options.
+ ///
+ /// The DbContext options builder.
+ /// Optional delegate to configure .
+ /// The same for chaining.
+ public static DbContextOptionsBuilder AddSqlCommenter(
+ this DbContextOptionsBuilder optionsBuilder,
+ Action? configureOptions = null)
+ {
+ var options = new SqlCommenterOptions();
+ configureOptions?.Invoke(options);
+ optionsBuilder.AddInterceptors(new SqlCommenterInterceptor(options));
+ return optionsBuilder;
+ }
+}
diff --git a/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/QueryDoctor.SqlCommenter.EFCore.csproj b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/QueryDoctor.SqlCommenter.EFCore.csproj
new file mode 100644
index 00000000..ac3b319d
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/QueryDoctor.SqlCommenter.EFCore.csproj
@@ -0,0 +1,39 @@
+
+
+
+ net8.0
+ enable
+ enable
+ QueryDoctor.SqlCommenter.EFCore
+
+
+ QueryDoctor.SqlCommenter.EFCore
+ 8.0.0
+ Query Doctor
+ Query Doctor
+ EF Core interceptor that appends SQLCommenter-formatted comments to SQL queries for query attribution and observability.
+ sqlcommenter;efcore;entity-framework-core;sql;observability
+ Apache-2.0
+ https://github.com/query-doctor/sqlcommenter
+ https://github.com/query-doctor/sqlcommenter.git
+ git
+ README.md
+ true
+
+
+ true
+ true
+ true
+ snupkg
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/QueryTagInfo.cs b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/QueryTagInfo.cs
new file mode 100644
index 00000000..47191a06
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/QueryTagInfo.cs
@@ -0,0 +1,116 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace QueryDoctor.SqlCommenter.EFCore;
+
+///
+/// Holds information about the query origin for SQLCommenter tagging.
+///
+public sealed class QueryTagInfo
+{
+ /// Source file path where the query originated.
+ public string? FilePath { get; init; }
+
+ /// Line number in the source file.
+ public int LineNumber { get; init; }
+
+ /// Column number in the source file. Defaults to 1 if not available.
+ public int ColumnNumber { get; init; }
+
+ /// Name of the method that initiated the query.
+ public string? MemberName { get; init; }
+
+ /// Controller action name (for ASP.NET Core controllers).
+ public string? Action { get; init; }
+
+ /// Controller name without the "Controller" suffix.
+ public string? Controller { get; init; }
+
+ /// HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS).
+ public string? HttpMethod { get; init; }
+
+ ///
+ /// Formats the tag info as a SQLCommenter-compatible comment.
+ /// Keys are sorted lexicographically per the spec.
+ ///
+ /// Whether to include the EF Core framework version tag.
+ /// A SQL comment string, or empty string if no tags are available.
+ public string ToSqlComment(bool includeFrameworkVersion = true)
+ {
+ var pairs = new SortedDictionary(StringComparer.Ordinal);
+
+ if (!string.IsNullOrEmpty(Action))
+ {
+ pairs["action"] = Action;
+ }
+
+ if (!string.IsNullOrEmpty(Controller))
+ {
+ pairs["controller"] = Controller;
+ }
+
+ pairs["db_driver"] = "efcore";
+
+ if (!string.IsNullOrEmpty(FilePath))
+ {
+ var normalizedPath = NormalizePath(FilePath);
+ var column = ColumnNumber > 0 ? ColumnNumber : 1;
+ var location = LineNumber > 0
+ ? $"{normalizedPath}:{LineNumber}:{column}"
+ : normalizedPath;
+ pairs["file"] = location;
+ }
+
+ if (includeFrameworkVersion)
+ {
+ var efCoreVersion = typeof(DbContext).Assembly.GetName().Version;
+ var major = efCoreVersion?.Major ?? 8;
+ var minor = efCoreVersion?.Minor ?? 0;
+ pairs["framework"] = $"efcore:{major}.{minor}";
+ }
+
+ if (!string.IsNullOrEmpty(HttpMethod))
+ {
+ pairs["method"] = HttpMethod;
+ }
+
+ if (pairs.Count == 0)
+ return string.Empty;
+
+ var formattedPairs = pairs.Select(kvp => $"{EncodeKey(kvp.Key)}={EncodeValue(kvp.Value)}");
+ return $"/*{string.Join(",", formattedPairs)}*/";
+ }
+
+ private static string NormalizePath(string path)
+ {
+ var srcIndex = path.IndexOf("src/", StringComparison.OrdinalIgnoreCase);
+ if (srcIndex == -1)
+ {
+ srcIndex = path.IndexOf("src\\", StringComparison.OrdinalIgnoreCase);
+ }
+
+ if (srcIndex >= 0)
+ {
+ return path[(srcIndex + 4)..].Replace('\\', '/');
+ }
+
+ return Path.GetFileName(path);
+ }
+
+ private static string EncodeKey(string key)
+ {
+ var encoded = Uri.EscapeDataString(key);
+ return EscapeMetaCharacters(encoded);
+ }
+
+ private static string EncodeValue(string value)
+ {
+ var urlEncoded = Uri.EscapeDataString(value);
+ var escaped = urlEncoded.Replace("'", "\\'");
+ return $"'{escaped}'";
+ }
+
+ private static string EscapeMetaCharacters(string value)
+ {
+ return value.Replace("\\", "\\\\").Replace("'", "\\'");
+ }
+}
diff --git a/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/QueryTaggingContext.cs b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/QueryTaggingContext.cs
new file mode 100644
index 00000000..906b20c3
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/QueryTaggingContext.cs
@@ -0,0 +1,68 @@
+using System.Runtime.CompilerServices;
+
+namespace QueryDoctor.SqlCommenter.EFCore;
+
+///
+/// Provides ambient context for SQL query tagging following SQLCommenter format.
+/// Uses to flow context across async/await boundaries.
+///
+public static class QueryTaggingContext
+{
+ private static readonly AsyncLocal _currentTag = new();
+
+ ///
+ /// Gets or sets the current query tag info for the async context.
+ /// Returns null if no context has been set.
+ ///
+ public static QueryTagInfo? Current
+ {
+ get => _currentTag.Value;
+ set => _currentTag.Value = value;
+ }
+
+ ///
+ /// Sets the query context with caller information.
+ /// Caller attributes are automatically captured by the compiler.
+ ///
+ /// The action name (e.g., controller action).
+ /// The controller name.
+ /// Automatically captured source file path.
+ /// Automatically captured source line number.
+ /// Automatically captured calling member name.
+ /// An that restores the previous context when disposed.
+ public static IDisposable SetContext(
+ string? action = null,
+ string? controller = null,
+ [CallerFilePath] string? filePath = null,
+ [CallerLineNumber] int lineNumber = 0,
+ [CallerMemberName] string? memberName = null)
+ {
+ var previous = _currentTag.Value;
+ _currentTag.Value = new QueryTagInfo
+ {
+ FilePath = filePath,
+ LineNumber = lineNumber,
+ MemberName = memberName,
+ Action = action,
+ Controller = controller
+ };
+ return new ContextScope(previous);
+ }
+
+ private sealed class ContextScope : IDisposable
+ {
+ private readonly QueryTagInfo? _previous;
+ private bool _disposed;
+
+ public ContextScope(QueryTagInfo? previous) => _previous = previous;
+
+ public void Dispose()
+ {
+ if (!_disposed)
+ {
+ _currentTag.Value = _previous;
+ _disposed = true;
+ }
+ }
+ }
+}
diff --git a/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/SqlCommenterInterceptor.cs b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/SqlCommenterInterceptor.cs
new file mode 100644
index 00000000..3d71a65d
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/SqlCommenterInterceptor.cs
@@ -0,0 +1,217 @@
+using System.Data.Common;
+using System.Diagnostics;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+
+namespace QueryDoctor.SqlCommenter.EFCore;
+
+///
+/// EF Core command interceptor that appends SQLCommenter-formatted comments to queries.
+/// Comments include source file location and method name for debugging and profiling.
+///
+public class SqlCommenterInterceptor : DbCommandInterceptor
+{
+ private readonly SqlCommenterOptions _options;
+
+ private static readonly HashSet _frameworkAssemblyPrefixes =
+ [
+ "Microsoft.EntityFrameworkCore",
+ "Npgsql",
+ "System.",
+ "Microsoft.Extensions",
+ "Microsoft.AspNetCore"
+ ];
+
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// Configuration options. If null , default options are used.
+ public SqlCommenterInterceptor(SqlCommenterOptions? options = null)
+ {
+ _options = options ?? new SqlCommenterOptions();
+ }
+
+ ///
+ public override InterceptionResult ReaderExecuting(
+ DbCommand command, CommandEventData eventData, InterceptionResult result)
+ {
+ AppendSqlComment(command);
+ return base.ReaderExecuting(command, eventData, result);
+ }
+
+ ///
+ public override ValueTask> ReaderExecutingAsync(
+ DbCommand command, CommandEventData eventData, InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ {
+ AppendSqlComment(command);
+ return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
+ }
+
+ ///
+ public override InterceptionResult NonQueryExecuting(
+ DbCommand command, CommandEventData eventData, InterceptionResult result)
+ {
+ AppendSqlComment(command);
+ return base.NonQueryExecuting(command, eventData, result);
+ }
+
+ ///
+ public override ValueTask> NonQueryExecutingAsync(
+ DbCommand command, CommandEventData eventData, InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ {
+ AppendSqlComment(command);
+ return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);
+ }
+
+ ///
+ public override InterceptionResult ScalarExecuting(
+ DbCommand command, CommandEventData eventData, InterceptionResult result)
+ {
+ AppendSqlComment(command);
+ return base.ScalarExecuting(command, eventData, result);
+ }
+
+ ///
+ public override ValueTask> ScalarExecutingAsync(
+ DbCommand command, CommandEventData eventData, InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ {
+ AppendSqlComment(command);
+ return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
+ }
+
+ private void AppendSqlComment(DbCommand command)
+ {
+ if (!_options.Enabled)
+ return;
+
+ if (command.CommandText.Contains("/*") || command.CommandText.Contains("*/"))
+ return;
+
+ try
+ {
+ var tagInfo = GetTagInfo();
+ if (tagInfo != null)
+ {
+ var comment = tagInfo.ToSqlComment(_options.IncludeFrameworkVersion);
+ if (!string.IsNullOrEmpty(comment))
+ {
+ command.CommandText = $"{command.CommandText} {comment}";
+ }
+ }
+ }
+ catch
+ {
+ // Never fail a query due to tagging errors
+ }
+ }
+
+ private QueryTagInfo? GetTagInfo()
+ {
+ var explicitContext = QueryTaggingContext.Current;
+ if (explicitContext != null)
+ return explicitContext;
+
+ if (_options.EnableStackInspection)
+ return InspectStackForCallerInfo();
+
+ return null;
+ }
+
+ private QueryTagInfo? InspectStackForCallerInfo()
+ {
+ try
+ {
+ var stackTrace = new StackTrace(fNeedFileInfo: true);
+ var frames = stackTrace.GetFrames();
+
+ if (frames == null)
+ return null;
+
+ for (int i = 0; i < Math.Min(frames.Length, _options.MaxStackDepth); i++)
+ {
+ var frame = frames[i];
+ var method = frame.GetMethod();
+ if (method == null) continue;
+
+ var declaringType = method.DeclaringType;
+ if (declaringType == null) continue;
+
+ var assemblyName = declaringType.Assembly.GetName().Name;
+ if (assemblyName == null) continue;
+
+ if (IsFrameworkAssembly(assemblyName)) continue;
+ if (declaringType.Namespace?.StartsWith("QueryDoctor.SqlCommenter.EFCore") == true) continue;
+
+ var fileName = frame.GetFileName();
+ var lineNumber = frame.GetFileLineNumber();
+ var columnNumber = frame.GetFileColumnNumber();
+
+ return new QueryTagInfo
+ {
+ FilePath = fileName,
+ LineNumber = lineNumber,
+ ColumnNumber = columnNumber > 0 ? columnNumber : 1,
+ MemberName = method.Name,
+ Controller = GetControllerName(declaringType),
+ Action = GetActionName(declaringType, method),
+ HttpMethod = GetHttpMethod(method)
+ };
+ }
+ }
+ catch
+ {
+ // Stack inspection can fail in various scenarios
+ }
+
+ return null;
+ }
+
+ private static bool IsFrameworkAssembly(string assemblyName)
+ {
+ foreach (var prefix in _frameworkAssemblyPrefixes)
+ {
+ if (assemblyName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ return true;
+ }
+ return false;
+ }
+
+ private static string? GetControllerName(Type type)
+ {
+ var name = type.Name;
+ if (name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
+ return name[..^"Controller".Length];
+ return null;
+ }
+
+ private static string? GetActionName(Type declaringType, System.Reflection.MethodBase method)
+ {
+ if (!declaringType.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
+ return null;
+ return method.Name;
+ }
+
+ private static string? GetHttpMethod(System.Reflection.MethodBase method)
+ {
+ var attrs = method.GetCustomAttributes(false);
+ foreach (var attr in attrs)
+ {
+ var attrName = attr.GetType().Name;
+ var httpMethod = attrName switch
+ {
+ "HttpGetAttribute" => "GET",
+ "HttpPostAttribute" => "POST",
+ "HttpPutAttribute" => "PUT",
+ "HttpDeleteAttribute" => "DELETE",
+ "HttpPatchAttribute" => "PATCH",
+ "HttpHeadAttribute" => "HEAD",
+ "HttpOptionsAttribute" => "OPTIONS",
+ _ => null
+ };
+ if (httpMethod != null) return httpMethod;
+ }
+ return null;
+ }
+}
diff --git a/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/SqlCommenterOptions.cs b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/SqlCommenterOptions.cs
new file mode 100644
index 00000000..eb84384f
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/src/QueryDoctor.SqlCommenter.EFCore/SqlCommenterOptions.cs
@@ -0,0 +1,30 @@
+namespace QueryDoctor.SqlCommenter.EFCore;
+
+///
+/// Configuration options for the SQLCommenter interceptor.
+///
+public class SqlCommenterOptions
+{
+ ///
+ /// Whether SQLCommenter tagging is enabled. Default: true.
+ ///
+ public bool Enabled { get; set; } = true;
+
+ ///
+ /// Whether to inspect the call stack for caller information
+ /// when no explicit context is set. Default: true.
+ ///
+ public bool EnableStackInspection { get; set; } = true;
+
+ ///
+ /// Maximum stack depth to inspect. Higher values provide
+ /// better accuracy but increase overhead. Default: 30.
+ ///
+ public int MaxStackDepth { get; set; } = 30;
+
+ ///
+ /// Whether to include the EF Core framework version in comments.
+ /// Default: true.
+ ///
+ public bool IncludeFrameworkVersion { get; set; } = true;
+}
diff --git a/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/DbContextOptionsBuilderExtensionsTests.cs b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/DbContextOptionsBuilderExtensionsTests.cs
new file mode 100644
index 00000000..19073b42
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/DbContextOptionsBuilderExtensionsTests.cs
@@ -0,0 +1,28 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+
+namespace QueryDoctor.SqlCommenter.EFCore.Tests;
+
+public class DbContextOptionsBuilderExtensionsTests
+{
+ [Fact]
+ public void AddSqlCommenter_RegistersInterceptor()
+ {
+ var optionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase("test_register");
+ optionsBuilder.AddSqlCommenter();
+ var options = optionsBuilder.Options;
+ var extension = options.FindExtension();
+ Assert.NotNull(extension);
+ Assert.NotNull(extension.Interceptors);
+ Assert.Contains(extension.Interceptors, i => i is SqlCommenterInterceptor);
+ }
+
+ [Fact]
+ public void AddSqlCommenter_CustomOptions_InvokesDelegate()
+ {
+ var optionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase("test_custom");
+ var delegateInvoked = false;
+ optionsBuilder.AddSqlCommenter(o => { delegateInvoked = true; o.Enabled = false; });
+ Assert.True(delegateInvoked);
+ }
+}
diff --git a/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/GlobalUsings.cs b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/GlobalUsings.cs
new file mode 100644
index 00000000..c802f448
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/GlobalUsings.cs
@@ -0,0 +1 @@
+global using Xunit;
diff --git a/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/QueryDoctor.SqlCommenter.EFCore.Tests.csproj b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/QueryDoctor.SqlCommenter.EFCore.Tests.csproj
new file mode 100644
index 00000000..726215a4
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/QueryDoctor.SqlCommenter.EFCore.Tests.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net8.0
+ enable
+ enable
+ false
+ true
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/QueryTagInfoTests.cs b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/QueryTagInfoTests.cs
new file mode 100644
index 00000000..298174b0
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/QueryTagInfoTests.cs
@@ -0,0 +1,118 @@
+namespace QueryDoctor.SqlCommenter.EFCore.Tests;
+
+public class QueryTagInfoTests
+{
+ [Fact]
+ public void ToSqlComment_WithAllFields_ProducesCorrectFormat()
+ {
+ var info = new QueryTagInfo
+ {
+ Action = "GetItems", Controller = "Items",
+ FilePath = "/app/src/Controllers/ItemsController.cs",
+ LineNumber = 42, ColumnNumber = 5, HttpMethod = "GET"
+ };
+ var comment = info.ToSqlComment();
+ Assert.StartsWith("/*", comment);
+ Assert.EndsWith("*/", comment);
+ Assert.Contains("action='GetItems'", comment);
+ Assert.Contains("controller='Items'", comment);
+ Assert.Contains("db_driver='efcore'", comment);
+ Assert.Contains("method='GET'", comment);
+ }
+
+ [Fact]
+ public void ToSqlComment_MinimalFields_ProducesOnlyDbDriver()
+ {
+ var info = new QueryTagInfo();
+ var comment = info.ToSqlComment(includeFrameworkVersion: false);
+ Assert.Equal("/*db_driver='efcore'*/", comment);
+ }
+
+ [Fact]
+ public void ToSqlComment_DbDriverIsEfcore()
+ {
+ var info = new QueryTagInfo();
+ var comment = info.ToSqlComment(includeFrameworkVersion: false);
+ Assert.Contains("db_driver='efcore'", comment);
+ }
+
+ [Fact]
+ public void ToSqlComment_KeysSortedLexicographically()
+ {
+ var info = new QueryTagInfo { Action = "Get", Controller = "Test", HttpMethod = "GET" };
+ var comment = info.ToSqlComment(includeFrameworkVersion: false);
+ var actionIdx = comment.IndexOf("action=");
+ var controllerIdx = comment.IndexOf("controller=");
+ var dbDriverIdx = comment.IndexOf("db_driver=");
+ var methodIdx = comment.IndexOf("method=");
+ Assert.True(actionIdx < controllerIdx);
+ Assert.True(controllerIdx < dbDriverIdx);
+ Assert.True(dbDriverIdx < methodIdx);
+ }
+
+ [Fact]
+ public void ToSqlComment_ValuesAreUrlEncoded()
+ {
+ var info = new QueryTagInfo { FilePath = "/app/src/path with spaces/file.cs", LineNumber = 1 };
+ var comment = info.ToSqlComment(includeFrameworkVersion: false);
+ Assert.Contains("%20", comment);
+ Assert.DoesNotContain("path with spaces", comment);
+ }
+
+ [Fact]
+ public void ToSqlComment_FilePathNormalization_StripsSrcPrefix()
+ {
+ var info = new QueryTagInfo { FilePath = "/home/user/project/src/Controllers/TestController.cs", LineNumber = 10, ColumnNumber = 1 };
+ var comment = info.ToSqlComment(includeFrameworkVersion: false);
+ Assert.Contains("Controllers%2FTestController.cs", comment);
+ Assert.DoesNotContain("/home/user/project", comment);
+ }
+
+ [Fact]
+ public void ToSqlComment_FilePathNormalization_WindowsPaths()
+ {
+ var info = new QueryTagInfo { FilePath = "C:\\Users\\dev\\project\\src\\Controllers\\TestController.cs", LineNumber = 10, ColumnNumber = 1 };
+ var comment = info.ToSqlComment(includeFrameworkVersion: false);
+ Assert.Contains("Controllers%2FTestController.cs", comment);
+ }
+
+ [Fact]
+ public void ToSqlComment_FilePathNormalization_NoSrcFolder_UsesFilename()
+ {
+ var info = new QueryTagInfo { FilePath = "/home/user/project/Controllers/TestController.cs", LineNumber = 10, ColumnNumber = 1 };
+ var comment = info.ToSqlComment(includeFrameworkVersion: false);
+ Assert.Contains("TestController.cs", comment);
+ }
+
+ [Fact]
+ public void ToSqlComment_FileLocation_IncludesLineAndColumn()
+ {
+ var info = new QueryTagInfo { FilePath = "/app/src/Test.cs", LineNumber = 42, ColumnNumber = 7 };
+ var comment = info.ToSqlComment(includeFrameworkVersion: false);
+ Assert.Contains("Test.cs%3A42%3A7", comment);
+ }
+
+ [Fact]
+ public void ToSqlComment_DefaultColumnIsOne()
+ {
+ var info = new QueryTagInfo { FilePath = "/app/src/Test.cs", LineNumber = 42, ColumnNumber = 0 };
+ var comment = info.ToSqlComment(includeFrameworkVersion: false);
+ Assert.Contains("Test.cs%3A42%3A1", comment);
+ }
+
+ [Fact]
+ public void ToSqlComment_WithFrameworkVersion_IncludesFrameworkTag()
+ {
+ var info = new QueryTagInfo();
+ var comment = info.ToSqlComment(includeFrameworkVersion: true);
+ Assert.Contains("framework='efcore%3A", comment);
+ }
+
+ [Fact]
+ public void ToSqlComment_WithoutFrameworkVersion_OmitsFrameworkTag()
+ {
+ var info = new QueryTagInfo();
+ var comment = info.ToSqlComment(includeFrameworkVersion: false);
+ Assert.DoesNotContain("framework=", comment);
+ }
+}
diff --git a/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/QueryTaggingContextTests.cs b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/QueryTaggingContextTests.cs
new file mode 100644
index 00000000..b479b385
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/QueryTaggingContextTests.cs
@@ -0,0 +1,75 @@
+namespace QueryDoctor.SqlCommenter.EFCore.Tests;
+
+public class QueryTaggingContextTests
+{
+ [Fact]
+ public void Current_DefaultsToNull()
+ {
+ QueryTaggingContext.Current = null;
+ Assert.Null(QueryTaggingContext.Current);
+ }
+
+ [Fact]
+ public void SetContext_SetsCurrentValue()
+ {
+ using var scope = QueryTaggingContext.SetContext(action: "Test");
+ Assert.NotNull(QueryTaggingContext.Current);
+ Assert.Equal("Test", QueryTaggingContext.Current!.Action);
+ }
+
+ [Fact]
+ public void SetContext_Dispose_RestoresPrevious()
+ {
+ QueryTaggingContext.Current = null;
+ using (QueryTaggingContext.SetContext(action: "Test"))
+ { Assert.NotNull(QueryTaggingContext.Current); }
+ Assert.Null(QueryTaggingContext.Current);
+ }
+
+ [Fact]
+ public void SetContext_NestedScopes_RestoresCorrectly()
+ {
+ QueryTaggingContext.Current = null;
+ using (QueryTaggingContext.SetContext(action: "Outer"))
+ {
+ Assert.Equal("Outer", QueryTaggingContext.Current!.Action);
+ using (QueryTaggingContext.SetContext(action: "Inner"))
+ { Assert.Equal("Inner", QueryTaggingContext.Current!.Action); }
+ Assert.Equal("Outer", QueryTaggingContext.Current!.Action);
+ }
+ Assert.Null(QueryTaggingContext.Current);
+ }
+
+ [Fact]
+ public void SetContext_CapturesCallerFilePath()
+ {
+ using var scope = QueryTaggingContext.SetContext();
+ Assert.NotNull(QueryTaggingContext.Current);
+ Assert.NotNull(QueryTaggingContext.Current!.FilePath);
+ Assert.Contains("QueryTaggingContextTests.cs", QueryTaggingContext.Current.FilePath);
+ }
+
+ [Fact]
+ public void SetContext_CapturesCallerLineNumber()
+ {
+ using var scope = QueryTaggingContext.SetContext();
+ Assert.NotNull(QueryTaggingContext.Current);
+ Assert.True(QueryTaggingContext.Current!.LineNumber > 0);
+ }
+
+ [Fact]
+ public void SetContext_CapturesCallerMemberName()
+ {
+ using var scope = QueryTaggingContext.SetContext();
+ Assert.NotNull(QueryTaggingContext.Current);
+ Assert.Equal("SetContext_CapturesCallerMemberName", QueryTaggingContext.Current!.MemberName);
+ }
+
+ [Fact]
+ public void DoubleDispose_DoesNotThrow()
+ {
+ var scope = QueryTaggingContext.SetContext(action: "Test");
+ scope.Dispose();
+ scope.Dispose();
+ }
+}
diff --git a/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/SqlCommenterIntegrationTests.cs b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/SqlCommenterIntegrationTests.cs
new file mode 100644
index 00000000..5fdddd8b
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/SqlCommenterIntegrationTests.cs
@@ -0,0 +1,108 @@
+using System.Data.Common;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+
+namespace QueryDoctor.SqlCommenter.EFCore.Tests;
+
+public class SqlCommenterIntegrationTests : IDisposable
+{
+ private readonly SqliteConnection _connection;
+
+ public SqlCommenterIntegrationTests()
+ {
+ _connection = new SqliteConnection("Data Source=:memory:");
+ _connection.Open();
+ }
+
+ public void Dispose() => _connection.Dispose();
+
+ private (TestDbContext context, SqlCapturingInterceptor captor) CreateContext(
+ Action? configure = null)
+ {
+ var captor = new SqlCapturingInterceptor();
+ var optionsBuilder = new DbContextOptionsBuilder().UseSqlite(_connection);
+ if (configure != null) optionsBuilder.AddSqlCommenter(configure);
+ else optionsBuilder.AddSqlCommenter(o => o.EnableStackInspection = false);
+ optionsBuilder.AddInterceptors(captor);
+ var context = new TestDbContext(optionsBuilder.Options);
+ context.Database.EnsureCreated();
+ captor.CapturedCommands.Clear();
+ return (context, captor);
+ }
+
+ [Fact]
+ public async Task RealQuery_AppendsDbDriverTag()
+ {
+ var (context, captor) = CreateContext();
+ using (QueryTaggingContext.SetContext(action: "Query"))
+ { await context.Items.ToListAsync(); }
+ Assert.NotEmpty(captor.CapturedCommands);
+ var sql = captor.CapturedCommands.First(c => c.Contains("Items"));
+ Assert.Contains("db_driver='efcore'", sql);
+ }
+
+ [Fact]
+ public async Task RealQuery_WithExplicitContext_IncludesControllerAndAction()
+ {
+ var (context, captor) = CreateContext(o => o.EnableStackInspection = false);
+ using (QueryTaggingContext.SetContext(action: "List", controller: "Items"))
+ { await context.Items.ToListAsync(); }
+ var sql = captor.CapturedCommands.First(c => c.Contains("Items"));
+ Assert.Contains("action='List'", sql);
+ Assert.Contains("controller='Items'", sql);
+ }
+
+ [Fact]
+ public async Task Disabled_DoesNotAppendToRealQuery()
+ {
+ var (context, captor) = CreateContext(o => o.Enabled = false);
+ using (QueryTaggingContext.SetContext(action: "Test"))
+ { await context.Items.ToListAsync(); }
+ var sql = captor.CapturedCommands.First(c => c.Contains("Items"));
+ Assert.DoesNotContain("/*", sql);
+ }
+
+ [Fact]
+ public async Task RealInsert_AppendsComment()
+ {
+ var (context, captor) = CreateContext(o => o.EnableStackInspection = false);
+ using (QueryTaggingContext.SetContext(action: "Create", controller: "Items"))
+ {
+ context.Items.Add(new TestItem { Name = "Test" });
+ await context.SaveChangesAsync();
+ }
+ var sql = captor.CapturedCommands.First(c => c.Contains("INSERT"));
+ Assert.Contains("db_driver='efcore'", sql);
+ Assert.Contains("action='Create'", sql);
+ }
+}
+
+public class TestDbContext : DbContext
+{
+ public TestDbContext(DbContextOptions options) : base(options) { }
+ public DbSet Items => Set();
+}
+
+public class TestItem
+{
+ public int Id { get; set; }
+ public string Name { get; set; } = "";
+}
+
+public class SqlCapturingInterceptor : DbCommandInterceptor
+{
+ public List CapturedCommands { get; } = new();
+
+ public override InterceptionResult ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult result)
+ { CapturedCommands.Add(command.CommandText); return base.ReaderExecuting(command, eventData, result); }
+
+ public override ValueTask> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default)
+ { CapturedCommands.Add(command.CommandText); return base.ReaderExecutingAsync(command, eventData, result, cancellationToken); }
+
+ public override InterceptionResult NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult result)
+ { CapturedCommands.Add(command.CommandText); return base.NonQueryExecuting(command, eventData, result); }
+
+ public override ValueTask> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default)
+ { CapturedCommands.Add(command.CommandText); return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken); }
+}
diff --git a/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/SqlCommenterInterceptorTests.cs b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/SqlCommenterInterceptorTests.cs
new file mode 100644
index 00000000..18303330
--- /dev/null
+++ b/dotnet/sqlcommenter-efcore/test/QueryDoctor.SqlCommenter.EFCore.Tests/SqlCommenterInterceptorTests.cs
@@ -0,0 +1,129 @@
+using System.Data.Common;
+using NSubstitute;
+
+namespace QueryDoctor.SqlCommenter.EFCore.Tests;
+
+public class SqlCommenterInterceptorTests
+{
+ private static DbCommand CreateMockCommand(string sql)
+ {
+ var command = Substitute.For();
+ command.CommandText = sql;
+ return command;
+ }
+
+ [Fact]
+ public void ReaderExecuting_AppendsComment()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { EnableStackInspection = false });
+ var command = CreateMockCommand("SELECT 1");
+ using (QueryTaggingContext.SetContext(action: "Test", controller: "Home"))
+ { interceptor.ReaderExecuting(command, null!, default); }
+ Assert.Contains("/*", command.CommandText);
+ Assert.Contains("*/", command.CommandText);
+ Assert.StartsWith("SELECT 1", command.CommandText);
+ }
+
+ [Fact]
+ public void ReaderExecutingAsync_AppendsComment()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { EnableStackInspection = false });
+ var command = CreateMockCommand("SELECT 1");
+ using (QueryTaggingContext.SetContext(action: "Test", controller: "Home"))
+ { interceptor.ReaderExecutingAsync(command, null!, default); }
+ Assert.Contains("/*", command.CommandText);
+ }
+
+ [Fact]
+ public void NonQueryExecuting_AppendsComment()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { EnableStackInspection = false });
+ var command = CreateMockCommand("INSERT INTO test VALUES (1)");
+ using (QueryTaggingContext.SetContext(action: "Create", controller: "Home"))
+ { interceptor.NonQueryExecuting(command, null!, default); }
+ Assert.Contains("/*", command.CommandText);
+ Assert.StartsWith("INSERT INTO test VALUES (1)", command.CommandText);
+ }
+
+ [Fact]
+ public void ScalarExecuting_AppendsComment()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { EnableStackInspection = false });
+ var command = CreateMockCommand("SELECT COUNT(*) FROM test");
+ using (QueryTaggingContext.SetContext(action: "Count", controller: "Home"))
+ { interceptor.ScalarExecuting(command, null!, default); }
+ Assert.Contains("/*", command.CommandText);
+ }
+
+ [Fact]
+ public void Disabled_DoesNotAppendComment()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { Enabled = false });
+ var command = CreateMockCommand("SELECT 1");
+ using (QueryTaggingContext.SetContext(action: "Test", controller: "Home"))
+ { interceptor.ReaderExecuting(command, null!, default); }
+ Assert.Equal("SELECT 1", command.CommandText);
+ }
+
+ [Fact]
+ public void ExistingCommentOpen_DoesNotAppendComment()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { EnableStackInspection = false });
+ var command = CreateMockCommand("SELECT 1 /* existing comment */");
+ using (QueryTaggingContext.SetContext(action: "Test", controller: "Home"))
+ { interceptor.ReaderExecuting(command, null!, default); }
+ Assert.Equal("SELECT 1 /* existing comment */", command.CommandText);
+ }
+
+ [Fact]
+ public void ExistingCommentClose_DoesNotAppendComment()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { EnableStackInspection = false });
+ var command = CreateMockCommand("SELECT 1 */");
+ using (QueryTaggingContext.SetContext(action: "Test", controller: "Home"))
+ { interceptor.ReaderExecuting(command, null!, default); }
+ Assert.Equal("SELECT 1 */", command.CommandText);
+ }
+
+ [Fact]
+ public void PreservesOriginalSql()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { EnableStackInspection = false });
+ var originalSql = "SELECT * FROM users WHERE id = @p0";
+ var command = CreateMockCommand(originalSql);
+ using (QueryTaggingContext.SetContext(action: "Get", controller: "Users"))
+ { interceptor.ReaderExecuting(command, null!, default); }
+ Assert.StartsWith(originalSql, command.CommandText);
+ }
+
+ [Fact]
+ public void ExplicitContext_TakesPriority()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { EnableStackInspection = true });
+ var command = CreateMockCommand("SELECT 1");
+ using (QueryTaggingContext.SetContext(action: "ExplicitAction", controller: "ExplicitController"))
+ { interceptor.ReaderExecuting(command, null!, default); }
+ Assert.Contains("action='ExplicitAction'", command.CommandText);
+ Assert.Contains("controller='ExplicitController'", command.CommandText);
+ }
+
+ [Fact]
+ public void NoContext_NoStackInspection_NoComment()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { EnableStackInspection = false });
+ var command = CreateMockCommand("SELECT 1");
+ interceptor.ReaderExecuting(command, null!, default);
+ Assert.Equal("SELECT 1", command.CommandText);
+ }
+
+ [Fact]
+ public void CommentContainsActionAndController()
+ {
+ var interceptor = new SqlCommenterInterceptor(new SqlCommenterOptions { EnableStackInspection = false });
+ var command = CreateMockCommand("SELECT 1");
+ using (QueryTaggingContext.SetContext(action: "MyAction", controller: "MyController"))
+ { interceptor.ReaderExecuting(command, null!, default); }
+ Assert.Contains("action='MyAction'", command.CommandText);
+ Assert.Contains("controller='MyController'", command.CommandText);
+ }
+}
diff --git a/nodejs/sqlcommenter-nodejs/.gitignore b/nodejs/sqlcommenter-nodejs/.gitignore
index ad46b308..5d1eacec 100644
--- a/nodejs/sqlcommenter-nodejs/.gitignore
+++ b/nodejs/sqlcommenter-nodejs/.gitignore
@@ -59,3 +59,6 @@ typings/
# next.js build output
.next
+
+# typescript build output
+**/dist
diff --git a/nodejs/sqlcommenter-nodejs/README.md b/nodejs/sqlcommenter-nodejs/README.md
index de9978bf..d4f8a948 100644
--- a/nodejs/sqlcommenter-nodejs/README.md
+++ b/nodejs/sqlcommenter-nodejs/README.md
@@ -1,5 +1,10 @@
# sqlcommenter
+For Query Doctor packages, see:
+- [sqlcommenter-drizzle](./packages/sqlcommenter-drizzle/README.md)
+- [sqlcommenter-typeorm](./packages/sqlcommenter-typeorm/README.md)
+- [sqlcommenter-mikroorm](./packages/sqlcommenter-mikroorm/README.md)
+
sqlcommenter is a suite of plugins/middleware/wrappers to augment SQL statements from ORMs/Querybuilders
with comments that can be used later to correlate user code with SQL statements.
@@ -9,13 +14,16 @@ It supports Node v6 and above to use ES6 features.
- Sequelize
- Knex.js
+- Drizzle
+- TypeORM
+- MikroORM
### Installation
-Go into either of the packages [packages/knex](./packages/knex) or [packages/sequelize](./packages/sequelize)
-and then you can run respectively
-
-Middleware|Command|URL
----|---|---
-Knex.js|`npm install @google-cloud/sqlcommenter-knex`|https://www.npmjs.com/package/@google-cloud/sqlcommenter-knex
-Sequelize.js|`npm install @google-cloud/sqlcommenter-sequelize`|https://www.npmjs.com/package/@google-cloud/sqlcommenter-sequelize
+| Middleware | Command | URL |
+| ------------ | --------------------------------------------------- | ------------------------------------------------------------------ |
+| Knex.js | `npm install @google-cloud/sqlcommenter-knex` | https://www.npmjs.com/package/@google-cloud/sqlcommenter-knex |
+| Sequelize.js | `npm install @google-cloud/sqlcommenter-sequelize` | https://www.npmjs.com/package/@google-cloud/sqlcommenter-sequelize |
+| Drizzle | `npm install @query-doctor/sqlcommenter-drizzle` | https://www.npmjs.com/package/@query-doctor/sqlcommenter-drizzle |
+| TypeORM | `npm install @query-doctor/sqlcommenter-typeorm` | https://www.npmjs.com/package/@query-doctor/sqlcommenter-typeorm |
+| MikroORM | `npm install @query-doctor/sqlcommenter-mikroorm` | https://www.npmjs.com/package/@query-doctor/sqlcommenter-mikroorm |
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/.nvmrc b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/.nvmrc
new file mode 100644
index 00000000..7092078d
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/.nvmrc
@@ -0,0 +1 @@
+24.8
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/LICENSE b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/README.md b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/README.md
new file mode 100644
index 00000000..586bf531
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/README.md
@@ -0,0 +1,92 @@
+# @query-doctor/sqlcommenter-drizzle
+
+Drizzle sqlcommenter support for drizzle >= 0.34.0 (including 1.0.0 beta).
+
+Only tested on Postgres, but theoretically it should be compatible with all clients supported by drizzle.
+
+Emits the following fields into the query:
+
+| name | included by default? | description |
+| ------------- | -------------------- | --------------------------------------------------------------- |
+| db_driver | Yes | The driver used to connect to the database. (Drizzle) |
+| file | Yes | The file that the query was executed in. |
+| route | No | The route that the query was executed in. |
+| method | No | The http method for the request that the query was executed in. |
+| anything else | No | Any other information that the user wants to add to the query. |
+
+It also emits the trace context, if available.
+
+### Installation
+
+```shell
+npm install @query-doctor/sqlcommenter-drizzle
+pnpm add @query-doctor/sqlcommenter-drizzle
+```
+
+### Usage
+
+Simply wrap your drizzle instance with the `patchDrizzle` function.
+
+Before:
+
+```ts
+// db/drizle.ts
+import { drizzle } from "drizzle-orm/postgres-js";
+
+const db = drizzle(process.env.DATABASE_URL);
+```
+
+After:
+
+```ts
+// db/drizle.ts
+import { drizzle } from "drizzle-orm/postgres-js";
+import { patchDrizzle } from "@query-doctor/sqlcommenter-drizzle";
+
+const db = patchDrizzle(drizzle(process.env.DATABASE_URL));
+```
+
+### Emitting route information
+
+To include route information in the comments, patching Drizzle by itself is not enough. You need to use the `withRequestContext` function to pass along relevant information to the query comments.
+
+You can add any arbitrary information to the request context aside from `route`, `method` and `controller`.
+
+Here are some examples of how to use it with different frameworks:
+
+#### Express
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-drizzle/http";
+
+app.use((req, res, next) => {
+ withRequestContext({ route: req.route.path, method: req.method }, next);
+});
+```
+
+#### Hono
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-drizzle/http";
+import { routePath } from "hono/route";
+
+app.use((c, next) => {
+ withRequestContext({ route: routePath(c), method: c.req.method }, next);
+});
+```
+
+#### Fastify
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-drizzle/http";
+
+app.addHook("onRequest", (request, _, done) => {
+ withRequestContext(
+ {
+ route: request.routerPath,
+ method: request.method,
+ },
+ done
+ );
+});
+```
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/package-lock.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/package-lock.json
new file mode 100644
index 00000000..fe30c20f
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/package-lock.json
@@ -0,0 +1,2322 @@
+{
+ "name": "@query-doctor/sqlcommenter-drizzle",
+ "version": "0.1.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@query-doctor/sqlcommenter-drizzle",
+ "version": "0.1.1",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api": "~1.9.0"
+ },
+ "devDependencies": {
+ "@electric-sql/pglite": "^0.3.10",
+ "@types/node": "^20.19.34",
+ "hono": "^4.9.8",
+ "postgres": "^3.4.7",
+ "rewiremock": "^3.14.3",
+ "tsx": "^4.20.5",
+ "typescript": "^5.9.3"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/core": ">=1.0.0",
+ "drizzle-orm": ">=0.35.0"
+ }
+ },
+ "node_modules/@electric-sql/pglite": {
+ "version": "0.3.15",
+ "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz",
+ "integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==",
+ "devOptional": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
+ "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
+ "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
+ "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
+ "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
+ "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
+ "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
+ "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
+ "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
+ "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
+ "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
+ "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
+ "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
+ "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
+ "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
+ "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
+ "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
+ "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
+ "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
+ "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
+ "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
+ "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
+ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@opentelemetry/core": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.1.tgz",
+ "integrity": "sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/semantic-conventions": {
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz",
+ "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.34",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.34.tgz",
+ "integrity": "sha512-by3/Z0Qp+L9cAySEsSNNwZ6WWw8ywgGLPQGgbQDhNRSitqYgkgp4pErd23ZSCavbtUA2CN4jQtoB3T8nk4j3Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/asn1.js": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/asn1.js/node_modules/bn.js": {
+ "version": "4.12.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
+ "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/assert": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.1.tgz",
+ "integrity": "sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object.assign": "^4.1.4",
+ "util": "^0.10.4"
+ }
+ },
+ "node_modules/assert/node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/assert/node_modules/util": {
+ "version": "0.10.4",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
+ "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "2.0.3"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
+ }
+ },
+ "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"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/bn.js": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz",
+ "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "node_modules/browserify-des": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/browserify-rsa": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz",
+ "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^5.2.1",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/browserify-sign": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.5.tgz",
+ "integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "bn.js": "^5.2.2",
+ "browserify-rsa": "^4.1.1",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "elliptic": "^6.6.1",
+ "inherits": "^2.0.4",
+ "parse-asn1": "^5.1.9",
+ "readable-stream": "^2.3.8",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pako": "~1.0.5"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+ "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ }
+ },
+ "node_modules/buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/cipher-base": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz",
+ "integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/compare-module-exports": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/compare-module-exports/-/compare-module-exports-2.1.0.tgz",
+ "integrity": "sha512-3Lc0sTIuX1jmY2K2RrXRJOND6KsRTX2D4v3+eu1PDptsuJZVK4LZc852eZa9I+avj0NrUKlTNgqvccNOH6mbGg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/console-browserify": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+ "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
+ "dev": true
+ },
+ "node_modules/constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/core-js": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT"
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/create-ecdh": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
+ "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.5.3"
+ }
+ },
+ "node_modules/create-ecdh/node_modules/bn.js": {
+ "version": "4.12.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
+ "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "node_modules/create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "node_modules/crypto-browserify": {
+ "version": "3.12.1",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz",
+ "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserify-cipher": "^1.0.1",
+ "browserify-sign": "^4.2.3",
+ "create-ecdh": "^4.0.4",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "diffie-hellman": "^5.0.3",
+ "hash-base": "~3.0.4",
+ "inherits": "^2.0.4",
+ "pbkdf2": "^3.1.2",
+ "public-encrypt": "^4.0.3",
+ "randombytes": "^2.1.0",
+ "randomfill": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/des.js": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz",
+ "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "node_modules/diffie-hellman/node_modules/bn.js": {
+ "version": "4.12.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
+ "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/domain-browser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4",
+ "npm": ">=1.2"
+ }
+ },
+ "node_modules/drizzle-orm": {
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.1.tgz",
+ "integrity": "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "peerDependencies": {
+ "@aws-sdk/client-rds-data": ">=3",
+ "@cloudflare/workers-types": ">=4",
+ "@electric-sql/pglite": ">=0.2.0",
+ "@libsql/client": ">=0.10.0",
+ "@libsql/client-wasm": ">=0.10.0",
+ "@neondatabase/serverless": ">=0.10.0",
+ "@op-engineering/op-sqlite": ">=2",
+ "@opentelemetry/api": "^1.4.1",
+ "@planetscale/database": ">=1.13",
+ "@prisma/client": "*",
+ "@tidbcloud/serverless": "*",
+ "@types/better-sqlite3": "*",
+ "@types/pg": "*",
+ "@types/sql.js": "*",
+ "@upstash/redis": ">=1.34.7",
+ "@vercel/postgres": ">=0.8.0",
+ "@xata.io/client": "*",
+ "better-sqlite3": ">=7",
+ "bun-types": "*",
+ "expo-sqlite": ">=14.0.0",
+ "gel": ">=2",
+ "knex": "*",
+ "kysely": "*",
+ "mysql2": ">=2",
+ "pg": ">=8",
+ "postgres": ">=3",
+ "sql.js": ">=1",
+ "sqlite3": ">=5"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/client-rds-data": {
+ "optional": true
+ },
+ "@cloudflare/workers-types": {
+ "optional": true
+ },
+ "@electric-sql/pglite": {
+ "optional": true
+ },
+ "@libsql/client": {
+ "optional": true
+ },
+ "@libsql/client-wasm": {
+ "optional": true
+ },
+ "@neondatabase/serverless": {
+ "optional": true
+ },
+ "@op-engineering/op-sqlite": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@planetscale/database": {
+ "optional": true
+ },
+ "@prisma/client": {
+ "optional": true
+ },
+ "@tidbcloud/serverless": {
+ "optional": true
+ },
+ "@types/better-sqlite3": {
+ "optional": true
+ },
+ "@types/pg": {
+ "optional": true
+ },
+ "@types/sql.js": {
+ "optional": true
+ },
+ "@upstash/redis": {
+ "optional": true
+ },
+ "@vercel/postgres": {
+ "optional": true
+ },
+ "@xata.io/client": {
+ "optional": true
+ },
+ "better-sqlite3": {
+ "optional": true
+ },
+ "bun-types": {
+ "optional": true
+ },
+ "expo-sqlite": {
+ "optional": true
+ },
+ "gel": {
+ "optional": true
+ },
+ "knex": {
+ "optional": true
+ },
+ "kysely": {
+ "optional": true
+ },
+ "mysql2": {
+ "optional": true
+ },
+ "pg": {
+ "optional": true
+ },
+ "postgres": {
+ "optional": true
+ },
+ "prisma": {
+ "optional": true
+ },
+ "sql.js": {
+ "optional": true
+ },
+ "sqlite3": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/elliptic": {
+ "version": "6.6.1",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz",
+ "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/elliptic/node_modules/bn.js": {
+ "version": "4.12.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
+ "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
+ "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.3",
+ "@esbuild/android-arm": "0.27.3",
+ "@esbuild/android-arm64": "0.27.3",
+ "@esbuild/android-x64": "0.27.3",
+ "@esbuild/darwin-arm64": "0.27.3",
+ "@esbuild/darwin-x64": "0.27.3",
+ "@esbuild/freebsd-arm64": "0.27.3",
+ "@esbuild/freebsd-x64": "0.27.3",
+ "@esbuild/linux-arm": "0.27.3",
+ "@esbuild/linux-arm64": "0.27.3",
+ "@esbuild/linux-ia32": "0.27.3",
+ "@esbuild/linux-loong64": "0.27.3",
+ "@esbuild/linux-mips64el": "0.27.3",
+ "@esbuild/linux-ppc64": "0.27.3",
+ "@esbuild/linux-riscv64": "0.27.3",
+ "@esbuild/linux-s390x": "0.27.3",
+ "@esbuild/linux-x64": "0.27.3",
+ "@esbuild/netbsd-arm64": "0.27.3",
+ "@esbuild/netbsd-x64": "0.27.3",
+ "@esbuild/openbsd-arm64": "0.27.3",
+ "@esbuild/openbsd-x64": "0.27.3",
+ "@esbuild/openharmony-arm64": "0.27.3",
+ "@esbuild/sunos-x64": "0.27.3",
+ "@esbuild/win32-arm64": "0.27.3",
+ "@esbuild/win32-ia32": "0.27.3",
+ "@esbuild/win32-x64": "0.27.3"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
+ "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hash-base": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz",
+ "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/hono": {
+ "version": "4.12.3",
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.3.tgz",
+ "integrity": "sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/https-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+ "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ },
+ "bin": {
+ "miller-rabin": "bin/miller-rabin"
+ }
+ },
+ "node_modules/miller-rabin/node_modules/bn.js": {
+ "version": "4.12.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
+ "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-libs-browser": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
+ "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assert": "^1.1.1",
+ "browserify-zlib": "^0.2.0",
+ "buffer": "^4.3.0",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "^1.0.0",
+ "crypto-browserify": "^3.11.0",
+ "domain-browser": "^1.1.1",
+ "events": "^3.0.0",
+ "https-browserify": "^1.0.0",
+ "os-browserify": "^0.3.0",
+ "path-browserify": "0.0.1",
+ "process": "^0.11.10",
+ "punycode": "^1.2.4",
+ "querystring-es3": "^0.2.0",
+ "readable-stream": "^2.3.3",
+ "stream-browserify": "^2.0.1",
+ "stream-http": "^2.7.2",
+ "string_decoder": "^1.0.0",
+ "timers-browserify": "^2.0.4",
+ "tty-browserify": "0.0.0",
+ "url": "^0.11.0",
+ "util": "^0.11.0",
+ "vm-browserify": "^1.0.1"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/os-browserify": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+ "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true,
+ "license": "(MIT AND Zlib)"
+ },
+ "node_modules/parse-asn1": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.9.tgz",
+ "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "asn1.js": "^4.10.1",
+ "browserify-aes": "^1.2.0",
+ "evp_bytestokey": "^1.0.3",
+ "pbkdf2": "^3.1.5",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/path-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+ "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pbkdf2": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz",
+ "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "ripemd160": "^2.0.3",
+ "safe-buffer": "^5.2.1",
+ "sha.js": "^2.4.12",
+ "to-buffer": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postgres": {
+ "version": "3.4.8",
+ "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.8.tgz",
+ "integrity": "sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==",
+ "devOptional": true,
+ "license": "Unlicense",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/porsager"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/public-encrypt/node_modules/bn.js": {
+ "version": "4.12.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
+ "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/qs": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
+ "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "randombytes": "^2.0.5",
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/readable-stream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/rewiremock": {
+ "version": "3.14.6",
+ "resolved": "https://registry.npmjs.org/rewiremock/-/rewiremock-3.14.6.tgz",
+ "integrity": "sha512-hjpS7iQUTVVh/IHV4GE1ypg4IzlgVc34gxZBarwwVrKfnjlyqHJuQdsia6Ac7m4f4k/zxxA3tX285MOstdysRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "babel-runtime": "^6.26.0",
+ "compare-module-exports": "^2.1.0",
+ "node-libs-browser": "^2.1.0",
+ "path-parse": "^1.0.5",
+ "wipe-node-cache": "^2.1.2",
+ "wipe-webpack-cache": "^2.1.0"
+ }
+ },
+ "node_modules/ripemd160": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz",
+ "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash-base": "^3.1.2",
+ "inherits": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/ripemd160/node_modules/hash-base": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz",
+ "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^2.3.8",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sha.js": {
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
+ "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
+ "dev": true,
+ "license": "(MIT AND BSD-3-Clause)",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.0"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/stream-browserify": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+ "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "node_modules/stream-http": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
+ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.3.6",
+ "to-arraybuffer": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/timers-browserify": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz",
+ "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "setimmediate": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/to-arraybuffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+ "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/to-buffer": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
+ "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isarray": "^2.0.5",
+ "safe-buffer": "^5.2.1",
+ "typed-array-buffer": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/to-buffer/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tsx": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/tty-browserify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+ "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/url": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz",
+ "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^1.4.1",
+ "qs": "^6.12.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/util": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
+ "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "2.0.3"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/util/node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/vm-browserify": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.20",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
+ "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/wipe-node-cache": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/wipe-node-cache/-/wipe-node-cache-2.1.2.tgz",
+ "integrity": "sha512-m7NXa8qSxBGMtdQilOu53ctMaIBXy93FOP04EC1Uf4bpsE+r+adfLKwIMIvGbABsznaSNxK/ErD4xXDyY5og9w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wipe-webpack-cache": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/wipe-webpack-cache/-/wipe-webpack-cache-2.1.0.tgz",
+ "integrity": "sha512-OXzQMGpA7MnQQ8AG+uMl5mWR2ezy6fw1+DMHY+wzYP1qkF1jrek87psLBmhZEj+er4efO/GD4R8jXWFierobaA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "wipe-node-cache": "^2.1.0"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ }
+ }
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/package.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/package.json
new file mode 100644
index 00000000..bcd66612
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/package.json
@@ -0,0 +1,68 @@
+{
+ "name": "@query-doctor/sqlcommenter-drizzle",
+ "version": "0.2.0",
+ "description": "SQLCommenter patch for drizzle-orm",
+ "main": "dist/cjs/index.js",
+ "type": "module",
+ "types": "dist/esm/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/esm/index.d.ts",
+ "default": "./dist/esm/index.js"
+ },
+ "require": {
+ "types": "./dist/cjs/index.d.ts",
+ "default": "./dist/cjs/index.js"
+ }
+ },
+ "./http": {
+ "import": {
+ "types": "./dist/esm/http.d.ts",
+ "default": "./dist/esm/http.js"
+ },
+ "require": {
+ "types": "./dist/cjs/http.d.ts",
+ "default": "./dist/cjs/http.js"
+ }
+ }
+ },
+ "devDependencies": {
+ "@electric-sql/pglite": "^0.3.10",
+ "@types/node": "^20.19.34",
+ "hono": "^4.9.8",
+ "postgres": "^3.4.7",
+ "rewiremock": "^3.14.3",
+ "tsx": "^4.20.5",
+ "typescript": "^5.9.3"
+ },
+ "dependencies": {
+ "@opentelemetry/api": "~1.9.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/core": ">=1.0.0",
+ "drizzle-orm": ">=0.35.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "scripts": {
+ "test": "node --import=tsx --test test/**/*.spec.ts",
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/query-doctor/sqlcommenter.git"
+ },
+ "publishConfig": {
+ "access": "public",
+ "registry": "https://registry.npmjs.org"
+ },
+ "author": "Query Doctor",
+ "license": "Apache-2.0",
+ "packageManager": "npm@11.9.0",
+ "module": "dist/esm/index.js"
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/als.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/als.ts
new file mode 100644
index 00000000..225da800
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/als.ts
@@ -0,0 +1,4 @@
+import { AsyncLocalStorage } from "node:async_hooks";
+import type { RequestContext } from "./request-context.js";
+
+export const als = new AsyncLocalStorage();
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/http.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/http.ts
new file mode 100644
index 00000000..2efcbb90
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/http.ts
@@ -0,0 +1,16 @@
+import { als } from "./als.js";
+import type { RequestContext } from "./request-context.js";
+
+/**
+ * Wraps the next function in the AsyncLocalStorage with the request context.
+ * Used to get `route` and `controller` information from the request into the query
+ * without exposing the underlying AsyncLocalStorage API.
+ */
+export function withRequestContext(
+ context: RequestContext,
+ next: () => Promise,
+) {
+ als.run(context, next);
+}
+
+export type { RequestContext, WellKnownFields } from "./request-context.js";
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/index.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/index.ts
new file mode 100644
index 00000000..b01d8948
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/index.ts
@@ -0,0 +1,219 @@
+import { alreadyHasComment, serializeTags } from "./sqlcommenter.js";
+import { als } from "./als.js";
+import { pushW3CTraceContext } from "./tracing.js";
+import { resolveFilePath } from "./path.js";
+
+const LIBRARY_NAME = "sqlcommenter-drizzle";
+
+type QueryContext = {
+ queryStack: string[];
+};
+
+type DriverSession = { prepareQuery: (query: unknown) => unknown };
+
+// We don't own the Session object here so using a WeakMap to prevent memory leaks
+// An alternative could be to set a Symbol in the Session to store the context
+// but this approach seems a little bit safer as we avoid interfacing with the object at all
+const contexts = new WeakMap();
+
+function isValidCaller(line: string): boolean {
+ if (line.includes("node_modules")) {
+ return false;
+ }
+ // make sure we don't break our own tests
+ // should ideally not even be included in this function to begin with since
+ // it'll never be true outside of testing
+ if (line.includes(`${LIBRARY_NAME}/test/`)) {
+ return true;
+ }
+ if (line.includes(LIBRARY_NAME)) {
+ return false;
+ }
+ return true;
+}
+
+// (file.ts:12:12) or file.ts:12:12
+// this is not 100% correct. Folders and filenames can have spaces in them
+// but its hard to parse that manually. Let future me deal with it
+const filepathRegex = /([^ (]*?:\d+:\d+)\)?$/;
+
+export function traceCaller(): string | undefined {
+ // we're not using the Error.capturaStackTrace because it doesn't play nicely
+ // with stack traces that aren't full paths to a specific file.
+ // eg: webpack:// or relative paths will produce no result at all so that's not usable
+ const stack = new Error().stack;
+ // can this ever happen?
+ if (!stack) {
+ return;
+ }
+ // skip 1 line for `Error:`, 1 line for the caller of the current function
+ // not hardcoding further to prevent fragile implementation
+ const stackLines = stack.split("\n").slice(2);
+ const methodCaller = stackLines.find(isValidCaller);
+ if (!methodCaller) {
+ return;
+ }
+ const match = methodCaller.match(filepathRegex);
+ if (match) {
+ return resolveFilePath(match[1]);
+ }
+}
+
+function patchMethod(
+ target: Function,
+ thisArg: unknown,
+ args: any[],
+ session: DriverSession,
+) {
+ const caller = traceCaller();
+ if (caller) {
+ const ctx = contexts.get(session);
+ if (ctx) {
+ ctx.queryStack.push(caller);
+ } else {
+ contexts.set(session, { queryStack: [caller] });
+ }
+ }
+ return Reflect.apply(target, thisArg, args);
+}
+
+const DRIZZLE_ORM_MODE_METHODS = ["findFirst", "findMany"] as const;
+
+export function patchDrizzle(
+ drizzle: T & {
+ // is this nullable?
+ session?: DriverSession;
+ execute: unknown;
+ // not all these methods exist on all clients
+ select?: Function;
+ selectDistinct?: Function;
+ selectDistinctOn?: Function;
+ insert?: Function;
+ update?: Function;
+ delete?: Function;
+ },
+): T {
+ try {
+ if ("session" in drizzle && drizzle.session) {
+ patchSession(drizzle.session);
+ } else {
+ // console.debug("No session found in drizzle");
+ }
+ } catch (e) {
+ console.error("Error patching driver", e);
+ }
+ const methods = [
+ "select",
+ "selectDistinct",
+ "selectDistinctOn",
+ "insert",
+ "update",
+ "delete",
+ ] as const;
+ if (typeof drizzle.execute === "function") {
+ drizzle.execute = new Proxy(drizzle.execute, {
+ apply(target, thisArg, args) {
+ const session = thisArg.session;
+ return patchMethod(target, thisArg, args, session);
+ },
+ });
+ }
+ if (drizzle && "query" in drizzle && drizzle.query) {
+ for (const key in drizzle.query) {
+ for (const func of DRIZZLE_ORM_MODE_METHODS) {
+ const schema = drizzle.query[key as keyof typeof drizzle.query];
+ if (!schema[func] || typeof schema[func] !== "function") {
+ continue;
+ }
+ schema[func] = new Proxy(schema[func], {
+ apply(target, thisArg, args) {
+ const session = thisArg.session;
+ return patchMethod(target, thisArg, args, session);
+ },
+ });
+ }
+ }
+ }
+ for (const method of methods) {
+ // not all drivers have all these calls so better be safe
+ if (!drizzle[method] || typeof drizzle[method] !== "function") {
+ continue;
+ }
+ // patching the CRUD functions.
+ // the correct function to patch here is QueryPromise.prototype.then
+ // but because of the way microtasks work, by the time `then` fires,
+ // the stack is already clear and the caller name is no longer available
+ // so we have to forcibly get it earlier when the query is built.
+ // TODO: This isn't 100% reliable as the user could build a query and not run it until much later
+ // which could throw off this process completely.
+ drizzle[method] = new Proxy(drizzle[method], {
+ apply(target, thisArg, args) {
+ const session = thisArg._.session;
+ return patchMethod(target, thisArg, args, session);
+ },
+ });
+ }
+ return drizzle;
+}
+
+const WellKnownFields = {
+ dbDriver: "db_driver",
+ file: "file",
+ route: "route",
+} as const;
+
+// This is very non-standard. If `file` is to be a semantic convention this probably
+// needs to be discussed with the community. It's what we use at query-doctor so
+// sticking with it f
+const SQLCOMMENTER_ARRAY_ELEM_DELIMITER = ";";
+
+/**
+ * Drizzle session is responsible for serializing the query and sending it downstream to
+ * the driver. We're patching `prepareQuery` to add the SQL comments there instead of
+ * patching every single driver that could be used with Drizzle.
+ */
+function patchSession(session: DriverSession) {
+ const proto = Object.getPrototypeOf(session);
+ if (!("prepareQuery" in proto) || typeof proto.prepareQuery !== "function") {
+ console.debug(
+ "Invalid session prototype. Missing `prepareQuery`, did drizzle change its API?",
+ proto,
+ );
+ return;
+ }
+ proto.prepareQuery = new Proxy(proto.prepareQuery, {
+ apply(target, thisArg, args) {
+ try {
+ const ctx = contexts.get(thisArg);
+ const requestContext = als.getStore();
+ const tags: [string, string][] = [
+ [WellKnownFields.dbDriver, "drizzle"],
+ ];
+ // adding traceparent and tracestate
+ pushW3CTraceContext(tags);
+ if (ctx) {
+ tags.push([
+ WellKnownFields.file,
+ // questionable
+ ctx.queryStack.join(SQLCOMMENTER_ARRAY_ELEM_DELIMITER),
+ ]);
+ }
+ if (args[0]) {
+ const query = args[0];
+ if (!alreadyHasComment(query.sql)) {
+ if (requestContext) {
+ for (const key in requestContext) {
+ tags.push([key, String(requestContext[key])]);
+ }
+ }
+ const sqlComment = serializeTags(tags);
+ query.sql += sqlComment;
+ }
+ }
+ } finally {
+ contexts.delete(thisArg);
+ }
+ return Reflect.apply(target, thisArg, args);
+ },
+ });
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/path.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/path.ts
new file mode 100644
index 00000000..811866bd
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/path.ts
@@ -0,0 +1,80 @@
+import { existsSync } from "node:fs";
+import { dirname, join } from "node:path";
+
+let cachedProjectRoot: string | undefined;
+
+/**
+ * Finds the project root by walking up from `process.cwd()` looking for `tsconfig.json`.
+ *
+ * In deployed environments, `process.cwd()` may not be the project root
+ * (e.g., `cd .amplify-hosting/compute/default/ && node app.js`).
+ * Walking up to find `tsconfig.json` — which is never copied to deployment directories —
+ * gives us the real project root.
+ *
+ * The result is cached since the project root doesn't change during a process's lifetime.
+ */
+export function findProjectRoot(): string {
+ if (cachedProjectRoot !== undefined) {
+ return cachedProjectRoot;
+ }
+ let projectRoot = process.cwd();
+ for (let d = projectRoot; d !== dirname(d); d = dirname(d)) {
+ if (existsSync(join(d, "tsconfig.json"))) {
+ projectRoot = d;
+ break;
+ }
+ }
+ cachedProjectRoot = projectRoot;
+ return projectRoot;
+}
+
+/**
+ * Resolves a file path from a stack trace to a correct absolute path.
+ *
+ * When compiled JS is relocated (e.g., postbuild copies `dist/` to a deployment directory),
+ * source-map-resolved paths become incorrect because the relative `sources` entries in
+ * `.map` files resolve against the new location instead of the original project.
+ *
+ * This extracts the `src/`-relative portion and reconstructs the path using the real
+ * project root.
+ *
+ * @param raw - A stack trace entry like "/wrong/path/src/routes/admin.ts:12:15"
+ * @returns The resolved path like "/project/root/src/routes/admin.ts:12:15"
+ */
+export function resolveFilePath(raw: string): string {
+ // Split off :line:column suffix
+ const match = raw.match(/^(.*?):(\d+:\d+)$/);
+ if (!match) {
+ return raw;
+ }
+ const [, filePath, lineCol] = match;
+ const srcIdx = filePath.indexOf("src/");
+ if (srcIdx < 0) {
+ return raw;
+ }
+ const projectRoot = findProjectRoot();
+ const relativePath = filePath.substring(srcIdx);
+ const resolved = `${projectRoot}/${relativePath}`;
+ return `${applyWslPrefix(resolved)}:${lineCol}`;
+}
+
+/**
+ * Prefixes an absolute path with the WSL network path when running inside WSL.
+ *
+ * Inside WSL, absolute paths like `/home/user/project/...` can't be resolved
+ * from Windows-side tooling (e.g., clickable links in dashboards or VS Code).
+ * The `WSL_DISTRO_NAME` env var is always set inside WSL, and the path format
+ * `//wsl.localhost//...` makes paths accessible from Windows.
+ */
+export function applyWslPrefix(filePath: string): string {
+ const distro = process.env.WSL_DISTRO_NAME;
+ if (distro) {
+ return `//wsl.localhost/${distro}${filePath}`;
+ }
+ return filePath;
+}
+
+/** @internal Exposed for testing only */
+export function _resetProjectRootCache() {
+ cachedProjectRoot = undefined;
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/request-context.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/request-context.ts
new file mode 100644
index 00000000..23d99f33
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/request-context.ts
@@ -0,0 +1,18 @@
+export const WellKnownFields = {
+ route: "route",
+ method: "method",
+ controller: "controller",
+} as const;
+
+/**
+ * A context object with values that will be passed along to the final emitted query comments.
+ * Can support well-known fields used by existing sqlcommenter-compatible tooling and
+ * arbitrary key-value pairs.
+ */
+export type RequestContext = {
+ [WellKnownFields.route]: string;
+ [WellKnownFields.method]?: string;
+ [WellKnownFields.controller]?: string;
+ // the user can choose to add any other information to the context
+ [key: string]: unknown;
+};
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/sqlcommenter.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/sqlcommenter.ts
new file mode 100644
index 00000000..688093a0
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/sqlcommenter.ts
@@ -0,0 +1,52 @@
+export type Tag = [string, string];
+
+function escapeMetaCharacters(value: string): string {
+ return value.replaceAll("'", "\\'");
+}
+
+function serializeKey(key: string): string {
+ return escapeMetaCharacters(encodeURIComponent(key));
+}
+
+function serializeValue(value: unknown): string {
+ const encoded = encodeURIComponent(String(value));
+ const metaEscaped = escapeMetaCharacters(encoded);
+ const final = `'${metaEscaped}'`;
+ return final;
+}
+
+function isEmpty(tags: Tag[]): boolean {
+ return tags.length === 0;
+}
+
+function sort(kvPairs: string[]): string[] {
+ return kvPairs.sort((a, b) => a.localeCompare(b));
+}
+
+export function serializeTags(tags: Tag[]): string {
+ if (isEmpty(tags)) {
+ return "";
+ }
+ const parts: string[] = [];
+ for (const [k, v] of tags) {
+ try {
+ const key = serializeKey(k);
+ const value = serializeValue(v);
+ parts.push(`${key}=${value}`);
+ } catch (e) {
+ // ignore errors in serialization and skip p[air]
+ console.error("Error encoding key", e);
+ }
+ }
+ const sorted = sort(parts);
+ const concatenated = sorted.join(",");
+ return `/*${concatenated}*/`;
+}
+
+/**
+ * Debatable whether this part of the spec even makes sense.
+ * But it's checked for compliance.
+ */
+export function alreadyHasComment(sql: string): boolean {
+ return sql.lastIndexOf("*/") !== -1;
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/tracing.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/tracing.ts
new file mode 100644
index 00000000..40a88820
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/src/tracing.ts
@@ -0,0 +1,14 @@
+import type { Tag } from "./sqlcommenter.js";
+import { context, type TextMapSetter } from "@opentelemetry/api";
+import { W3CTraceContextPropagator } from "@opentelemetry/core";
+
+const sqlcommentAppender: TextMapSetter = {
+ set(context, key, value) {
+ context.push([key, value]);
+ },
+};
+
+export function pushW3CTraceContext(tags: Tag[]) {
+ let propagator = new W3CTraceContextPropagator();
+ propagator.inject(context.active(), tags, sqlcommentAppender);
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/test/driver-integration.spec.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/test/driver-integration.spec.ts
new file mode 100644
index 00000000..fa29c8d7
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/test/driver-integration.spec.ts
@@ -0,0 +1,30 @@
+import { test } from "node:test";
+import { pgTable } from "drizzle-orm/pg-core";
+import { serial } from "drizzle-orm/pg-core";
+import { text } from "drizzle-orm/pg-core";
+import { patchDrizzle } from "../src/index.js";
+import { drizzle } from "drizzle-orm/pglite";
+import { DrizzleQueryError } from "drizzle-orm";
+import assert from "node:assert";
+
+const myTable = pgTable("my_table", {
+ id: serial("id").primaryKey(),
+ name: text("name"),
+});
+
+test("pglite integration", async () => {
+ const db = patchDrizzle(drizzle());
+ let errored = false;
+ try {
+ await db.select().from(myTable);
+ } catch (err) {
+ if (err instanceof DrizzleQueryError) {
+ errored = true;
+ console.log(err.query);
+ assert.match(err.query, /\.ts%3A(\d+)%3A(\d+)'\*\/$/);
+ }
+ }
+ if (!errored) {
+ assert.fail("Expected an error to be thrown");
+ }
+});
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/test/path.spec.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/test/path.spec.ts
new file mode 100644
index 00000000..2ac88428
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/test/path.spec.ts
@@ -0,0 +1,124 @@
+import { test } from "node:test";
+import assert from "node:assert";
+import { existsSync } from "node:fs";
+import { join } from "node:path";
+import {
+ findProjectRoot,
+ resolveFilePath,
+ applyWslPrefix,
+ _resetProjectRootCache,
+} from "../src/path.js";
+
+test("findProjectRoot", async (t) => {
+ t.afterEach(() => {
+ _resetProjectRootCache();
+ });
+
+ await t.test("returns a directory containing tsconfig.json", () => {
+ const root = findProjectRoot();
+ assert.ok(
+ existsSync(join(root, "tsconfig.json")),
+ `Expected ${root} to contain tsconfig.json`,
+ );
+ });
+
+ await t.test("caches the result across calls", () => {
+ const first = findProjectRoot();
+ const second = findProjectRoot();
+ assert.strictEqual(first, second);
+ });
+});
+
+test("resolveFilePath", async (t) => {
+ t.afterEach(() => {
+ _resetProjectRootCache();
+ });
+
+ await t.test(
+ "resolves path with src/ to project root",
+ () => {
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath(
+ "/wrong/deploy/dir/src/routes/admin.ts:12:15",
+ );
+ assert.strictEqual(result, `${projectRoot}/src/routes/admin.ts:12:15`);
+ },
+ );
+
+ await t.test("leaves path without src/ unchanged", () => {
+ const result = resolveFilePath("/some/other/path/routes/admin.ts:5:10");
+ assert.strictEqual(result, "/some/other/path/routes/admin.ts:5:10");
+ });
+
+ await t.test("preserves line:column suffix", () => {
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath("/bad/path/src/index.ts:99:3");
+ assert.strictEqual(result, `${projectRoot}/src/index.ts:99:3`);
+ });
+
+ await t.test("uses first src/ occurrence", () => {
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath(
+ "/deploy/src/nested/src/routes/admin.ts:1:1",
+ );
+ assert.strictEqual(
+ result,
+ `${projectRoot}/src/nested/src/routes/admin.ts:1:1`,
+ );
+ });
+
+ await t.test("returns raw string if no line:column suffix", () => {
+ const result = resolveFilePath("/some/path/src/file.ts");
+ assert.strictEqual(result, "/some/path/src/file.ts");
+ });
+});
+
+test("applyWslPrefix", async (t) => {
+ const originalWslDistro = process.env.WSL_DISTRO_NAME;
+
+ t.afterEach(() => {
+ if (originalWslDistro === undefined) {
+ delete process.env.WSL_DISTRO_NAME;
+ } else {
+ process.env.WSL_DISTRO_NAME = originalWslDistro;
+ }
+ });
+
+ await t.test("prefixes path when WSL_DISTRO_NAME is set", () => {
+ process.env.WSL_DISTRO_NAME = "Ubuntu";
+ const result = applyWslPrefix("/home/user/project/src/index.ts");
+ assert.strictEqual(
+ result,
+ "//wsl.localhost/Ubuntu/home/user/project/src/index.ts",
+ );
+ });
+
+ await t.test("returns path unchanged when WSL_DISTRO_NAME is not set", () => {
+ delete process.env.WSL_DISTRO_NAME;
+ const result = applyWslPrefix("/home/user/project/src/index.ts");
+ assert.strictEqual(result, "/home/user/project/src/index.ts");
+ });
+});
+
+test("resolveFilePath with WSL", async (t) => {
+ const originalWslDistro = process.env.WSL_DISTRO_NAME;
+
+ t.afterEach(() => {
+ _resetProjectRootCache();
+ if (originalWslDistro === undefined) {
+ delete process.env.WSL_DISTRO_NAME;
+ } else {
+ process.env.WSL_DISTRO_NAME = originalWslDistro;
+ }
+ });
+
+ await t.test("applies WSL prefix to resolved src/ paths", () => {
+ process.env.WSL_DISTRO_NAME = "Ubuntu";
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath("/wrong/path/src/routes/admin.ts:12:15");
+ assert.strictEqual(
+ result,
+ `//wsl.localhost/Ubuntu${projectRoot}/src/routes/admin.ts:12:15`,
+ );
+ });
+});
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/tsconfig.cjs.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/tsconfig.cjs.json
new file mode 100644
index 00000000..49f03bb2
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/tsconfig.cjs.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "outDir": "./dist/cjs"
+ }
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/tsconfig.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/tsconfig.json
new file mode 100644
index 00000000..8853e4fb
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-drizzle/tsconfig.json
@@ -0,0 +1,113 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "es2018" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
+ "lib": [
+ "es2022"
+ ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "es2020" /* Specify what module code is generated. */,
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ "moduleResolution": "bundler" /* Specify how TypeScript looks up a file from a given module specifier. */,
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "noUncheckedSideEffectImports": true, /* Check side effect imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
+ "declarationMap": true /* Create sourcemaps for d.ts files. */,
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ "sourceMap": true /* Create source map files for emitted JavaScript files. */,
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ "outDir": "./dist/esm" /* Specify an output folder for all emitted files. */,
+ // "removeComments": true, /* Disable emitting comments. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
+
+ /* Type Checking */
+ "strict": true /* Enable all strict type-checking options. */,
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ },
+ "exclude": ["test", "dist", "node_modules"]
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/LICENSE b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/README.md b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/README.md
new file mode 100644
index 00000000..414ae777
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/README.md
@@ -0,0 +1,110 @@
+# @query-doctor/sqlcommenter-mikroorm
+
+SQLCommenter support for MikroORM >= 6.4.0.
+
+Uses MikroORM's built-in `onQuery` configuration hook — no monkey-patching required.
+
+Emits the following fields into the query:
+
+| name | included by default? | description |
+| ------------- | -------------------- | --------------------------------------------------------------- |
+| db_driver | Yes | The driver used to connect to the database. (MikroORM) |
+| file | Yes | The file that the query was executed in. |
+| route | No | The route that the query was executed in. |
+| method | No | The http method for the request that the query was executed in. |
+| anything else | No | Any other information that the user wants to add to the query. |
+
+It also emits the trace context, if available.
+
+### Installation
+
+```shell
+npm install @query-doctor/sqlcommenter-mikroorm
+pnpm add @query-doctor/sqlcommenter-mikroorm
+```
+
+### Usage
+
+Simply call `patchMikroORM` on your MikroORM instance after initialization.
+
+Before:
+
+```ts
+import { MikroORM } from "@mikro-orm/core";
+
+const orm = await MikroORM.init({
+ dbName: "my-db",
+ entities: [...],
+});
+```
+
+After:
+
+```ts
+import { MikroORM } from "@mikro-orm/core";
+import { patchMikroORM } from "@query-doctor/sqlcommenter-mikroorm";
+
+const mikroORM = await MikroORM.init({
+ dbName: "my-db",
+ entities: [...],
+});
+const orm = patchMikroORM(mikroORM);
+```
+
+### Emitting route information
+
+To include route information in the comments, patching MikroORM by itself is not enough. You need to use the `withRequestContext` function to pass along relevant information to the query comments.
+
+You can add any arbitrary information to the request context aside from `route`, `method` and `controller`.
+
+Here are some examples of how to use it with different frameworks:
+
+#### Express
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-mikroorm/http";
+
+app.use((req, res, next) => {
+ withRequestContext({ route: req.route.path, method: req.method }, next);
+});
+```
+
+#### Hono
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-mikroorm/http";
+import { routePath } from "hono/route";
+
+app.use((c, next) => {
+ withRequestContext({ route: routePath(c), method: c.req.method }, next);
+});
+```
+
+#### Fastify
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-mikroorm/http";
+
+app.addHook("onRequest", (request, _, done) => {
+ withRequestContext(
+ {
+ route: request.routerPath,
+ method: request.method,
+ },
+ done
+ );
+});
+```
+
+#### NestJS
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-mikroorm/http";
+
+@Injectable()
+export class SqlcommenterMiddleware implements NestMiddleware {
+ use(req: Request, res: Response, next: NextFunction) {
+ withRequestContext({ route: req.path, method: req.method }, next);
+ }
+}
+```
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/package-lock.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/package-lock.json
new file mode 100644
index 00000000..d79be627
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/package-lock.json
@@ -0,0 +1,1920 @@
+{
+ "name": "@query-doctor/sqlcommenter-mikroorm",
+ "version": "0.0.2",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@query-doctor/sqlcommenter-mikroorm",
+ "version": "0.0.2",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api": "~1.9.0"
+ },
+ "devDependencies": {
+ "@mikro-orm/better-sqlite": "^6.6.7",
+ "@types/node": "^20.19.34",
+ "better-sqlite3": "^12.6.2",
+ "tsx": "^4.20.5",
+ "typescript": "^5.9.3"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@mikro-orm/core": ">=6.4.0",
+ "@opentelemetry/core": ">=1.0.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
+ "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
+ "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
+ "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
+ "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
+ "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
+ "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
+ "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
+ "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
+ "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
+ "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
+ "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
+ "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
+ "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
+ "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
+ "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
+ "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
+ "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
+ "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
+ "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
+ "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
+ "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@mikro-orm/better-sqlite": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/@mikro-orm/better-sqlite/-/better-sqlite-6.6.7.tgz",
+ "integrity": "sha512-fQLTuAIX34W0k2B2oPLUxIUm/jktdSOCYqMuDQ6r3E6oJzpu7imclcJtJqOmZtPQDJXPRHeXP4CweA0G5RugeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@mikro-orm/knex": "6.6.7",
+ "better-sqlite3": "11.10.0",
+ "fs-extra": "11.3.3",
+ "sqlstring-sqlite": "0.1.1"
+ },
+ "engines": {
+ "node": ">= 18.12.0"
+ },
+ "peerDependencies": {
+ "@mikro-orm/core": "^6.0.0"
+ }
+ },
+ "node_modules/@mikro-orm/better-sqlite/node_modules/better-sqlite3": {
+ "version": "11.10.0",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",
+ "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "prebuild-install": "^7.1.1"
+ }
+ },
+ "node_modules/@mikro-orm/core": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-6.6.7.tgz",
+ "integrity": "sha512-VuL9WK6Z1Op5Lg5FCDOfFeVQdfpCrtEDQXEMHnlb0mRL7WnNz2vUu8AJ96t7iOIxkIBJUXrlzpkaHPdrV9lmkA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "dataloader": "2.2.3",
+ "dotenv": "17.2.3",
+ "esprima": "4.0.1",
+ "fs-extra": "11.3.3",
+ "globby": "11.1.0",
+ "mikro-orm": "6.6.7",
+ "reflect-metadata": "0.2.2"
+ },
+ "engines": {
+ "node": ">= 18.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/b4nan"
+ }
+ },
+ "node_modules/@mikro-orm/knex": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/@mikro-orm/knex/-/knex-6.6.7.tgz",
+ "integrity": "sha512-/EfSu3D1A5OrV3vyHSILbFrV0B4FFbHn4Fa3qc1wKf8Dl5adZlPe7jj+R4c87V1+oLo6VzST1sT4Rhp7NWArdw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fs-extra": "11.3.3",
+ "knex": "3.1.0",
+ "sqlstring": "2.3.3"
+ },
+ "engines": {
+ "node": ">= 18.12.0"
+ },
+ "peerDependencies": {
+ "@mikro-orm/core": "^6.0.0",
+ "better-sqlite3": "*",
+ "libsql": "*",
+ "mariadb": "*"
+ },
+ "peerDependenciesMeta": {
+ "better-sqlite3": {
+ "optional": true
+ },
+ "libsql": {
+ "optional": true
+ },
+ "mariadb": {
+ "optional": true
+ }
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
+ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@opentelemetry/core": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.1.tgz",
+ "integrity": "sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/semantic-conventions": {
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz",
+ "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.34",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.34.tgz",
+ "integrity": "sha512-by3/Z0Qp+L9cAySEsSNNwZ6WWw8ywgGLPQGgbQDhNRSitqYgkgp4pErd23ZSCavbtUA2CN4jQtoB3T8nk4j3Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/better-sqlite3": {
+ "version": "12.6.2",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz",
+ "integrity": "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "prebuild-install": "^7.1.1"
+ },
+ "engines": {
+ "node": "20.x || 22.x || 23.x || 24.x || 25.x"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.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==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/colorette": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
+ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/dataloader": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz",
+ "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "17.2.3",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
+ "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
+ "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.3",
+ "@esbuild/android-arm": "0.27.3",
+ "@esbuild/android-arm64": "0.27.3",
+ "@esbuild/android-x64": "0.27.3",
+ "@esbuild/darwin-arm64": "0.27.3",
+ "@esbuild/darwin-x64": "0.27.3",
+ "@esbuild/freebsd-arm64": "0.27.3",
+ "@esbuild/freebsd-x64": "0.27.3",
+ "@esbuild/linux-arm": "0.27.3",
+ "@esbuild/linux-arm64": "0.27.3",
+ "@esbuild/linux-ia32": "0.27.3",
+ "@esbuild/linux-loong64": "0.27.3",
+ "@esbuild/linux-mips64el": "0.27.3",
+ "@esbuild/linux-ppc64": "0.27.3",
+ "@esbuild/linux-riscv64": "0.27.3",
+ "@esbuild/linux-s390x": "0.27.3",
+ "@esbuild/linux-x64": "0.27.3",
+ "@esbuild/netbsd-arm64": "0.27.3",
+ "@esbuild/netbsd-x64": "0.27.3",
+ "@esbuild/openbsd-arm64": "0.27.3",
+ "@esbuild/openbsd-x64": "0.27.3",
+ "@esbuild/openharmony-arm64": "0.27.3",
+ "@esbuild/sunos-x64": "0.27.3",
+ "@esbuild/win32-arm64": "0.27.3",
+ "@esbuild/win32-ia32": "0.27.3",
+ "@esbuild/win32-x64": "0.27.3"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/esm": {
+ "version": "3.2.25",
+ "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
+ "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/expand-template": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "dev": true,
+ "license": "(MIT OR WTFPL)",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "license": "MIT",
+ "peer": 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.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fs-extra": {
+ "version": "11.3.3",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz",
+ "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==",
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
+ "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/getopts": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz",
+ "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/github-from-package": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "ISC"
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/interpret": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
+ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/knex": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz",
+ "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "colorette": "2.0.19",
+ "commander": "^10.0.0",
+ "debug": "4.3.4",
+ "escalade": "^3.1.1",
+ "esm": "^3.2.25",
+ "get-package-type": "^0.1.0",
+ "getopts": "2.3.0",
+ "interpret": "^2.2.0",
+ "lodash": "^4.17.21",
+ "pg-connection-string": "2.6.2",
+ "rechoir": "^0.8.0",
+ "resolve-from": "^5.0.0",
+ "tarn": "^3.0.2",
+ "tildify": "2.0.0"
+ },
+ "bin": {
+ "knex": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependenciesMeta": {
+ "better-sqlite3": {
+ "optional": true
+ },
+ "mysql": {
+ "optional": true
+ },
+ "mysql2": {
+ "optional": true
+ },
+ "pg": {
+ "optional": true
+ },
+ "pg-native": {
+ "optional": true
+ },
+ "sqlite3": {
+ "optional": true
+ },
+ "tedious": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mikro-orm": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-6.6.7.tgz",
+ "integrity": "sha512-Iw8BC2qMeyqgU6lQS86Ht+yzxjK0DKfmXkGQC2wRzDLYiUQj/CEn5ne8Q+5yIrZdIr/y53KqUNyUWDSup+ZT5w==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 18.12.0"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/napi-build-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
+ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-abi": {
+ "version": "3.87.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz",
+ "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pg-connection-string": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
+ "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/prebuild-install": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
+ "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
+ "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.0",
+ "expand-template": "^2.0.3",
+ "github-from-package": "0.0.0",
+ "minimist": "^1.2.3",
+ "mkdirp-classic": "^0.5.3",
+ "napi-build-utils": "^2.0.0",
+ "node-abi": "^3.3.0",
+ "pump": "^3.0.0",
+ "rc": "^1.2.7",
+ "simple-get": "^4.0.0",
+ "tar-fs": "^2.0.0",
+ "tunnel-agent": "^0.6.0"
+ },
+ "bin": {
+ "prebuild-install": "bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/pump": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
+ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "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==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/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,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/rechoir": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
+ "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve": "^1.20.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/reflect-metadata": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+ "license": "Apache-2.0",
+ "peer": true
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/simple-get": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
+ "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decompress-response": "^6.0.0",
+ "once": "^1.3.1",
+ "simple-concat": "^1.0.0"
+ }
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/sqlstring": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
+ "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/sqlstring-sqlite": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/sqlstring-sqlite/-/sqlstring-sqlite-0.1.1.tgz",
+ "integrity": "sha512-9CAYUJ0lEUPYJrswqiqdINNSfq3jqWo/bFJ7tufdoNeSK0Fy+d1kFTxjqO9PIqza0Kri+ZtYMfPVf1aZaFOvrQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
+ "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.1.4"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tarn": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz",
+ "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/tildify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz",
+ "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tsx": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.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,
+ "license": "MIT"
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ }
+ }
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/package.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/package.json
new file mode 100644
index 00000000..c3534020
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/package.json
@@ -0,0 +1,66 @@
+{
+ "name": "@query-doctor/sqlcommenter-mikroorm",
+ "version": "0.1.0",
+ "description": "SQLCommenter patch for MikroORM",
+ "main": "dist/cjs/index.js",
+ "type": "module",
+ "types": "dist/esm/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/esm/index.d.ts",
+ "default": "./dist/esm/index.js"
+ },
+ "require": {
+ "types": "./dist/cjs/index.d.ts",
+ "default": "./dist/cjs/index.js"
+ }
+ },
+ "./http": {
+ "import": {
+ "types": "./dist/esm/http.d.ts",
+ "default": "./dist/esm/http.js"
+ },
+ "require": {
+ "types": "./dist/cjs/http.d.ts",
+ "default": "./dist/cjs/http.js"
+ }
+ }
+ },
+ "devDependencies": {
+ "@mikro-orm/better-sqlite": "^6.6.7",
+ "@types/node": "^20.19.34",
+ "better-sqlite3": "^12.6.2",
+ "tsx": "^4.20.5",
+ "typescript": "^5.9.3"
+ },
+ "dependencies": {
+ "@opentelemetry/api": "~1.9.0"
+ },
+ "peerDependencies": {
+ "@mikro-orm/core": ">=6.4.0",
+ "@opentelemetry/core": ">=1.0.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "scripts": {
+ "test": "node --import=tsx --test test/**/*.spec.ts",
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/query-doctor/sqlcommenter.git"
+ },
+ "publishConfig": {
+ "access": "public",
+ "registry": "https://registry.npmjs.org"
+ },
+ "author": "Query Doctor",
+ "license": "Apache-2.0",
+ "packageManager": "npm@11.9.0",
+ "module": "dist/esm/index.js"
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/als.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/als.ts
new file mode 100644
index 00000000..225da800
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/als.ts
@@ -0,0 +1,4 @@
+import { AsyncLocalStorage } from "node:async_hooks";
+import type { RequestContext } from "./request-context.js";
+
+export const als = new AsyncLocalStorage();
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/http.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/http.ts
new file mode 100644
index 00000000..2efcbb90
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/http.ts
@@ -0,0 +1,16 @@
+import { als } from "./als.js";
+import type { RequestContext } from "./request-context.js";
+
+/**
+ * Wraps the next function in the AsyncLocalStorage with the request context.
+ * Used to get `route` and `controller` information from the request into the query
+ * without exposing the underlying AsyncLocalStorage API.
+ */
+export function withRequestContext(
+ context: RequestContext,
+ next: () => Promise,
+) {
+ als.run(context, next);
+}
+
+export type { RequestContext, WellKnownFields } from "./request-context.js";
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/index.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/index.ts
new file mode 100644
index 00000000..953dc07e
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/index.ts
@@ -0,0 +1,114 @@
+import { alreadyHasTrailingComment, serializeTags, type Tag } from "./sqlcommenter.js";
+import { als } from "./als.js";
+import { pushW3CTraceContext } from "./tracing.js";
+import { resolveFilePath } from "./path.js";
+
+const LIBRARY_NAME = "sqlcommenter-mikroorm";
+
+type ConfigLike = {
+ get(key: "onQuery"): (sql: string, params: readonly unknown[]) => string;
+ set(key: "onQuery", value: (sql: string, params: readonly unknown[]) => string): void;
+};
+
+type MikroORMLike = {
+ config: ConfigLike;
+};
+
+function isValidCaller(line: string): boolean {
+ if (line.includes("node_modules")) {
+ return false;
+ }
+ if (line.includes(`${LIBRARY_NAME}/test/`)) {
+ return true;
+ }
+ if (line.includes(LIBRARY_NAME)) {
+ return false;
+ }
+ return true;
+}
+
+// (file.ts:12:12) or file.ts:12:12
+const filepathRegex = /([^ (]*?:\d+:\d+)\)?$/;
+
+export function traceCaller(): string | undefined {
+ const stack = new Error().stack;
+ if (!stack) {
+ return;
+ }
+ // skip 1 line for `Error:`, 1 line for the caller of the current function
+ const stackLines = stack.split("\n").slice(2);
+ const methodCaller = stackLines.find(isValidCaller);
+ if (!methodCaller) {
+ return;
+ }
+ const match = methodCaller.match(filepathRegex);
+ if (match) {
+ return resolveFilePath(match[1]);
+ }
+}
+
+const WellKnownFields = {
+ dbDriver: "db_driver",
+ file: "file",
+ route: "route",
+} as const;
+
+function buildOnQuery(
+ existingOnQuery: (sql: string, params: readonly unknown[]) => string,
+): (sql: string, params: readonly unknown[]) => string {
+ return (sql: string, params: readonly unknown[]): string => {
+ // chain existing onQuery first
+ sql = existingOnQuery(sql, params);
+
+ try {
+ if (alreadyHasTrailingComment(sql)) {
+ return sql;
+ }
+
+ const caller = traceCaller();
+ const requestContext = als.getStore();
+ const tags: Tag[] = [
+ [WellKnownFields.dbDriver, "mikroorm"],
+ ];
+ pushW3CTraceContext(tags);
+ if (caller) {
+ tags.push([WellKnownFields.file, caller]);
+ }
+ if (requestContext) {
+ for (const key in requestContext) {
+ tags.push([key, String(requestContext[key])]);
+ }
+ }
+ return sql + serializeTags(tags);
+ } catch {
+ // never let comment generation break query execution
+ return sql;
+ }
+ };
+}
+
+/**
+ * Patches a MikroORM instance to append sqlcommenter tags to all queries.
+ * Uses MikroORM's built-in `onQuery` configuration hook (available since v6.4).
+ * Call this after `MikroORM.init()`.
+ *
+ * If an existing `onQuery` handler is already configured, it will be chained
+ * (the existing handler runs first, then sqlcommenter tags are appended).
+ *
+ * @example
+ * ```ts
+ * import { MikroORM } from "@mikro-orm/core";
+ * import { patchMikroORM } from "@query-doctor/sqlcommenter-mikroorm";
+ *
+ * const mikroORM = await MikroORM.init({
+ * dbName: "my-db",
+ * entities: [...],
+ * });
+ * const orm = patchMikroORM(mikroORM);
+ * ```
+ */
+export function patchMikroORM(orm: T): T {
+ const existingOnQuery = orm.config.get("onQuery");
+ orm.config.set("onQuery", buildOnQuery(existingOnQuery));
+ return orm;
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/path.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/path.ts
new file mode 100644
index 00000000..811866bd
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/path.ts
@@ -0,0 +1,80 @@
+import { existsSync } from "node:fs";
+import { dirname, join } from "node:path";
+
+let cachedProjectRoot: string | undefined;
+
+/**
+ * Finds the project root by walking up from `process.cwd()` looking for `tsconfig.json`.
+ *
+ * In deployed environments, `process.cwd()` may not be the project root
+ * (e.g., `cd .amplify-hosting/compute/default/ && node app.js`).
+ * Walking up to find `tsconfig.json` — which is never copied to deployment directories —
+ * gives us the real project root.
+ *
+ * The result is cached since the project root doesn't change during a process's lifetime.
+ */
+export function findProjectRoot(): string {
+ if (cachedProjectRoot !== undefined) {
+ return cachedProjectRoot;
+ }
+ let projectRoot = process.cwd();
+ for (let d = projectRoot; d !== dirname(d); d = dirname(d)) {
+ if (existsSync(join(d, "tsconfig.json"))) {
+ projectRoot = d;
+ break;
+ }
+ }
+ cachedProjectRoot = projectRoot;
+ return projectRoot;
+}
+
+/**
+ * Resolves a file path from a stack trace to a correct absolute path.
+ *
+ * When compiled JS is relocated (e.g., postbuild copies `dist/` to a deployment directory),
+ * source-map-resolved paths become incorrect because the relative `sources` entries in
+ * `.map` files resolve against the new location instead of the original project.
+ *
+ * This extracts the `src/`-relative portion and reconstructs the path using the real
+ * project root.
+ *
+ * @param raw - A stack trace entry like "/wrong/path/src/routes/admin.ts:12:15"
+ * @returns The resolved path like "/project/root/src/routes/admin.ts:12:15"
+ */
+export function resolveFilePath(raw: string): string {
+ // Split off :line:column suffix
+ const match = raw.match(/^(.*?):(\d+:\d+)$/);
+ if (!match) {
+ return raw;
+ }
+ const [, filePath, lineCol] = match;
+ const srcIdx = filePath.indexOf("src/");
+ if (srcIdx < 0) {
+ return raw;
+ }
+ const projectRoot = findProjectRoot();
+ const relativePath = filePath.substring(srcIdx);
+ const resolved = `${projectRoot}/${relativePath}`;
+ return `${applyWslPrefix(resolved)}:${lineCol}`;
+}
+
+/**
+ * Prefixes an absolute path with the WSL network path when running inside WSL.
+ *
+ * Inside WSL, absolute paths like `/home/user/project/...` can't be resolved
+ * from Windows-side tooling (e.g., clickable links in dashboards or VS Code).
+ * The `WSL_DISTRO_NAME` env var is always set inside WSL, and the path format
+ * `//wsl.localhost//...` makes paths accessible from Windows.
+ */
+export function applyWslPrefix(filePath: string): string {
+ const distro = process.env.WSL_DISTRO_NAME;
+ if (distro) {
+ return `//wsl.localhost/${distro}${filePath}`;
+ }
+ return filePath;
+}
+
+/** @internal Exposed for testing only */
+export function _resetProjectRootCache() {
+ cachedProjectRoot = undefined;
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/request-context.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/request-context.ts
new file mode 100644
index 00000000..23d99f33
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/request-context.ts
@@ -0,0 +1,18 @@
+export const WellKnownFields = {
+ route: "route",
+ method: "method",
+ controller: "controller",
+} as const;
+
+/**
+ * A context object with values that will be passed along to the final emitted query comments.
+ * Can support well-known fields used by existing sqlcommenter-compatible tooling and
+ * arbitrary key-value pairs.
+ */
+export type RequestContext = {
+ [WellKnownFields.route]: string;
+ [WellKnownFields.method]?: string;
+ [WellKnownFields.controller]?: string;
+ // the user can choose to add any other information to the context
+ [key: string]: unknown;
+};
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/sqlcommenter.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/sqlcommenter.ts
new file mode 100644
index 00000000..cd362f73
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/sqlcommenter.ts
@@ -0,0 +1,53 @@
+export type Tag = [string, string];
+
+function escapeMetaCharacters(value: string): string {
+ return value.replaceAll("'", "\\'");
+}
+
+function serializeKey(key: string): string {
+ return escapeMetaCharacters(encodeURIComponent(key));
+}
+
+function serializeValue(value: unknown): string {
+ const encoded = encodeURIComponent(String(value));
+ const metaEscaped = escapeMetaCharacters(encoded);
+ const final = `'${metaEscaped}'`;
+ return final;
+}
+
+function isEmpty(tags: Tag[]): boolean {
+ return tags.length === 0;
+}
+
+function sort(kvPairs: string[]): string[] {
+ return kvPairs.sort((a, b) => a.localeCompare(b));
+}
+
+export function serializeTags(tags: Tag[]): string {
+ if (isEmpty(tags)) {
+ return "";
+ }
+ const parts: string[] = [];
+ for (const [k, v] of tags) {
+ try {
+ const key = serializeKey(k);
+ const value = serializeValue(v);
+ parts.push(`${key}=${value}`);
+ } catch (e) {
+ // ignore errors in serialization and skip pair
+ console.error("Error encoding key", e);
+ }
+ }
+ const sorted = sort(parts);
+ const concatenated = sorted.join(",");
+ return `/*${concatenated}*/`;
+}
+
+/**
+ * Checks if the query already has a TRAILING SQL comment (i.e. sqlcommenter tags
+ * already appended). Only checks the end of the query so that MikroORM's
+ * QueryBuilder comments don't prevent us from appending sqlcommenter tags.
+ */
+export function alreadyHasTrailingComment(sql: string): boolean {
+ return sql.trimEnd().endsWith("*/");
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/tracing.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/tracing.ts
new file mode 100644
index 00000000..40a88820
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/src/tracing.ts
@@ -0,0 +1,14 @@
+import type { Tag } from "./sqlcommenter.js";
+import { context, type TextMapSetter } from "@opentelemetry/api";
+import { W3CTraceContextPropagator } from "@opentelemetry/core";
+
+const sqlcommentAppender: TextMapSetter = {
+ set(context, key, value) {
+ context.push([key, value]);
+ },
+};
+
+export function pushW3CTraceContext(tags: Tag[]) {
+ let propagator = new W3CTraceContextPropagator();
+ propagator.inject(context.active(), tags, sqlcommentAppender);
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/test/driver-integration.spec.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/test/driver-integration.spec.ts
new file mode 100644
index 00000000..711c56b0
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/test/driver-integration.spec.ts
@@ -0,0 +1,112 @@
+import { test } from "node:test";
+import assert from "node:assert";
+import { patchMikroORM, traceCaller } from "../src/index.js";
+
+// Mock MikroORM's config interface to test without a real database driver.
+// This tests the actual onQuery function that patchMikroORM sets up.
+function createMockOrm() {
+ let onQuery: (sql: string, params: readonly unknown[]) => string = (sql) => sql;
+ return {
+ config: {
+ get(_key: "onQuery") {
+ return onQuery;
+ },
+ set(_key: "onQuery", value: (sql: string, params: readonly unknown[]) => string) {
+ onQuery = value;
+ },
+ },
+ // helper to run a query through the onQuery pipeline
+ executeQuery(sql: string, params: readonly unknown[] = []): string {
+ return onQuery(sql, params);
+ },
+ };
+}
+
+test("patchMikroORM appends sqlcommenter tags to queries", () => {
+ const orm = createMockOrm();
+ patchMikroORM(orm);
+
+ const result = orm.executeQuery("SELECT 1 as result", []);
+ assert.match(result, /^SELECT 1 as result\/\*/);
+ assert.match(result, /db_driver='mikroorm'/);
+ assert.match(result, /file='[^']+'/);
+ assert.match(result, /\*\/$/);
+});
+
+test("patchMikroORM includes db_driver tag", () => {
+ const orm = createMockOrm();
+ patchMikroORM(orm);
+
+ const result = orm.executeQuery("SELECT * FROM users", []);
+ assert.match(result, /db_driver='mikroorm'/);
+});
+
+test("patchMikroORM includes file tag with caller location", () => {
+ const orm = createMockOrm();
+ patchMikroORM(orm);
+
+ const result = orm.executeQuery("SELECT * FROM users", []);
+ // file tag should contain a .ts or .spec.ts file path with line:column
+ assert.match(result, /file='[^']*\.ts%3A\d+%3A\d+'/);
+});
+
+test("patchMikroORM skips queries with trailing comments", () => {
+ const orm = createMockOrm();
+ patchMikroORM(orm);
+
+ const sql = "SELECT 1 /*db_driver='something'*/";
+ const result = orm.executeQuery(sql, []);
+ // should not double-append
+ assert.strictEqual(result, sql);
+ const commentCount = (result.match(/db_driver/g) || []).length;
+ assert.strictEqual(commentCount, 1, "Should not add another comment when trailing tags exist");
+});
+
+test("patchMikroORM chains existing onQuery handler", () => {
+ const orm = createMockOrm();
+
+ // simulate user having an existing onQuery
+ orm.config.set("onQuery", (sql: string) => sql + " /* user-tag */");
+
+ patchMikroORM(orm);
+
+ const result = orm.executeQuery("SELECT 1", []);
+ // existing onQuery appends "/* user-tag */" which ends with "*/"
+ // so our sqlcommenter should detect the trailing comment and skip
+ assert.ok(
+ result.includes("/* user-tag */"),
+ "Existing onQuery handler output should be preserved",
+ );
+});
+
+test("patchMikroORM chains existing onQuery and appends when no trailing comment", () => {
+ const orm = createMockOrm();
+
+ // simulate user having an existing onQuery that adds a prefix, not a trailing comment
+ orm.config.set("onQuery", (sql: string) => "/* hint */ " + sql);
+
+ patchMikroORM(orm);
+
+ const result = orm.executeQuery("SELECT 1", []);
+ // should have both the user's prefix hint and our appended sqlcommenter tags
+ assert.ok(result.startsWith("/* hint */"), "Existing onQuery prefix should be preserved");
+ assert.match(result, /db_driver='mikroorm'/);
+});
+
+test("patchMikroORM preserves original SQL content", () => {
+ const orm = createMockOrm();
+ patchMikroORM(orm);
+
+ const originalSql = "SELECT id, name FROM users WHERE id = $1";
+ const result = orm.executeQuery(originalSql, [42]);
+ assert.ok(
+ result.startsWith(originalSql),
+ "Original SQL should be preserved at the start of the result",
+ );
+});
+
+test("traceCaller returns a file path with line and column", () => {
+ const caller = traceCaller();
+ assert.ok(caller, "traceCaller should return a value");
+ assert.match(caller!, /:\d+:\d+$/, "Should end with :line:column");
+});
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/test/path.spec.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/test/path.spec.ts
new file mode 100644
index 00000000..1e09cf4d
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/test/path.spec.ts
@@ -0,0 +1,118 @@
+import { test } from "node:test";
+import assert from "node:assert";
+import { existsSync } from "node:fs";
+import { join } from "node:path";
+import {
+ findProjectRoot,
+ resolveFilePath,
+ applyWslPrefix,
+ _resetProjectRootCache,
+} from "../src/path.js";
+
+test("findProjectRoot", async (t) => {
+ t.afterEach(() => {
+ _resetProjectRootCache();
+ });
+
+ await t.test("returns a directory containing tsconfig.json", () => {
+ const root = findProjectRoot();
+ assert.ok(
+ existsSync(join(root, "tsconfig.json")),
+ `Expected ${root} to contain tsconfig.json`,
+ );
+ });
+
+ await t.test("caches the result across calls", () => {
+ const first = findProjectRoot();
+ const second = findProjectRoot();
+ assert.strictEqual(first, second);
+ });
+});
+
+test("resolveFilePath", async (t) => {
+ t.afterEach(() => {
+ _resetProjectRootCache();
+ });
+
+ await t.test("resolves path with src/ to project root", () => {
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath(
+ "/wrong/deploy/dir/src/routes/admin.ts:12:15",
+ );
+ assert.strictEqual(result, `${projectRoot}/src/routes/admin.ts:12:15`);
+ });
+
+ await t.test("leaves path without src/ unchanged", () => {
+ const result = resolveFilePath("/some/other/path/routes/admin.ts:5:10");
+ assert.strictEqual(result, "/some/other/path/routes/admin.ts:5:10");
+ });
+
+ await t.test("preserves line:column suffix", () => {
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath("/bad/path/src/index.ts:99:3");
+ assert.strictEqual(result, `${projectRoot}/src/index.ts:99:3`);
+ });
+
+ await t.test("uses first src/ occurrence", () => {
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath(
+ "/deploy/src/nested/src/routes/admin.ts:1:1",
+ );
+ assert.strictEqual(
+ result,
+ `${projectRoot}/src/nested/src/routes/admin.ts:1:1`,
+ );
+ });
+
+ await t.test("returns raw string if no line:column suffix", () => {
+ const result = resolveFilePath("/some/path/src/file.ts");
+ assert.strictEqual(result, "/some/path/src/file.ts");
+ });
+});
+
+test("applyWslPrefix", async (t) => {
+ const originalWslDistroName = process.env.WSL_DISTRO_NAME;
+
+ t.afterEach(() => {
+ if (originalWslDistroName === undefined) {
+ delete process.env.WSL_DISTRO_NAME;
+ } else {
+ process.env.WSL_DISTRO_NAME = originalWslDistroName;
+ }
+ });
+
+ await t.test("prefixes path when WSL_DISTRO_NAME is set", () => {
+ process.env.WSL_DISTRO_NAME = "Ubuntu-22.04";
+ const path = "/home/user/project/src/file.ts:1:1";
+ const result = applyWslPrefix(path);
+ assert.strictEqual(result, "//wsl.localhost/Ubuntu-22.04/home/user/project/src/file.ts:1:1");
+ });
+
+ await t.test("returns path unchanged when WSL_DISTRO_NAME is not set", () => {
+ delete process.env.WSL_DISTRO_NAME;
+ const path = "/home/user/project/src/file.ts:1:1";
+ const result = applyWslPrefix(path);
+ assert.strictEqual(result, path);
+ });
+});
+
+test("resolveFilePath with WSL", async (t) => {
+ const originalWslDistroName = process.env.WSL_DISTRO_NAME;
+
+ t.afterEach(() => {
+ _resetProjectRootCache();
+ if (originalWslDistroName === undefined) {
+ delete process.env.WSL_DISTRO_NAME;
+ } else {
+ process.env.WSL_DISTRO_NAME = originalWslDistroName;
+ }
+ });
+
+ await t.test("applies WSL prefix to resolved src/ paths", () => {
+ process.env.WSL_DISTRO_NAME = "Ubuntu-22.04";
+ const projectRoot = findProjectRoot();
+ const rawPath = "/wrong/deploy/dir/src/routes/admin.ts:12:15";
+ const result = resolveFilePath(rawPath);
+ assert.strictEqual(result, `//wsl.localhost/Ubuntu-22.04${projectRoot}/src/routes/admin.ts:12:15`);
+ });
+});
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/test/sqlite-integration.spec.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/test/sqlite-integration.spec.ts
new file mode 100644
index 00000000..6812ef55
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/test/sqlite-integration.spec.ts
@@ -0,0 +1,151 @@
+import { test } from "node:test";
+import assert from "node:assert";
+import { MikroORM, EntitySchema } from "@mikro-orm/core";
+import { BetterSqliteDriver } from "@mikro-orm/better-sqlite";
+import { patchMikroORM } from "../src/index.js";
+import { withRequestContext } from "../src/http.js";
+
+const UserSchema = new EntitySchema({
+ name: "User",
+ tableName: "users",
+ properties: {
+ id: { type: "number", primary: true },
+ name: { type: "string" },
+ },
+});
+
+async function createOrm() {
+ return MikroORM.init({
+ driver: BetterSqliteDriver,
+ dbName: ":memory:",
+ entities: [UserSchema],
+ allowGlobalContext: true,
+ });
+}
+
+// Wraps the onQuery to capture all transformed SQL that passes through it
+function captureQueries(orm: MikroORM): string[] {
+ const captured: string[] = [];
+ const currentOnQuery = orm.config.get("onQuery");
+ orm.config.set("onQuery", (sql: string, params: readonly unknown[]) => {
+ const result = currentOnQuery(sql, params);
+ captured.push(result);
+ return result;
+ });
+ return captured;
+}
+
+test("sqlite: tags appended to raw em.execute()", async () => {
+ const orm = patchMikroORM(await createOrm());
+ await orm.getSchemaGenerator().createSchema();
+ const captured = captureQueries(orm);
+
+ const result = await orm.em.execute("SELECT 42 as answer");
+ assert.deepStrictEqual(result, [{ answer: 42 }]);
+
+ const tagged = captured.find((q) => q.includes("SELECT 42") && q.includes("db_driver"));
+ assert.ok(tagged, `Expected tagged query, got: ${captured.join("\n")}`);
+ assert.match(tagged!, /db_driver='mikroorm'/);
+ assert.match(tagged!, /file='[^']+'/);
+
+ await orm.close();
+});
+
+test("sqlite: tags appended to entity find", async () => {
+ const orm = patchMikroORM(await createOrm());
+ await orm.getSchemaGenerator().createSchema();
+ await orm.em.execute("INSERT INTO users (id, name) VALUES (1, 'Alice')");
+ await orm.em.execute("INSERT INTO users (id, name) VALUES (2, 'Bob')");
+
+ const captured = captureQueries(orm);
+
+ const em = orm.em.fork();
+ const users = await em.find("User", {});
+ assert.strictEqual(users.length, 2);
+
+ const tagged = captured.find((q) => q.includes("users") && q.includes("db_driver"));
+ assert.ok(tagged, `Expected tagged SELECT, got: ${captured.join("\n")}`);
+ assert.match(tagged!, /db_driver='mikroorm'/);
+
+ await orm.close();
+});
+
+test("sqlite: tags appended to entity findOne", async () => {
+ const orm = patchMikroORM(await createOrm());
+ await orm.getSchemaGenerator().createSchema();
+ await orm.em.execute("INSERT INTO users (id, name) VALUES (1, 'Alice')");
+
+ const captured = captureQueries(orm);
+
+ const em = orm.em.fork();
+ const user = await em.findOne("User", { name: "Alice" });
+ assert.ok(user);
+ assert.strictEqual(user!.name, "Alice");
+
+ const tagged = captured.find((q) => q.includes("users") && q.includes("db_driver"));
+ assert.ok(tagged, `Expected tagged SELECT, got: ${captured.join("\n")}`);
+
+ await orm.close();
+});
+
+test("sqlite: tags appended to entity persist + flush", async () => {
+ const orm = patchMikroORM(await createOrm());
+ await orm.getSchemaGenerator().createSchema();
+
+ const captured = captureQueries(orm);
+
+ const em = orm.em.fork();
+ em.create("User", { id: 1, name: "Charlie" });
+ await em.flush();
+
+ const tagged = captured.find((q) => q.includes("insert") && q.includes("db_driver"));
+ assert.ok(tagged, `Expected tagged INSERT, got: ${captured.join("\n")}`);
+ assert.match(tagged!, /db_driver='mikroorm'/);
+
+ await orm.close();
+});
+
+test("sqlite: tags appended to QueryBuilder", async () => {
+ const orm = patchMikroORM(await createOrm());
+ await orm.getSchemaGenerator().createSchema();
+ await orm.em.execute("INSERT INTO users (id, name) VALUES (1, 'Dave')");
+
+ const captured = captureQueries(orm);
+
+ const em = orm.em.fork();
+ const result = await em.createQueryBuilder("User").select("*").where({ name: "Dave" }).execute();
+ assert.strictEqual(result.length, 1);
+ assert.strictEqual(result[0].name, "Dave");
+
+ const tagged = captured.find((q) => q.includes("users") && q.includes("db_driver"));
+ assert.ok(tagged, `Expected tagged QueryBuilder SELECT, got: ${captured.join("\n")}`);
+
+ await orm.close();
+});
+
+test("sqlite: withRequestContext adds route/method/controller tags", async () => {
+ const orm = patchMikroORM(await createOrm());
+ await orm.getSchemaGenerator().createSchema();
+ await orm.em.execute("INSERT INTO users (id, name) VALUES (1, 'Eve')");
+
+ const captured = captureQueries(orm);
+
+ await new Promise((resolve) => {
+ withRequestContext(
+ { route: "/api/users", method: "GET", controller: "UserController" },
+ async () => {
+ const em = orm.em.fork();
+ await em.find("User", {});
+ resolve();
+ },
+ );
+ });
+
+ const tagged = captured.find((q) => q.includes("users") && q.includes("route"));
+ assert.ok(tagged, `Expected tagged SELECT with route, got: ${captured.join("\n")}`);
+ assert.ok(tagged!.includes("route='%2Fapi%2Fusers'"), "Should have URL-encoded route");
+ assert.ok(tagged!.includes("method='GET'"), "Should have method");
+ assert.ok(tagged!.includes("controller='UserController'"), "Should have controller");
+
+ await orm.close();
+});
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/tsconfig.cjs.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/tsconfig.cjs.json
new file mode 100644
index 00000000..49f03bb2
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/tsconfig.cjs.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "outDir": "./dist/cjs"
+ }
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/tsconfig.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/tsconfig.json
new file mode 100644
index 00000000..de4e28fe
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-mikroorm/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "target": "es2018",
+ "lib": ["es2022"],
+ "module": "es2020",
+ "moduleResolution": "bundler",
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true,
+ "outDir": "./dist/esm",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "skipLibCheck": true
+ },
+ "exclude": ["test", "dist", "node_modules"]
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/LICENSE b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/README.md b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/README.md
new file mode 100644
index 00000000..19f0e35c
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/README.md
@@ -0,0 +1,109 @@
+# @query-doctor/sqlcommenter-typeorm
+
+TypeORM sqlcommenter support for TypeORM >= 0.3.0.
+
+Emits the following fields into the query:
+
+| name | included by default? | description |
+| ------------- | -------------------- | --------------------------------------------------------------- |
+| db_driver | Yes | The driver used to connect to the database. (TypeORM) |
+| file | Yes | The file that the query was executed in. |
+| route | No | The route that the query was executed in. |
+| method | No | The http method for the request that the query was executed in. |
+| anything else | No | Any other information that the user wants to add to the query. |
+
+It also emits the trace context, if available.
+
+### Installation
+
+```shell
+npm install @query-doctor/sqlcommenter-typeorm
+pnpm add @query-doctor/sqlcommenter-typeorm
+```
+
+### Usage
+
+Simply wrap your TypeORM DataSource with the `patchTypeORM` function.
+
+Before:
+
+```ts
+// db/data-source.ts
+import { DataSource } from "typeorm";
+
+const dataSource = new DataSource({
+ type: "postgres",
+ url: process.env.DATABASE_URL,
+});
+```
+
+After:
+
+```ts
+// db/data-source.ts
+import { DataSource } from "typeorm";
+import { patchTypeORM } from "@query-doctor/sqlcommenter-typeorm";
+
+const dataSource = patchTypeORM(new DataSource({
+ type: "postgres",
+ url: process.env.DATABASE_URL,
+}));
+```
+
+### Emitting route information
+
+To include route information in the comments, patching TypeORM by itself is not enough. You need to use the `withRequestContext` function to pass along relevant information to the query comments.
+
+You can add any arbitrary information to the request context aside from `route`, `method` and `controller`.
+
+Here are some examples of how to use it with different frameworks:
+
+#### Express
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-typeorm/http";
+
+app.use((req, res, next) => {
+ withRequestContext({ route: req.route.path, method: req.method }, next);
+});
+```
+
+#### Hono
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-typeorm/http";
+import { routePath } from "hono/route";
+
+app.use((c, next) => {
+ withRequestContext({ route: routePath(c), method: c.req.method }, next);
+});
+```
+
+#### Fastify
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-typeorm/http";
+
+app.addHook("onRequest", (request, _, done) => {
+ withRequestContext(
+ {
+ route: request.routerPath,
+ method: request.method,
+ },
+ done
+ );
+});
+```
+
+#### NestJS
+
+```ts
+import { withRequestContext } from "@query-doctor/sqlcommenter-typeorm/http";
+
+@Injectable()
+export class SqlcommenterMiddleware implements NestMiddleware {
+ use(req: Request, res: Response, next: NextFunction) {
+ withRequestContext({ route: req.path, method: req.method }, next);
+ }
+}
+```
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package-lock.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package-lock.json
new file mode 100644
index 00000000..28698920
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package-lock.json
@@ -0,0 +1,2114 @@
+{
+ "name": "@query-doctor/sqlcommenter-typeorm",
+ "version": "0.0.2",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@query-doctor/sqlcommenter-typeorm",
+ "version": "0.0.2",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api": "~1.9.0"
+ },
+ "devDependencies": {
+ "@types/node": "^20.19.34",
+ "reflect-metadata": "^0.2.2",
+ "sql.js": "^1.12.0",
+ "tsx": "^4.20.5",
+ "typescript": "^5.9.3"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/core": ">=1.0.0",
+ "typeorm": ">=0.3.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
+ "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
+ "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
+ "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
+ "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
+ "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
+ "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
+ "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
+ "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
+ "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
+ "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
+ "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
+ "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
+ "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
+ "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
+ "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
+ "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
+ "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
+ "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
+ "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
+ "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
+ "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "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==",
+ "license": "ISC",
+ "peer": 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/@opentelemetry/api": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
+ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@opentelemetry/core": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.1.tgz",
+ "integrity": "sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/semantic-conventions": {
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz",
+ "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@sqltools/formatter": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
+ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.34",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.34.tgz",
+ "integrity": "sha512-by3/Z0Qp+L9cAySEsSNNwZ6WWw8ywgGLPQGgbQDhNRSitqYgkgp4pErd23ZSCavbtUA2CN4jQtoB3T8nk4j3Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ansis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz",
+ "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/app-root-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz",
+ "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "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==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz",
+ "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "license": "ISC",
+ "peer": 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/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/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==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": 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/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^5.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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.19",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
+ "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/dedent": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz",
+ "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "babel-plugin-macros": "^3.1.0"
+ },
+ "peerDependenciesMeta": {
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
+ "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.3",
+ "@esbuild/android-arm": "0.27.3",
+ "@esbuild/android-arm64": "0.27.3",
+ "@esbuild/android-x64": "0.27.3",
+ "@esbuild/darwin-arm64": "0.27.3",
+ "@esbuild/darwin-x64": "0.27.3",
+ "@esbuild/freebsd-arm64": "0.27.3",
+ "@esbuild/freebsd-x64": "0.27.3",
+ "@esbuild/linux-arm": "0.27.3",
+ "@esbuild/linux-arm64": "0.27.3",
+ "@esbuild/linux-ia32": "0.27.3",
+ "@esbuild/linux-loong64": "0.27.3",
+ "@esbuild/linux-mips64el": "0.27.3",
+ "@esbuild/linux-ppc64": "0.27.3",
+ "@esbuild/linux-riscv64": "0.27.3",
+ "@esbuild/linux-s390x": "0.27.3",
+ "@esbuild/linux-x64": "0.27.3",
+ "@esbuild/netbsd-arm64": "0.27.3",
+ "@esbuild/netbsd-x64": "0.27.3",
+ "@esbuild/openbsd-arm64": "0.27.3",
+ "@esbuild/openbsd-x64": "0.27.3",
+ "@esbuild/openharmony-arm64": "0.27.3",
+ "@esbuild/sunos-x64": "0.27.3",
+ "@esbuild/win32-arm64": "0.27.3",
+ "@esbuild/win32-ia32": "0.27.3",
+ "@esbuild/win32-x64": "0.27.3"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
+ "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "license": "ISC",
+ "peer": 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/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause",
+ "peer": true
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC",
+ "peer": true
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC",
+ "peer": true
+ },
+ "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==",
+ "license": "BlueOak-1.0.0",
+ "peer": true,
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "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==",
+ "license": "ISC",
+ "peer": true
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "9.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.8.tgz",
+ "integrity": "sha512-reYkDYtj/b19TeqbNZCV4q9t+Yxylf/rYBsLb42SXJatTv4/ylq5lEiAmhA/IToxO7NI2UzNMghHoHuaqDkAjw==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "license": "BlueOak-1.0.0",
+ "peer": true,
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "license": "BlueOak-1.0.0",
+ "peer": true
+ },
+ "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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "BlueOak-1.0.0",
+ "peer": 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/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/reflect-metadata": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+ "license": "Apache-2.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==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/sha.js": {
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
+ "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
+ "license": "(MIT AND BSD-3-Clause)",
+ "peer": true,
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.0"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/sql-highlight": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz",
+ "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==",
+ "funding": [
+ "https://github.com/scriptcoded/sql-highlight?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/scriptcoded"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/sql.js": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.0.tgz",
+ "integrity": "sha512-NXYh+kFqLiYRCNAaHD0PcbjFgXyjuolEKLMk5vRt2DgPENtF1kkNzzMlg42dUk5wIsH8MhUzsRhaUxIisoSlZQ==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": 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/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "peer": true,
+ "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==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/to-buffer": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
+ "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "isarray": "^2.0.5",
+ "safe-buffer": "^5.2.1",
+ "typed-array-buffer": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD",
+ "peer": true
+ },
+ "node_modules/tsx": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typeorm": {
+ "version": "0.3.28",
+ "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.28.tgz",
+ "integrity": "sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@sqltools/formatter": "^1.2.5",
+ "ansis": "^4.2.0",
+ "app-root-path": "^3.1.0",
+ "buffer": "^6.0.3",
+ "dayjs": "^1.11.19",
+ "debug": "^4.4.3",
+ "dedent": "^1.7.0",
+ "dotenv": "^16.6.1",
+ "glob": "^10.5.0",
+ "reflect-metadata": "^0.2.2",
+ "sha.js": "^2.4.12",
+ "sql-highlight": "^6.1.0",
+ "tslib": "^2.8.1",
+ "uuid": "^11.1.0",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "typeorm": "cli.js",
+ "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js",
+ "typeorm-ts-node-esm": "cli-ts-node-esm.js"
+ },
+ "engines": {
+ "node": ">=16.13.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/typeorm"
+ },
+ "peerDependencies": {
+ "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
+ "@sap/hana-client": "^2.14.22",
+ "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0",
+ "ioredis": "^5.0.4",
+ "mongodb": "^5.8.0 || ^6.0.0",
+ "mssql": "^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0",
+ "mysql2": "^2.2.5 || ^3.0.1",
+ "oracledb": "^6.3.0",
+ "pg": "^8.5.1",
+ "pg-native": "^3.0.0",
+ "pg-query-stream": "^4.0.0",
+ "redis": "^3.1.1 || ^4.0.0 || ^5.0.14",
+ "sql.js": "^1.4.0",
+ "sqlite3": "^5.0.3",
+ "ts-node": "^10.7.0",
+ "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@google-cloud/spanner": {
+ "optional": true
+ },
+ "@sap/hana-client": {
+ "optional": true
+ },
+ "better-sqlite3": {
+ "optional": true
+ },
+ "ioredis": {
+ "optional": true
+ },
+ "mongodb": {
+ "optional": true
+ },
+ "mssql": {
+ "optional": true
+ },
+ "mysql2": {
+ "optional": true
+ },
+ "oracledb": {
+ "optional": true
+ },
+ "pg": {
+ "optional": true
+ },
+ "pg-native": {
+ "optional": true
+ },
+ "pg-query-stream": {
+ "optional": true
+ },
+ "redis": {
+ "optional": true
+ },
+ "sql.js": {
+ "optional": true
+ },
+ "sqlite3": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ },
+ "typeorm-aurora-data-api-driver": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "uuid": "dist/esm/bin/uuid"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.20",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
+ "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": 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/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": 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-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "license": "ISC",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs/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==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "peer": 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==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/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==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ }
+ }
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package.json
new file mode 100644
index 00000000..6663a142
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package.json
@@ -0,0 +1,66 @@
+{
+ "name": "@query-doctor/sqlcommenter-typeorm",
+ "version": "0.1.0",
+ "description": "SQLCommenter patch for TypeORM",
+ "main": "dist/cjs/index.js",
+ "type": "module",
+ "types": "dist/esm/index.d.ts",
+ "module": "dist/esm/index.js",
+ "files": [
+ "dist"
+ ],
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/esm/index.d.ts",
+ "default": "./dist/esm/index.js"
+ },
+ "require": {
+ "types": "./dist/cjs/index.d.ts",
+ "default": "./dist/cjs/index.js"
+ }
+ },
+ "./http": {
+ "import": {
+ "types": "./dist/esm/http.d.ts",
+ "default": "./dist/esm/http.js"
+ },
+ "require": {
+ "types": "./dist/cjs/http.d.ts",
+ "default": "./dist/cjs/http.js"
+ }
+ }
+ },
+ "devDependencies": {
+ "@types/node": "^20.19.34",
+ "reflect-metadata": "^0.2.2",
+ "sql.js": "^1.12.0",
+ "tsx": "^4.20.5",
+ "typescript": "^5.9.3"
+ },
+ "dependencies": {
+ "@opentelemetry/api": "~1.9.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/core": ">=1.0.0",
+ "typeorm": ">=0.3.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "scripts": {
+ "test": "node --import=tsx --test test/**/*.spec.ts",
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/query-doctor/sqlcommenter.git"
+ },
+ "publishConfig": {
+ "access": "public",
+ "registry": "https://registry.npmjs.org"
+ },
+ "author": "Query Doctor",
+ "license": "Apache-2.0",
+ "packageManager": "npm@11.9.0"
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/als.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/als.ts
new file mode 100644
index 00000000..225da800
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/als.ts
@@ -0,0 +1,4 @@
+import { AsyncLocalStorage } from "node:async_hooks";
+import type { RequestContext } from "./request-context.js";
+
+export const als = new AsyncLocalStorage();
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/http.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/http.ts
new file mode 100644
index 00000000..2efcbb90
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/http.ts
@@ -0,0 +1,16 @@
+import { als } from "./als.js";
+import type { RequestContext } from "./request-context.js";
+
+/**
+ * Wraps the next function in the AsyncLocalStorage with the request context.
+ * Used to get `route` and `controller` information from the request into the query
+ * without exposing the underlying AsyncLocalStorage API.
+ */
+export function withRequestContext(
+ context: RequestContext,
+ next: () => Promise,
+) {
+ als.run(context, next);
+}
+
+export type { RequestContext, WellKnownFields } from "./request-context.js";
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/index.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/index.ts
new file mode 100644
index 00000000..4ed1186c
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/index.ts
@@ -0,0 +1,130 @@
+import { alreadyHasTrailingComment, serializeTags, type Tag } from "./sqlcommenter.js";
+import { als } from "./als.js";
+import { pushW3CTraceContext } from "./tracing.js";
+import { resolveFilePath } from "./path.js";
+
+const LIBRARY_NAME = "sqlcommenter-typeorm";
+
+type DataSourceLike = {
+ createQueryRunner: (...args: any[]) => QueryRunnerLike;
+};
+
+type QueryRunnerLike = {
+ query: (
+ query: string,
+ parameters?: any[],
+ useStructuredResult?: boolean,
+ ) => Promise;
+};
+
+const PATCHED = Symbol("sqlcommenter-patched");
+
+function isValidCaller(line: string): boolean {
+ if (line.includes("node_modules")) {
+ return false;
+ }
+ if (line.includes(`${LIBRARY_NAME}/test/`)) {
+ return true;
+ }
+ if (line.includes(LIBRARY_NAME)) {
+ return false;
+ }
+ return true;
+}
+
+// (file.ts:12:12) or file.ts:12:12
+const filepathRegex = /([^ (]*?:\d+:\d+)\)?$/;
+
+export function traceCaller(): string | undefined {
+ const stack = new Error().stack;
+ if (!stack) {
+ return;
+ }
+ // skip 1 line for `Error:`, 1 line for the caller of the current function
+ const stackLines = stack.split("\n").slice(2);
+ const methodCaller = stackLines.find(isValidCaller);
+ if (!methodCaller) {
+ return;
+ }
+ const match = methodCaller.match(filepathRegex);
+ if (match) {
+ return resolveFilePath(match[1]);
+ }
+}
+
+const WellKnownFields = {
+ dbDriver: "db_driver",
+ file: "file",
+ route: "route",
+} as const;
+
+function patchQueryRunnerPrototype(proto: any) {
+ if (proto[PATCHED]) {
+ return;
+ }
+ const originalQuery = proto.query;
+ if (typeof originalQuery !== "function") {
+ console.debug(
+ "Invalid QueryRunner prototype. Missing `query`, did TypeORM change its API?",
+ proto,
+ );
+ return;
+ }
+ proto.query = new Proxy(originalQuery, {
+ async apply(target, thisArg, args) {
+ try {
+ const [query] = args;
+ if (typeof query === "string" && !alreadyHasTrailingComment(query)) {
+ const caller = traceCaller();
+ const requestContext = als.getStore();
+ const tags: Tag[] = [
+ [WellKnownFields.dbDriver, "typeorm"],
+ ];
+ pushW3CTraceContext(tags);
+ if (caller) {
+ tags.push([WellKnownFields.file, caller]);
+ }
+ if (requestContext) {
+ for (const key in requestContext) {
+ tags.push([key, String(requestContext[key])]);
+ }
+ }
+ const sqlComment = serializeTags(tags);
+ args[0] = query + sqlComment;
+ }
+ } catch {
+ // never let comment generation break query execution
+ }
+ return Reflect.apply(target, thisArg, args);
+ },
+ });
+ proto[PATCHED] = true;
+}
+
+/**
+ * Patches a TypeORM DataSource to append sqlcommenter tags to all queries.
+ * Call this after creating the DataSource (before or after initialize).
+ *
+ * @example
+ * ```ts
+ * import { DataSource } from "typeorm";
+ * import { patchTypeORM } from "@query-doctor/sqlcommenter-typeorm";
+ *
+ * const dataSource = patchTypeORM(new DataSource({
+ * type: "postgres",
+ * url: process.env.DATABASE_URL,
+ * }));
+ * ```
+ */
+export function patchTypeORM(dataSource: T): T {
+ const originalCreateQueryRunner = dataSource.createQueryRunner;
+ dataSource.createQueryRunner = new Proxy(originalCreateQueryRunner, {
+ apply(target, thisArg, args) {
+ const queryRunner = Reflect.apply(target, thisArg, args);
+ const proto = Object.getPrototypeOf(queryRunner);
+ patchQueryRunnerPrototype(proto);
+ return queryRunner;
+ },
+ });
+ return dataSource;
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts
new file mode 100644
index 00000000..811866bd
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts
@@ -0,0 +1,80 @@
+import { existsSync } from "node:fs";
+import { dirname, join } from "node:path";
+
+let cachedProjectRoot: string | undefined;
+
+/**
+ * Finds the project root by walking up from `process.cwd()` looking for `tsconfig.json`.
+ *
+ * In deployed environments, `process.cwd()` may not be the project root
+ * (e.g., `cd .amplify-hosting/compute/default/ && node app.js`).
+ * Walking up to find `tsconfig.json` — which is never copied to deployment directories —
+ * gives us the real project root.
+ *
+ * The result is cached since the project root doesn't change during a process's lifetime.
+ */
+export function findProjectRoot(): string {
+ if (cachedProjectRoot !== undefined) {
+ return cachedProjectRoot;
+ }
+ let projectRoot = process.cwd();
+ for (let d = projectRoot; d !== dirname(d); d = dirname(d)) {
+ if (existsSync(join(d, "tsconfig.json"))) {
+ projectRoot = d;
+ break;
+ }
+ }
+ cachedProjectRoot = projectRoot;
+ return projectRoot;
+}
+
+/**
+ * Resolves a file path from a stack trace to a correct absolute path.
+ *
+ * When compiled JS is relocated (e.g., postbuild copies `dist/` to a deployment directory),
+ * source-map-resolved paths become incorrect because the relative `sources` entries in
+ * `.map` files resolve against the new location instead of the original project.
+ *
+ * This extracts the `src/`-relative portion and reconstructs the path using the real
+ * project root.
+ *
+ * @param raw - A stack trace entry like "/wrong/path/src/routes/admin.ts:12:15"
+ * @returns The resolved path like "/project/root/src/routes/admin.ts:12:15"
+ */
+export function resolveFilePath(raw: string): string {
+ // Split off :line:column suffix
+ const match = raw.match(/^(.*?):(\d+:\d+)$/);
+ if (!match) {
+ return raw;
+ }
+ const [, filePath, lineCol] = match;
+ const srcIdx = filePath.indexOf("src/");
+ if (srcIdx < 0) {
+ return raw;
+ }
+ const projectRoot = findProjectRoot();
+ const relativePath = filePath.substring(srcIdx);
+ const resolved = `${projectRoot}/${relativePath}`;
+ return `${applyWslPrefix(resolved)}:${lineCol}`;
+}
+
+/**
+ * Prefixes an absolute path with the WSL network path when running inside WSL.
+ *
+ * Inside WSL, absolute paths like `/home/user/project/...` can't be resolved
+ * from Windows-side tooling (e.g., clickable links in dashboards or VS Code).
+ * The `WSL_DISTRO_NAME` env var is always set inside WSL, and the path format
+ * `//wsl.localhost//...` makes paths accessible from Windows.
+ */
+export function applyWslPrefix(filePath: string): string {
+ const distro = process.env.WSL_DISTRO_NAME;
+ if (distro) {
+ return `//wsl.localhost/${distro}${filePath}`;
+ }
+ return filePath;
+}
+
+/** @internal Exposed for testing only */
+export function _resetProjectRootCache() {
+ cachedProjectRoot = undefined;
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/request-context.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/request-context.ts
new file mode 100644
index 00000000..23d99f33
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/request-context.ts
@@ -0,0 +1,18 @@
+export const WellKnownFields = {
+ route: "route",
+ method: "method",
+ controller: "controller",
+} as const;
+
+/**
+ * A context object with values that will be passed along to the final emitted query comments.
+ * Can support well-known fields used by existing sqlcommenter-compatible tooling and
+ * arbitrary key-value pairs.
+ */
+export type RequestContext = {
+ [WellKnownFields.route]: string;
+ [WellKnownFields.method]?: string;
+ [WellKnownFields.controller]?: string;
+ // the user can choose to add any other information to the context
+ [key: string]: unknown;
+};
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/sqlcommenter.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/sqlcommenter.ts
new file mode 100644
index 00000000..9a3352a2
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/sqlcommenter.ts
@@ -0,0 +1,54 @@
+export type Tag = [string, string];
+
+function escapeMetaCharacters(value: string): string {
+ return value.replaceAll("'", "\\'");
+}
+
+function serializeKey(key: string): string {
+ return escapeMetaCharacters(encodeURIComponent(key));
+}
+
+function serializeValue(value: unknown): string {
+ const encoded = encodeURIComponent(String(value));
+ const metaEscaped = escapeMetaCharacters(encoded);
+ const final = `'${metaEscaped}'`;
+ return final;
+}
+
+function isEmpty(tags: Tag[]): boolean {
+ return tags.length === 0;
+}
+
+function sort(kvPairs: string[]): string[] {
+ return kvPairs.sort((a, b) => a.localeCompare(b));
+}
+
+export function serializeTags(tags: Tag[]): string {
+ if (isEmpty(tags)) {
+ return "";
+ }
+ const parts: string[] = [];
+ for (const [k, v] of tags) {
+ try {
+ const key = serializeKey(k);
+ const value = serializeValue(v);
+ parts.push(`${key}=${value}`);
+ } catch (e) {
+ // ignore errors in serialization and skip pair
+ console.error("Error encoding key", e);
+ }
+ }
+ const sorted = sort(parts);
+ const concatenated = sorted.join(",");
+ return `/*${concatenated}*/`;
+}
+
+/**
+ * Checks if the query already has a TRAILING SQL comment (i.e. sqlcommenter tags
+ * already appended). Unlike the drizzle version which checks for `* /` anywhere,
+ * this only checks the end of the query so that TypeORM's QueryBuilder.comment()
+ * (which prepends comments) doesn't prevent us from appending sqlcommenter tags.
+ */
+export function alreadyHasTrailingComment(sql: string): boolean {
+ return sql.trimEnd().endsWith("*/");
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/tracing.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/tracing.ts
new file mode 100644
index 00000000..40a88820
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/tracing.ts
@@ -0,0 +1,14 @@
+import type { Tag } from "./sqlcommenter.js";
+import { context, type TextMapSetter } from "@opentelemetry/api";
+import { W3CTraceContextPropagator } from "@opentelemetry/core";
+
+const sqlcommentAppender: TextMapSetter = {
+ set(context, key, value) {
+ context.push([key, value]);
+ },
+};
+
+export function pushW3CTraceContext(tags: Tag[]) {
+ let propagator = new W3CTraceContextPropagator();
+ propagator.inject(context.active(), tags, sqlcommentAppender);
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/driver-integration.spec.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/driver-integration.spec.ts
new file mode 100644
index 00000000..e14f6169
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/driver-integration.spec.ts
@@ -0,0 +1,188 @@
+import { test } from "node:test";
+import assert from "node:assert";
+import { DataSource, EntitySchema } from "typeorm";
+import { patchTypeORM } from "../src/index.js";
+
+const UserEntity = new EntitySchema({
+ name: "User",
+ tableName: "users",
+ columns: {
+ id: { type: Number, primary: true, generated: true },
+ name: { type: String },
+ },
+});
+
+function createDataSource() {
+ return new DataSource({
+ type: "sqljs",
+ entities: [UserEntity],
+ synchronize: true,
+ logging: false,
+ });
+}
+
+function interceptQueries(dataSource: DataSource): string[] {
+ const executedQueries: string[] = [];
+ const db = (dataSource.driver as any).databaseConnection;
+ const originalPrepare = db.prepare.bind(db);
+ db.prepare = function (sql: string) {
+ executedQueries.push(sql);
+ return originalPrepare(sql);
+ };
+ return executedQueries;
+}
+
+test("tags appended through Repository.find()", async () => {
+ const dataSource = patchTypeORM(createDataSource());
+ await dataSource.initialize();
+
+ // Intercept AFTER synchronize to only capture our queries
+ const executedQueries = interceptQueries(dataSource);
+
+ try {
+ const repo = dataSource.getRepository("User");
+ await repo.find();
+
+ const taggedQuery = executedQueries.find(
+ (q) => q.includes("users") && q.includes("db_driver"),
+ );
+ assert.ok(
+ taggedQuery,
+ `Expected a tagged query via Repository.find(), got: ${executedQueries.join(", ")}`,
+ );
+ assert.match(taggedQuery!, /db_driver='typeorm'/);
+ } finally {
+ await dataSource.destroy();
+ }
+});
+
+test("tags appended through Repository.save() and findOneBy()", async () => {
+ const dataSource = patchTypeORM(createDataSource());
+ await dataSource.initialize();
+
+ const executedQueries = interceptQueries(dataSource);
+
+ try {
+ const repo = dataSource.getRepository("User");
+ await repo.save({ name: "alice" });
+
+ const insertQuery = executedQueries.find(
+ (q) => q.includes("INSERT") && q.includes("db_driver"),
+ );
+ assert.ok(
+ insertQuery,
+ `Expected a tagged INSERT via Repository.save(), got: ${executedQueries.join(", ")}`,
+ );
+ assert.match(insertQuery!, /db_driver='typeorm'/);
+
+ executedQueries.length = 0;
+
+ await repo.findOneBy({ name: "alice" });
+
+ const selectQuery = executedQueries.find(
+ (q) => q.includes("users") && q.includes("db_driver"),
+ );
+ assert.ok(
+ selectQuery,
+ `Expected a tagged SELECT via Repository.findOneBy(), got: ${executedQueries.join(", ")}`,
+ );
+ assert.match(selectQuery!, /db_driver='typeorm'/);
+ } finally {
+ await dataSource.destroy();
+ }
+});
+
+test("tags appended through QueryBuilder", async () => {
+ const dataSource = patchTypeORM(createDataSource());
+ await dataSource.initialize();
+
+ const repo = dataSource.getRepository("User");
+ await repo.save({ name: "bob" });
+
+ const executedQueries = interceptQueries(dataSource);
+
+ try {
+ await repo.createQueryBuilder("user").where("user.name = :name", { name: "bob" }).getMany();
+
+ const taggedQuery = executedQueries.find(
+ (q) => q.includes("users") && q.includes("db_driver"),
+ );
+ assert.ok(
+ taggedQuery,
+ `Expected a tagged query via QueryBuilder, got: ${executedQueries.join(", ")}`,
+ );
+ assert.match(taggedQuery!, /db_driver='typeorm'/);
+ } finally {
+ await dataSource.destroy();
+ }
+});
+
+test("tags still appended when QueryBuilder.comment() is used", async () => {
+ const dataSource = patchTypeORM(createDataSource());
+ await dataSource.initialize();
+
+ const repo = dataSource.getRepository("User");
+ await repo.save({ name: "carol" });
+
+ const executedQueries = interceptQueries(dataSource);
+
+ try {
+ await repo
+ .createQueryBuilder("user")
+ .comment("my-trace-id")
+ .getMany();
+
+ const taggedQuery = executedQueries.find(
+ (q) => q.includes("users") && q.includes("db_driver"),
+ );
+ assert.ok(
+ taggedQuery,
+ `Expected sqlcommenter tags even with QueryBuilder.comment(), got: ${executedQueries.join(", ")}`,
+ );
+ assert.match(taggedQuery!, /\/\* my-trace-id \*\//);
+ assert.match(taggedQuery!, /db_driver='typeorm'/);
+ } finally {
+ await dataSource.destroy();
+ }
+});
+
+test("skips queries that already have trailing sqlcommenter tags", async () => {
+ const dataSource = patchTypeORM(createDataSource());
+ await dataSource.initialize();
+ const executedQueries = interceptQueries(dataSource);
+
+ try {
+ const qr = dataSource.createQueryRunner();
+ try {
+ await qr.query("SELECT 1 as result /*db_driver='something'*/");
+ } finally {
+ await qr.release();
+ }
+
+ const query = executedQueries.find((q) => q.includes("db_driver"));
+ assert.ok(query);
+ const commentCount = (query!.match(/db_driver/g) || []).length;
+ assert.strictEqual(
+ commentCount,
+ 1,
+ "Should not add another comment when trailing sqlcommenter tags exist",
+ );
+ } finally {
+ await dataSource.destroy();
+ }
+});
+
+test("query result is preserved", async () => {
+ const dataSource = patchTypeORM(createDataSource());
+ await dataSource.initialize();
+
+ try {
+ const repo = dataSource.getRepository("User");
+ await repo.save({ name: "dave" });
+ const user = await repo.findOneBy({ name: "dave" });
+ assert.ok(user);
+ assert.strictEqual(user.name, "dave");
+ } finally {
+ await dataSource.destroy();
+ }
+});
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts
new file mode 100644
index 00000000..a379bb32
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts
@@ -0,0 +1,121 @@
+import { test } from "node:test";
+import assert from "node:assert";
+import { existsSync } from "node:fs";
+import { join } from "node:path";
+import {
+ findProjectRoot,
+ resolveFilePath,
+ applyWslPrefix,
+ _resetProjectRootCache,
+} from "../src/path.js";
+
+test("findProjectRoot", async (t) => {
+ t.afterEach(() => {
+ _resetProjectRootCache();
+ });
+
+ await t.test("returns a directory containing tsconfig.json", () => {
+ const root = findProjectRoot();
+ assert.ok(
+ existsSync(join(root, "tsconfig.json")),
+ `Expected ${root} to contain tsconfig.json`,
+ );
+ });
+
+ await t.test("caches the result across calls", () => {
+ const first = findProjectRoot();
+ const second = findProjectRoot();
+ assert.strictEqual(first, second);
+ });
+});
+
+test("resolveFilePath", async (t) => {
+ t.afterEach(() => {
+ _resetProjectRootCache();
+ });
+
+ await t.test("resolves path with src/ to project root", () => {
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath(
+ "/wrong/deploy/dir/src/routes/admin.ts:12:15",
+ );
+ assert.strictEqual(result, `${projectRoot}/src/routes/admin.ts:12:15`);
+ });
+
+ await t.test("leaves path without src/ unchanged", () => {
+ const result = resolveFilePath("/some/other/path/routes/admin.ts:5:10");
+ assert.strictEqual(result, "/some/other/path/routes/admin.ts:5:10");
+ });
+
+ await t.test("preserves line:column suffix", () => {
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath("/bad/path/src/index.ts:99:3");
+ assert.strictEqual(result, `${projectRoot}/src/index.ts:99:3`);
+ });
+
+ await t.test("uses first src/ occurrence", () => {
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath(
+ "/deploy/src/nested/src/routes/admin.ts:1:1",
+ );
+ assert.strictEqual(
+ result,
+ `${projectRoot}/src/nested/src/routes/admin.ts:1:1`,
+ );
+ });
+
+ await t.test("returns raw string if no line:column suffix", () => {
+ const result = resolveFilePath("/some/path/src/file.ts");
+ assert.strictEqual(result, "/some/path/src/file.ts");
+ });
+});
+
+test("applyWslPrefix", async (t) => {
+ const originalWslDistro = process.env.WSL_DISTRO_NAME;
+
+ t.afterEach(() => {
+ if (originalWslDistro === undefined) {
+ delete process.env.WSL_DISTRO_NAME;
+ } else {
+ process.env.WSL_DISTRO_NAME = originalWslDistro;
+ }
+ });
+
+ await t.test("prefixes path when WSL_DISTRO_NAME is set", () => {
+ process.env.WSL_DISTRO_NAME = "Ubuntu";
+ const result = applyWslPrefix("/home/user/project/src/index.ts");
+ assert.strictEqual(
+ result,
+ "//wsl.localhost/Ubuntu/home/user/project/src/index.ts",
+ );
+ });
+
+ await t.test("returns path unchanged when WSL_DISTRO_NAME is not set", () => {
+ delete process.env.WSL_DISTRO_NAME;
+ const result = applyWslPrefix("/home/user/project/src/index.ts");
+ assert.strictEqual(result, "/home/user/project/src/index.ts");
+ });
+});
+
+test("resolveFilePath with WSL", async (t) => {
+ const originalWslDistro = process.env.WSL_DISTRO_NAME;
+
+ t.afterEach(() => {
+ _resetProjectRootCache();
+ if (originalWslDistro === undefined) {
+ delete process.env.WSL_DISTRO_NAME;
+ } else {
+ process.env.WSL_DISTRO_NAME = originalWslDistro;
+ }
+ });
+
+ await t.test("applies WSL prefix to resolved src/ paths", () => {
+ process.env.WSL_DISTRO_NAME = "Ubuntu";
+ const projectRoot = findProjectRoot();
+ const result = resolveFilePath("/wrong/path/src/routes/admin.ts:12:15");
+ assert.strictEqual(
+ result,
+ `//wsl.localhost/Ubuntu${projectRoot}/src/routes/admin.ts:12:15`,
+ );
+ });
+});
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/tsconfig.cjs.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/tsconfig.cjs.json
new file mode 100644
index 00000000..49f03bb2
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/tsconfig.cjs.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "outDir": "./dist/cjs"
+ }
+}
diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/tsconfig.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/tsconfig.json
new file mode 100644
index 00000000..de4e28fe
--- /dev/null
+++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "target": "es2018",
+ "lib": ["es2022"],
+ "module": "es2020",
+ "moduleResolution": "bundler",
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true,
+ "outDir": "./dist/esm",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "skipLibCheck": true
+ },
+ "exclude": ["test", "dist", "node_modules"]
+}