diff --git a/.gitignore b/.gitignore index c88e68b..b86308f 100644 --- a/.gitignore +++ b/.gitignore @@ -176,4 +176,7 @@ dist pgdata/ -.npmrc \ No newline at end of file +.npmrc +lab +.vscode +.arc/ \ No newline at end of file diff --git a/README.md b/README.md index 558c071..25d13f8 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,40 @@ # kysely-pglite -[Kysely](https://github.com/kysely-org/kysely) dialect for [PGlite](https://github.com/electric-sql/pglite). - -Generate types using the provided CLI. +[Kysely](https://github.com/kysely-org/kysely) dialect for [PGlite](https://github.com/electric-sql/pglite) with a [CLI](#generating-types) to generate TypeScript types. Kysely specific wrapper over PGlite's [Live Queries](https://pglite.dev/docs/live-queries) extension to take advantage of Kysely's type-safe features. +## Installation + +[`@electric-sql/pglite`](https://github.com/electric-sql/pglite) needs to be installed as well. + +#### PNPM + +```bash +pnpm add @electric-sql/pglite kysely-pglite +``` + +#### NPM + +```bash +npm install @electric-sql/pglite kysely-pglite +``` + +#### Yarn + +```bash +yarn add @electric-sql/pglite kysely-pglite +``` + ## Usage -The examples below use the static async method `await KyselyPGlite.create()` to align with the preferred way to create a PGlite instance as stated in the [PGlite docs](https://pglite.dev/docs/api#main-constructor). But an instance can still be created using the `new KyselyPGlite()` constructor. +The examples below mostly use the static async method `await KyselyPGlite.create()` to align with the [PGlite docs](https://pglite.dev/docs/api#main-constructor). But an instance can still be created using the `new KyselyPGlite()` constructor. ```typescript import { Kysely } from 'kysely' import { KyselyPGlite } from 'kysely-pglite' -// This will use in-memory Postgres +// Use in-memory Postgres const { dialect } = await KyselyPGlite.create() // For persisting the data to disk, pass in a path to a directory @@ -23,7 +43,7 @@ const { dialect } = await KyselyPGlite.create() const db = new Kysely({ dialect }) ``` -`PGlite` options can be passed in as the second parameter. See [PGlite options](https://pglite.dev/docs/api#options) for more info. +`PGlite` options can be passed in, it has the same function signature as PGlite. See [PGlite options](https://pglite.dev/docs/api#options) for more info. ```typescript const { dialect } = await KyselyPGlite.create('./path/to/pgdata', { @@ -34,21 +54,50 @@ const { dialect } = await KyselyPGlite.create('./path/to/pgdata', { ## Generating Types -`kysely-pglite` has a CLI to generate TypeScript types. It's a wrapper around [kysely-codegen](https://github.com/RobinBlomberg/kysely-codegen) to get around its requirement of a connection to a running database. +`kysely-pglite` provides a CLI to generate TypeScript types. It's a wrapper around [kysely-codegen](https://github.com/RobinBlomberg/kysely-codegen) to get around its requirement of a connection to a running database. So the CLI accepts most of `kysely-codegen`'s options just minus the connection specific settings. + +Without a running database to connect to, the codegen needs a `path` to a file/directory of Kysely migrations or to a persisted PGlite database. + +Using Kysely migrations, the `kysely-pglite` CLI expects a path to either a file or directory of migration files that exports 2 async functions called `up` and `down` (same pattern as in the [Kysely docs](https://kysely.dev/docs/migrations#migration-files)). For example: -You'll need to point the `kysely-pglite` CLI to a file that exports a `Kysely` instance or to a directory that stores the persisted Postgres database. +```typescript +// src/db/migrations/2024-05-04-create-user.ts +import { Kysely } from 'kysely' + +export async function up(db: Kysely) { + await db.schema + .createTable('user') + .ifNotExists() + .addColumn('id', 'serial', (cb) => cb.primaryKey()) + .addColumn('name', 'text', (cb) => cb.notNull()) + .execute() +} -If using a Kysely instance, the CLI will look through file's exports and find the Kysely instance so the object doesn't need to be named `db` or be the only thing exported. +export async function down(db: Kysely) { + await db.schema.dropTable('user').execute() +} +``` ```bash -npx kysely-pglite ./path/to/your/db.ts +npx kysely-pglite ./src/db/migrations --out-file ./src/db/types.ts ``` +> [!TIP] +> The CLI is also aliased as `kpg` for easier typing. + +A persisted PGlite's database can also be used to generate the types. `kysely-pglite` will automatically detect that the directory is a PGlite database and not migration files. + ```bash -npx kysely-pglite --data-dir ./path/to/pgdata +npx kysely-pglite ./path/to/pgdata ``` -You can also use the `Codegen` class to have more flexibilty +There's also a `--watch` option to make `kysely-pglite` watch the given `path` and automatically re-generate the types whenever a change is detected. + +```bash +npx kysely-pglite --watch ./src/db/migrations --out-file ./src/db/types.ts +``` + +You can also import the `Codegen` class directly to have more flexibilty: ```typescript import { Codegen } from 'kysely-pglite' @@ -57,7 +106,7 @@ const { dialect } = new KyselyPGlite() const codegen = new Codegen(dialect) -// See `kysely-pglite --help` for more options +// See `kysely-pglite help` for more options const types = await codegen.generate({ // Your Kysely DB db, @@ -67,11 +116,9 @@ const types = await codegen.generate({ console.log(types) // stringified types ``` -If you're starting fresh, you will probably need to create a temporary empty `interface DB {}` to create a Kysely instance, generate the types then update it to the generated `DB`. - ## `KyselyLive` Usage -`KyselyLive` is a "bridge" for using PGlite's live queries extension and Kysely's type-safe features. To quickly compare: +`KyselyLive` is a [AsyncIterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncIterator) class for using PGlite's live queries extension with Kysely's type-safe features when defining queries watch. To quickly compare: ```typescript const ret = pg.live.query( @@ -136,7 +183,7 @@ liveQuery.refresh() ## Migrations -Kysely migrations work well with `PGlite`. See example setup below: +Kysely migrations work well with `PGlite`. This example setup is mostly relevant when using in-memory storage as you'll probably need to create tables during server startup. ```typescript // file: migrations/2025-08-01-create-user-table.ts @@ -228,37 +275,3 @@ app.listen(port, () => { console.log(`Example app listening on port ${port}`) }) ``` - -> [!WARNING] -> Applying migrations with `await` in the same file that runs your server could potentially impact its start-up time if there are a lot of migrations to run. - -## Installation - -[`@electric-sql/pglite`](https://github.com/electric-sql/pglite) needs to be installed as well. - -#### PNPM - -```bash -pnpm add @electric-sql/pglite kysely-pglite -``` - -#### NPM - -```bash -npm install @electric-sql/pglite kysely-pglite -``` - -#### Yarn - -```bash -yarn add @electric-sql/pglite kysely-pglite -``` - -> [!WARNING] -> This dialect has not been tested on Deno yet. - -## Todos - -- Verify browser usage. `kysely` and `pglite` both work in browser - -- Verify works on Deno diff --git a/bin/dev.cmd b/bin/dev.cmd new file mode 100644 index 0000000..cec553b --- /dev/null +++ b/bin/dev.cmd @@ -0,0 +1,3 @@ +@echo off + +node --loader ts-node/esm --no-warnings=ExperimentalWarning "%~dp0\dev" %* diff --git a/bin/dev.js b/bin/dev.js new file mode 100755 index 0000000..cb41ebc --- /dev/null +++ b/bin/dev.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +// eslint-disable-next-line n/shebang +import { execute } from '@oclif/core' + +await execute({ development: true, dir: import.meta.url }) diff --git a/bin/run.cmd b/bin/run.cmd new file mode 100644 index 0000000..968fc30 --- /dev/null +++ b/bin/run.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\run" %* diff --git a/bin/run.js b/bin/run.js new file mode 100755 index 0000000..dd50271 --- /dev/null +++ b/bin/run.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +import {execute} from '@oclif/core' + +await execute({dir: import.meta.url}) diff --git a/package.json b/package.json index 3214992..b6a01a0 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "packageManager": "pnpm@8.14.2", "scripts": { "build": "rimraf dist && tsc", + "dev": "tsc --watch", "typecheck": "tsc --noEmit", "prepublishOnly": "pnpm run build", "prepare": "pnpm run build", @@ -27,7 +28,11 @@ "files": [ "dist" ], - "bin": "./dist/cli.js", + "imports": { + "#*": { + "default": "./dist/*" + } + }, "exports": { "import": { "types": "./dist/index.d.ts", @@ -39,22 +44,48 @@ "kysely": "*" }, "devDependencies": { - "@repeaterjs/repeater": "^3.0.6", + "@types/fs-extra": "^11.0.4", "@types/node": "^22.5.0", "prettier": "^3.3.3", "rimraf": "^6.0.1", - "tsx": "^4.18.0", + "tsx": "^4.19.0", "type-fest": "^4.25.0", - "typescript": "^5.0.0", - "vitest": "^2.0.3" + "typescript": "^5.5.4", + "vitest": "^2.0.5" }, - "author": "Daniel Sandiego", "dependencies": { + "@oclif/core": "^4.0.19", + "@repeaterjs/repeater": "^3.0.6", "@sindresorhus/is": "^7.0.0", - "cleye": "^1.3.2", + "chokidar": "^3.6.0", + "consola": "^3.2.3", + "fs-extra": "^11.2.0", "globby": "^14.0.2", "jiti": "2.0.0-beta.3", "kysely-codegen": "^0.15.0", "radash": "^12.1.0" - } + }, + "bin": { + "kysely-pglite": "./bin/run.js", + "kpg": "./bin/run.js" + }, + "oclif": { + "binAliases": [ + "kysely-pglite", + "kpg" + ], + "commands": { + "strategy": "single", + "target": "./dist/cli.js" + }, + "dirname": "kysely-pglite", + "topicSeparator": " " + }, + "pnpm": { + "allowedDeprecatedVersions": { + "glob": "*", + "inflight": "*" + } + }, + "author": "Daniel Sandiego" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index def8f2a..47be728 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,13 +7,25 @@ settings: dependencies: '@electric-sql/pglite': specifier: '*' - version: 0.2.0 + version: 0.2.4 + '@oclif/core': + specifier: ^4.0.19 + version: 4.0.19 + '@repeaterjs/repeater': + specifier: ^3.0.6 + version: 3.0.6 '@sindresorhus/is': specifier: ^7.0.0 version: 7.0.0 - cleye: - specifier: ^1.3.2 - version: 1.3.2 + chokidar: + specifier: ^3.6.0 + version: 3.6.0 + consola: + specifier: ^3.2.3 + version: 3.2.3 + fs-extra: + specifier: ^11.2.0 + version: 11.2.0 globby: specifier: ^14.0.2 version: 14.0.2 @@ -31,9 +43,9 @@ dependencies: version: 12.1.0 devDependencies: - '@repeaterjs/repeater': - specifier: ^3.0.6 - version: 3.0.6 + '@types/fs-extra': + specifier: ^11.0.4 + version: 11.0.4 '@types/node': specifier: ^22.5.0 version: 22.5.0 @@ -44,17 +56,17 @@ devDependencies: specifier: ^6.0.1 version: 6.0.1 tsx: - specifier: ^4.18.0 - version: 4.18.0 + specifier: ^4.19.0 + version: 4.19.0 type-fest: specifier: ^4.25.0 version: 4.25.0 typescript: - specifier: ^5.0.0 - version: 5.5.3 + specifier: ^5.5.4 + version: 5.5.4 vitest: - specifier: ^2.0.3 - version: 2.0.3(@types/node@22.5.0) + specifier: ^2.0.5 + version: 2.0.5(@types/node@22.5.0) packages: @@ -66,8 +78,8 @@ packages: '@jridgewell/trace-mapping': 0.3.25 dev: true - /@electric-sql/pglite@0.2.0: - resolution: {integrity: sha512-9ckSTnr9ChwY03lojiHM3HIOKFen72koxo44hb94Qz/L9fNejg1riwbfl2ROghA/z4ntCuyT+Zwl5pkbrSX3Aw==} + /@electric-sql/pglite@0.2.4: + resolution: {integrity: sha512-NN1ATH9aYTCD4257wZH1CjAKdro8jDd5r/BumxZtEVTDNDztBmPYYtBd4TEIHWdi0+QuM7SWk2JRCLvAYCSEWg==} dev: false /@esbuild/aix-ppc64@0.21.5: @@ -556,6 +568,29 @@ packages: fastq: 1.17.1 dev: false + /@oclif/core@4.0.19: + resolution: {integrity: sha512-VXnsYNVfmucXp5BdOA/OcWi8F/h2h8ofW1GxQDdspodnmnUgALEpqrxXBl5NFuA+iEihtAJeXzX260ICHYDaBg==} + engines: {node: '>=18.0.0'} + dependencies: + ansi-escapes: 4.3.2 + ansis: 3.3.2 + clean-stack: 3.0.1 + cli-spinners: 2.9.2 + debug: 4.3.6(supports-color@8.1.1) + ejs: 3.1.10 + get-package-type: 0.1.0 + globby: 11.1.0 + indent-string: 4.0.0 + is-wsl: 2.2.0 + lilconfig: 3.1.2 + minimatch: 9.0.5 + string-width: 4.2.3 + supports-color: 8.1.1 + widest-line: 3.1.0 + wordwrap: 1.0.0 + wrap-ansi: 7.0.0 + dev: false + /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -565,130 +600,130 @@ packages: /@repeaterjs/repeater@3.0.6: resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} - dev: true + dev: false - /@rollup/rollup-android-arm-eabi@4.18.1: - resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} + /@rollup/rollup-android-arm-eabi@4.21.1: + resolution: {integrity: sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-android-arm64@4.18.1: - resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} + /@rollup/rollup-android-arm64@4.21.1: + resolution: {integrity: sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-arm64@4.18.1: - resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} + /@rollup/rollup-darwin-arm64@4.21.1: + resolution: {integrity: sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-x64@4.18.1: - resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} + /@rollup/rollup-darwin-x64@4.21.1: + resolution: {integrity: sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.18.1: - resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} + /@rollup/rollup-linux-arm-gnueabihf@4.21.1: + resolution: {integrity: sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-musleabihf@4.18.1: - resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} + /@rollup/rollup-linux-arm-musleabihf@4.21.1: + resolution: {integrity: sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.18.1: - resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} + /@rollup/rollup-linux-arm64-gnu@4.21.1: + resolution: {integrity: sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-musl@4.18.1: - resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} + /@rollup/rollup-linux-arm64-musl@4.21.1: + resolution: {integrity: sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.18.1: - resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} + /@rollup/rollup-linux-powerpc64le-gnu@4.21.1: + resolution: {integrity: sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==} cpu: [ppc64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.18.1: - resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} + /@rollup/rollup-linux-riscv64-gnu@4.21.1: + resolution: {integrity: sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-s390x-gnu@4.18.1: - resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} + /@rollup/rollup-linux-s390x-gnu@4.21.1: + resolution: {integrity: sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==} cpu: [s390x] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-gnu@4.18.1: - resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} + /@rollup/rollup-linux-x64-gnu@4.21.1: + resolution: {integrity: sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-musl@4.18.1: - resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} + /@rollup/rollup-linux-x64-musl@4.21.1: + resolution: {integrity: sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.18.1: - resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} + /@rollup/rollup-win32-arm64-msvc@4.21.1: + resolution: {integrity: sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.18.1: - resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} + /@rollup/rollup-win32-ia32-msvc@4.21.1: + resolution: {integrity: sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-x64-msvc@4.18.1: - resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} + /@rollup/rollup-win32-x64-msvc@4.21.1: + resolution: {integrity: sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==} cpu: [x64] os: [win32] requiresBuild: true @@ -709,61 +744,80 @@ packages: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true + /@types/fs-extra@11.0.4: + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} + dependencies: + '@types/jsonfile': 6.1.4 + '@types/node': 22.5.0 + dev: true + + /@types/jsonfile@6.1.4: + resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + dependencies: + '@types/node': 22.5.0 + dev: true + /@types/node@22.5.0: resolution: {integrity: sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==} dependencies: undici-types: 6.19.8 dev: true - /@vitest/expect@2.0.3: - resolution: {integrity: sha512-X6AepoOYePM0lDNUPsGXTxgXZAl3EXd0GYe/MZyVE4HzkUqyUVC6S3PrY5mClDJ6/7/7vALLMV3+xD/Ko60Hqg==} + /@vitest/expect@2.0.5: + resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} dependencies: - '@vitest/spy': 2.0.3 - '@vitest/utils': 2.0.3 + '@vitest/spy': 2.0.5 + '@vitest/utils': 2.0.5 chai: 5.1.1 tinyrainbow: 1.2.0 dev: true - /@vitest/pretty-format@2.0.3: - resolution: {integrity: sha512-URM4GLsB2xD37nnTyvf6kfObFafxmycCL8un3OC9gaCs5cti2u+5rJdIflZ2fUJUen4NbvF6jCufwViAFLvz1g==} + /@vitest/pretty-format@2.0.5: + resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} dependencies: tinyrainbow: 1.2.0 dev: true - /@vitest/runner@2.0.3: - resolution: {integrity: sha512-EmSP4mcjYhAcuBWwqgpjR3FYVeiA4ROzRunqKltWjBfLNs1tnMLtF+qtgd5ClTwkDP6/DGlKJTNa6WxNK0bNYQ==} + /@vitest/runner@2.0.5: + resolution: {integrity: sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==} dependencies: - '@vitest/utils': 2.0.3 + '@vitest/utils': 2.0.5 pathe: 1.1.2 dev: true - /@vitest/snapshot@2.0.3: - resolution: {integrity: sha512-6OyA6v65Oe3tTzoSuRPcU6kh9m+mPL1vQ2jDlPdn9IQoUxl8rXhBnfICNOC+vwxWY684Vt5UPgtcA2aPFBb6wg==} + /@vitest/snapshot@2.0.5: + resolution: {integrity: sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==} dependencies: - '@vitest/pretty-format': 2.0.3 - magic-string: 0.30.10 + '@vitest/pretty-format': 2.0.5 + magic-string: 0.30.11 pathe: 1.1.2 dev: true - /@vitest/spy@2.0.3: - resolution: {integrity: sha512-sfqyAw/ypOXlaj4S+w8689qKM1OyPOqnonqOc9T91DsoHbfN5mU7FdifWWv3MtQFf0lEUstEwR9L/q/M390C+A==} + /@vitest/spy@2.0.5: + resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} dependencies: tinyspy: 3.0.0 dev: true - /@vitest/utils@2.0.3: - resolution: {integrity: sha512-c/UdELMuHitQbbc/EVctlBaxoYAwQPQdSNwv7z/vHyBKy2edYZaFgptE27BRueZB7eW8po+cllotMNTDpL3HWg==} + /@vitest/utils@2.0.5: + resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} dependencies: - '@vitest/pretty-format': 2.0.3 + '@vitest/pretty-format': 2.0.5 estree-walker: 3.0.3 loupe: 3.1.1 tinyrainbow: 1.2.0 dev: true + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: false + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} @@ -788,14 +842,41 @@ packages: engines: {node: '>=12'} dev: true + /ansis@3.3.2: + resolution: {integrity: sha512-cFthbBlt+Oi0i9Pv/j6YdVWJh54CtjGACaMPCIrEV4Ha7HWsIjXDwseYV79TIL0B4+KfSwD5S70PeQDkPUd1rA==} + engines: {node: '>=15'} + dev: false + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: false + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: false + /assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} dev: true + /async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + dev: false + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + /binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + dev: false + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -807,7 +888,6 @@ packages: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 - dev: true /braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -854,11 +934,31 @@ packages: engines: {node: '>= 16'} dev: true - /cleye@1.3.2: - resolution: {integrity: sha512-MngIC2izcCz07iRKr3Pe8Z6ZBv4zbKFl/YnQEN/aMHis6PpH+MxI2e6n0bMUAmSVlMoAyQkdBCSTbfDmtcSovQ==} + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} dependencies: - terminal-columns: 1.4.1 - type-flag: 3.0.0 + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: false + + /clean-stack@3.0.1: + resolution: {integrity: sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 4.0.0 + dev: false + + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} dev: false /color-convert@1.9.3: @@ -884,6 +984,11 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: false + /consola@3.2.3: + resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + engines: {node: ^14.18.0 || >=16.10.0} + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -893,8 +998,8 @@ packages: which: 2.0.2 dev: true - /debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + /debug@4.3.6(supports-color@8.1.1): + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -903,7 +1008,7 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true + supports-color: 8.1.1 /deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} @@ -915,6 +1020,13 @@ packages: engines: {node: '>=0.3.1'} dev: false + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: false + /dotenv-expand@11.0.6: resolution: {integrity: sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==} engines: {node: '>=12'} @@ -931,9 +1043,16 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true + /ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + jake: 10.9.2 + dev: false + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -1007,6 +1126,11 @@ packages: engines: {node: '>=0.8.0'} dev: false + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: false + /estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} dependencies: @@ -1045,6 +1169,12 @@ packages: reusify: 1.0.4 dev: false + /filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + dependencies: + minimatch: 5.1.6 + dev: false + /fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1060,6 +1190,15 @@ packages: signal-exit: 4.1.0 dev: true + /fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: false + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: false @@ -1069,7 +1208,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: true optional: true /function-bind@1.1.2: @@ -1080,6 +1218,11 @@ packages: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} dev: true + /get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + dev: false + /get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} @@ -1134,6 +1277,18 @@ packages: path-is-absolute: 1.0.1 dev: false + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + dev: false + /globby@14.0.2: resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} engines: {node: '>=18'} @@ -1146,6 +1301,10 @@ packages: unicorn-magic: 0.1.0 dev: false + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: false + /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -1154,7 +1313,6 @@ packages: /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - dev: false /hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} @@ -1173,6 +1331,11 @@ packages: engines: {node: '>= 4'} dev: false + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: false + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -1190,13 +1353,26 @@ packages: engines: {node: '>= 0.10'} dev: false - /is-core-module@2.15.0: - resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.3.0 + dev: false + + /is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} engines: {node: '>= 0.4'} dependencies: hasown: 2.0.2 dev: false + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: false + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1205,7 +1381,6 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -1224,6 +1399,13 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: false + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -1237,11 +1419,30 @@ packages: '@pkgjs/parseargs': 0.11.0 dev: true + /jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + async: 3.2.6 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + dev: false + /jiti@2.0.0-beta.3: resolution: {integrity: sha512-pmfRbVRs/7khFrSAYnSiJ8C0D5GvzkE4Ey2pAvUcJsw1ly/p+7ut27jbJrjY79BpAJQJ4gXYFtK6d1Aub+9baQ==} hasBin: true dev: false + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + dev: false + /kysely-codegen@0.15.0(kysely@0.27.4): resolution: {integrity: sha512-LPta2nQOyoEPDQ3w/Gsplc+2iyZPAsGvtWoS21VzOB0NDQ0B38Xy1gS8WlbGef542Zdw2eLJHxekud9DzVdNRw==} hasBin: true @@ -1287,6 +1488,11 @@ packages: engines: {node: '>=14.0.0'} dev: false + /lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + dev: false + /loglevel@1.9.1: resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==} engines: {node: '>= 0.6.0'} @@ -1303,8 +1509,8 @@ packages: engines: {node: 20 || >=22} dev: true - /magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + /magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} dependencies: '@jridgewell/sourcemap-codec': 1.5.0 dev: true @@ -1344,6 +1550,20 @@ packages: brace-expansion: 1.1.11 dev: false + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: false + + /minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: false + /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: false @@ -1355,7 +1575,6 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} @@ -1363,6 +1582,11 @@ packages: hasBin: true dev: true + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: false + /npm-run-path@5.3.0: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1414,6 +1638,11 @@ packages: minipass: 7.1.2 dev: true + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: false + /path-type@5.0.0: resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} engines: {node: '>=12'} @@ -1437,8 +1666,8 @@ packages: engines: {node: '>=8.6'} dev: false - /postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + /postcss@8.4.41: + resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.7 @@ -1461,6 +1690,13 @@ packages: engines: {node: '>=14.18.0'} dev: false + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: false + /rechoir@0.6.2: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} @@ -1476,7 +1712,7 @@ packages: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true dependencies: - is-core-module: 2.15.0 + is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: false @@ -1495,29 +1731,29 @@ packages: package-json-from-dist: 1.0.0 dev: true - /rollup@4.18.1: - resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} + /rollup@4.21.1: + resolution: {integrity: sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.18.1 - '@rollup/rollup-android-arm64': 4.18.1 - '@rollup/rollup-darwin-arm64': 4.18.1 - '@rollup/rollup-darwin-x64': 4.18.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.18.1 - '@rollup/rollup-linux-arm-musleabihf': 4.18.1 - '@rollup/rollup-linux-arm64-gnu': 4.18.1 - '@rollup/rollup-linux-arm64-musl': 4.18.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.18.1 - '@rollup/rollup-linux-riscv64-gnu': 4.18.1 - '@rollup/rollup-linux-s390x-gnu': 4.18.1 - '@rollup/rollup-linux-x64-gnu': 4.18.1 - '@rollup/rollup-linux-x64-musl': 4.18.1 - '@rollup/rollup-win32-arm64-msvc': 4.18.1 - '@rollup/rollup-win32-ia32-msvc': 4.18.1 - '@rollup/rollup-win32-x64-msvc': 4.18.1 + '@rollup/rollup-android-arm-eabi': 4.21.1 + '@rollup/rollup-android-arm64': 4.21.1 + '@rollup/rollup-darwin-arm64': 4.21.1 + '@rollup/rollup-darwin-x64': 4.21.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.21.1 + '@rollup/rollup-linux-arm-musleabihf': 4.21.1 + '@rollup/rollup-linux-arm64-gnu': 4.21.1 + '@rollup/rollup-linux-arm64-musl': 4.21.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.21.1 + '@rollup/rollup-linux-riscv64-gnu': 4.21.1 + '@rollup/rollup-linux-s390x-gnu': 4.21.1 + '@rollup/rollup-linux-x64-gnu': 4.21.1 + '@rollup/rollup-linux-x64-musl': 4.21.1 + '@rollup/rollup-win32-arm64-msvc': 4.21.1 + '@rollup/rollup-win32-ia32-msvc': 4.21.1 + '@rollup/rollup-win32-x64-msvc': 4.21.1 fsevents: 2.3.3 dev: true @@ -1563,6 +1799,11 @@ packages: engines: {node: '>=14'} dev: true + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: false + /slash@5.1.0: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} @@ -1588,7 +1829,6 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true /string-width@5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} @@ -1604,7 +1844,6 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - dev: true /strip-ansi@7.1.0: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} @@ -1632,21 +1871,23 @@ packages: has-flag: 4.0.0 dev: false + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: false - /terminal-columns@1.4.1: - resolution: {integrity: sha512-IKVL/itiMy947XWVv4IHV7a0KQXvKjj4ptbi7Ew9MPMcOLzkiQeyx3Gyvh62hKrfJ0RZc4M1nbhzjNM39Kyujw==} - dev: false - - /tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + /tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} dev: true - /tinypool@1.0.0: - resolution: {integrity: sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==} + /tinypool@1.0.1: + resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} engines: {node: ^18.0.0 || >=20.0.0} dev: true @@ -1667,8 +1908,8 @@ packages: is-number: 7.0.0 dev: false - /tsx@4.18.0: - resolution: {integrity: sha512-a1jaKBSVQkd6yEc1/NI7G6yHFfefIcuf3QJST7ZEyn4oQnxLYrZR5uZAM8UrwUa3Ge8suiZHcNS1gNrEvmobqg==} + /tsx@4.19.0: + resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} engines: {node: '>=18.0.0'} hasBin: true dependencies: @@ -1678,17 +1919,18 @@ packages: fsevents: 2.3.3 dev: true + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: false + /type-fest@4.25.0: resolution: {integrity: sha512-bRkIGlXsnGBRBQRAY56UXBm//9qH4bmJfFvq83gSz41N282df+fjy8ofcEgc1sM8geNt5cl6mC2g9Fht1cs8Aw==} engines: {node: '>=16'} dev: true - /type-flag@3.0.0: - resolution: {integrity: sha512-3YaYwMseXCAhBB14RXW5cRQfJQlEknS6i4C8fCfeUdS3ihG9EdccdR9kt3vP73ZdeTGmPb4bZtkDn5XMIn1DLA==} - dev: false - - /typescript@5.5.3: - resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} + /typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} engines: {node: '>=14.17'} hasBin: true dev: true @@ -1702,29 +1944,35 @@ packages: engines: {node: '>=18'} dev: false - /vite-node@2.0.3(@types/node@22.5.0): - resolution: {integrity: sha512-14jzwMx7XTcMB+9BhGQyoEAmSl0eOr3nrnn+Z12WNERtOvLN+d2scbRUvyni05rT3997Bg+rZb47NyP4IQPKXg==} + /universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + dev: false + + /vite-node@2.0.5(@types/node@22.5.0): + resolution: {integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true dependencies: cac: 6.7.14 - debug: 4.3.5 + debug: 4.3.6(supports-color@8.1.1) pathe: 1.1.2 tinyrainbow: 1.2.0 - vite: 5.3.4(@types/node@22.5.0) + vite: 5.4.2(@types/node@22.5.0) transitivePeerDependencies: - '@types/node' - less - lightningcss - sass + - sass-embedded - stylus - sugarss - supports-color - terser dev: true - /vite@5.3.4(@types/node@22.5.0): - resolution: {integrity: sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==} + /vite@5.4.2(@types/node@22.5.0): + resolution: {integrity: sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -1732,6 +1980,7 @@ packages: less: '*' lightningcss: ^1.21.0 sass: '*' + sass-embedded: '*' stylus: '*' sugarss: '*' terser: ^5.4.0 @@ -1744,6 +1993,8 @@ packages: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: @@ -1753,21 +2004,21 @@ packages: dependencies: '@types/node': 22.5.0 esbuild: 0.21.5 - postcss: 8.4.39 - rollup: 4.18.1 + postcss: 8.4.41 + rollup: 4.21.1 optionalDependencies: fsevents: 2.3.3 dev: true - /vitest@2.0.3(@types/node@22.5.0): - resolution: {integrity: sha512-o3HRvU93q6qZK4rI2JrhKyZMMuxg/JRt30E6qeQs6ueaiz5hr1cPj+Sk2kATgQzMMqsa2DiNI0TIK++1ULx8Jw==} + /vitest@2.0.5(@types/node@22.5.0): + resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.0.3 - '@vitest/ui': 2.0.3 + '@vitest/browser': 2.0.5 + '@vitest/ui': 2.0.5 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -1786,28 +2037,29 @@ packages: dependencies: '@ampproject/remapping': 2.3.0 '@types/node': 22.5.0 - '@vitest/expect': 2.0.3 - '@vitest/pretty-format': 2.0.3 - '@vitest/runner': 2.0.3 - '@vitest/snapshot': 2.0.3 - '@vitest/spy': 2.0.3 - '@vitest/utils': 2.0.3 + '@vitest/expect': 2.0.5 + '@vitest/pretty-format': 2.0.5 + '@vitest/runner': 2.0.5 + '@vitest/snapshot': 2.0.5 + '@vitest/spy': 2.0.5 + '@vitest/utils': 2.0.5 chai: 5.1.1 - debug: 4.3.5 + debug: 4.3.6(supports-color@8.1.1) execa: 8.0.1 - magic-string: 0.30.10 + magic-string: 0.30.11 pathe: 1.1.2 std-env: 3.7.0 - tinybench: 2.8.0 - tinypool: 1.0.0 + tinybench: 2.9.0 + tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.3.4(@types/node@22.5.0) - vite-node: 2.0.3(@types/node@22.5.0) + vite: 5.4.2(@types/node@22.5.0) + vite-node: 2.0.5(@types/node@22.5.0) why-is-node-running: 2.3.0 transitivePeerDependencies: - less - lightningcss - sass + - sass-embedded - stylus - sugarss - supports-color @@ -1831,6 +2083,17 @@ packages: stackback: 0.0.2 dev: true + /widest-line@3.1.0: + resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} + engines: {node: '>=8'} + dependencies: + string-width: 4.2.3 + dev: false + + /wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + dev: false + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -1838,7 +2101,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrap-ansi@8.1.0: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} diff --git a/src/cli.ts b/src/cli.ts index be4ba00..a5c6d5d 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,119 +1,185 @@ -#!/usr/bin/env node - +import { Codegen } from '#codegen.js' +import { DEFAULT_OUT_FILE, pgDataFiles } from '#constants.js' +import { applyLogsPatch } from '#utils/apply-logs-patch.js' +import { createKyselyPGlite } from '#utils/create-kysely.js' +import { createMigrator } from '#utils/create-migrator.js' +import { getDatabaseErrorInfo } from '#utils/get-database-error-info.js' +import { Args, Command, Flags } from '@oclif/core' import { isError } from '@sindresorhus/is' -import { cli } from 'cleye' -import { join } from 'path/posix' +import chokidar from 'chokidar' +import consola from 'consola' +import { colorize } from 'consola/utils' +import { lstatSync } from 'fs' +import fs from 'fs-extra' import { group } from 'radash' -import { Codegen } from './codegen.js' -import { createKyselyPGlite } from './utils/create-kysely.js' -import { createMigrator } from './utils/migrator.js' - -export const DEFAULT_OUT_FILE = join( - process.cwd(), - 'node_modules', - 'kysely-pglite', - 'dist', - 'db.d.ts', -) - -const argv = cli({ - name: 'kysely-pglite', - - parameters: [''], - flags: { - dataDir: { - type: Boolean, + +// Patching console.log and console.warn because of noisy logs from @electic/pglite +// https://github.com/electric-sql/pglite/issues/256 +applyLogsPatch() + +export default class CodegenCommand extends Command { + static override args = { + path: Args.string({ + required: true, + parse: async (path) => { + const stat = lstatSync(path, { throwIfNoEntry: false }) + if (!stat?.isDirectory() && !stat?.isFile()) { + throw new Error( + `${path} is an invalid path. It doesn't resolve to a file or directory`, + ) + } + return path + }, description: - 'Path to the directory that stores the persisted Postgres database', - alias: 'd', - }, - camelCase: { - type: Boolean, + 'The path to a file/directory of Kysely migrations or a persisted PGlite database', + }), + } + + static override description = + 'Generate TypeScript types based on Kysely migrations or a persisted PGlite database' + + static override examples = [ + '<%= config.bin %> <%= command.id %> ./src/db/migrations --out-file ./src/db/types.ts', + '<%= config.bin %> <%= command.id %> ./src/db/migrations-file.ts', + '<%= config.bin %> <%= command.id %> ./pgdata', + '<%= config.bin %> <%= command.id %> --watch ./src/db/migrations', + ] + + static override flags = { + watch: Flags.boolean({ + char: 'w', + default: false, + description: + 'Watches the given path and generates types whenever a change is detected', + }), + outFile: Flags.string({ + char: 'o', + description: 'Path to persist the generated types', + default: DEFAULT_OUT_FILE, + }), + camelCase: Flags.boolean({ description: 'Use the Kysely CamelCasePlugin', - }, - envFile: { - type: String, - description: 'Specify the path to an environment file to use', - }, - excludePattern: { - type: String, + }), + envFile: Flags.directory({ + description: 'The path to an environment file to use', + }), + excludePattern: Flags.string({ description: 'Exclude tables matching the specified glob pattern', - }, - includePattern: { - type: String, + }), + includePattern: Flags.string({ description: 'Only include tables matching the specified glob pattern', - }, - logLevel: { - type: String, + }), + logLevel: Flags.string({ description: 'Set the terminal log level', options: ['debug', 'info', 'warn', 'error', 'silent'], - default: 'warn', - }, - outFile: { - type: String, - description: 'Set the file build path', - default: DEFAULT_OUT_FILE, - alias: 'o', - }, - print: { - type: Boolean, + }), + print: Flags.boolean({ description: 'Print the generated output to the terminal', - alias: 'p', - }, - runtimeEnums: { - type: Boolean, + char: 'p', + }), + runtimeEnums: Flags.boolean({ description: 'Generate runtime enums instead of string unions', - }, - typeOnlyImports: { - type: Boolean, + }), + typeOnlyImports: Flags.boolean({ description: 'Generate TypeScript 3.8+ `import type` syntax', default: true, - }, - verify: { - type: Boolean, + }), + verify: Flags.boolean({ description: 'Verify that the generated types are up-to-date', default: false, - }, - }, -}) + }), + noDomain: Flags.boolean({ + description: 'Skip generating types for PostgreSQL domains', + default: false, + }), + } -const { path } = argv._ -const { dataDir, ...opts } = argv.flags + private async isDataDir(path: string) { + const stat = lstatSync(path) + if (!stat.isDirectory()) { + return false + } + const files = await fs.readdir(path, { + encoding: 'utf-8', + withFileTypes: true, + }) + return files.some((f) => pgDataFiles.includes(f.name)) + } + + private async runCodegen() { + const { + args, + flags: { watch, ...flags }, + } = await this.parse(CodegenCommand) + const isDataDir = await this.isDataDir(args.path) + const { db, dialect } = await createKyselyPGlite( + isDataDir ? args.path : undefined, + ) + const codegen = new Codegen(dialect) + const migrator = await createMigrator(db, args.path) + + if (isDataDir) { + await codegen.generate({ ...flags, db }) + } else { + const { results = [], error } = await migrator.migrateToLatest() + const { Success = [], Error = [] } = group(results, (r) => r.status) -const { db, dialect } = await createKyselyPGlite(dataDir ? path : undefined) + if (Success.length) { + await codegen.generate({ ...flags, db }) + } -const migrator = await createMigrator(db, path) + if (isError(error)) { + const [migration] = Error + const { message, hint } = getDatabaseErrorInfo(error) + this.error(`${message} in ${migration.migrationName}`, { + suggestions: hint ? [hint] : [], + }) + } + } -const resultSet = await migrator.migrateToLatest() + return db + } -const { results = [], error } = resultSet + public async run(): Promise { + const { args, flags } = await this.parse(CodegenCommand) -if (results.length) { - const { - Success = [], - Error = [], - NotExecuted = [], - } = group(results, (r) => r.status) + consola.start('Introspecting database...') - if (isError(error)) { - const [failedMigration] = Error - const { stack, ...err } = error - console.error( - `${failedMigration?.migrationName} failed. ${error.message}`, - err, + const db = await this.runCodegen() + const tables = await db.introspection.getTables() + consola.success( + `Introspected ${tables.length} tables. Generated types in ${colorize('underline', flags.outFile)}`, ) - } - for (const result of NotExecuted) { - console.warn(`${result.migrationName} was not executed`) - } + if (flags.watch) { + // https://oclif.io/docs/commands/#avoiding-timeouts + return new Promise(() => { + consola.start( + `Watching changes in ${colorize('underline', args.path)}...`, + ) + const watcher = chokidar.watch(args.path, { + ignored: /(^|[\/\\])\../, // ignore dotfiles + ignoreInitial: true, + }) - if (Success.length) { - const codegen = new Codegen(dialect) - await codegen.generate({ ...opts, db }) - } -} + const watcherFn = async (event: string, path: string) => { + await this.runCodegen() + consola.info( + `${event} File: ${colorize('underline', path)}. Types updated`, + ) + } -if (error) { - console.error(error) + watcher + .on('add', async (path) => + watcherFn(colorize('blueBright', '[added]'), path), + ) + .on('change', async (path) => + watcherFn(colorize('cyan', '[changed]'), path), + ) + .on('unlink', async (path) => + watcherFn(colorize('magenta', '[deleted]'), path), + ) + }) + } + } } diff --git a/src/codegen.ts b/src/codegen.ts index 6ca079b..cc374dd 100644 --- a/src/codegen.ts +++ b/src/codegen.ts @@ -1,15 +1,12 @@ import type { Dialect } from 'kysely' import { Generator, - Logger, PostgresAdapter, type GenerateOptions, } from 'kysely-codegen' import { KyselyPGliteIntrospector } from './introspector.js' export class Codegen { - logger = new Logger() - constructor(public dialect: Dialect) {} async generate(opts: Omit) { @@ -24,8 +21,6 @@ export class Codegen { return this.dialect }, }, - - logger: this.logger, }) } } diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..7ca319b --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,40 @@ +import { Migration } from 'kysely' +import { join } from 'path/posix' + +export type MigrationMethod = keyof Migration + +export const DEFAULT_OUT_FILE = join( + process.cwd(), + 'node_modules', + 'kysely-pglite', + 'dist', + 'db.d.ts', +) + +export const pgDataFiles = [ + 'PG_VERSION', + 'base', + 'global', + 'pg_commit_ts', + 'pg_dynshmem', + 'pg_hba.conf', + 'pg_ident.conf', + 'pg_logical', + 'pg_multixact', + 'pg_notify', + 'pg_replslot', + 'pg_serial', + 'pg_snapshots', + 'pg_stat', + 'pg_stat_tmp', + 'pg_subtrans', + 'pg_tblspc', + 'pg_twophase', + 'pg_wal', + 'pg_xact', + 'postgresql.auto.conf', + 'postgresql.conf', + 'postmaster.pid', +] + +export const migrationMethods: MigrationMethod[] = ['up', 'down'] diff --git a/src/introspector.ts b/src/introspector.ts index 7086555..743ff55 100644 --- a/src/introspector.ts +++ b/src/introspector.ts @@ -3,6 +3,7 @@ import { DatabaseMetadata, EnumCollection, Introspector, + TableMatcher, type ConnectOptions, type IntrospectOptions, } from 'kysely-codegen' @@ -18,19 +19,19 @@ export class KyselyPGliteIntrospector extends Introspector { protected async getTables(options: IntrospectOptions) { let tables = await options.db.introspection.getTables() - // if (options.includePattern) { - // const tableMatcher = new TableMatcher(options.includePattern) - // tables = tables.filter(({ name, schema }) => - // tableMatcher.match(schema, name), - // ) - // } + if (options.includePattern) { + const tableMatcher = new TableMatcher(options.includePattern) + tables = tables.filter(({ name, schema }) => + tableMatcher.match(schema, name), + ) + } - // if (options.excludePattern) { - // const tableMatcher = new TableMatcher(options.excludePattern) - // tables = tables.filter( - // ({ name, schema }) => !tableMatcher.match(schema, name), - // ) - // } + if (options.excludePattern) { + const tableMatcher = new TableMatcher(options.excludePattern) + tables = tables.filter( + ({ name, schema }) => !tableMatcher.match(schema, name), + ) + } return tables } diff --git a/src/kysely-live.ts b/src/kysely-live.ts index 18b90c8..a396813 100644 --- a/src/kysely-live.ts +++ b/src/kysely-live.ts @@ -5,21 +5,31 @@ import type { ReferenceExpression } from 'kysely' import { type SelectQueryBuilder } from 'kysely' /** * Wrapper for PGlite's Live Queries extension + * @example + * const pglive = new KyselyLive(pglite) + * const query = db + * .selectFrom('sales') + * .select(['id', 'price']) + * .orderBy((eb) => eb.fn('rand')) + * const liveQuery = pglive.query(query) + * + * for await (const data of liveQuery.subscribe) { + * const [sale] = data + * console.log(sale.id, sale.price) +} */ export class KyselyLive { private live: PGliteWithLive['live'] - constructor(public pglite: PGliteWithLive) { + constructor(pglite: PGliteWithLive) { this.live = pglite.live } query(builder: SelectQueryBuilder) { const { sql, parameters } = builder.compile() - return this.createLiveRepeater(async (push) => { - return await this.live.query(sql, [...parameters], (data) => - push(data.rows), - ) + return this.createLiveRepeater((push) => { + return this.live.query(sql, [...parameters], (data) => push(data.rows)) }) } @@ -31,10 +41,8 @@ export class KyselyLive { assertString(ref, '`changes` received invalid key') - type Change = ReturnType - - return this.createLiveRepeater(async (push) => { - return await this.live.changes(sql, [...parameters], ref, (data) => + return this.createLiveRepeater((push) => { + return this.live.changes(sql, [...parameters], ref, (data) => push(data), ) }) @@ -48,12 +56,9 @@ export class KyselyLive { assertString(ref, '`incrementalQuery` received invalid key') - return this.createLiveRepeater(async (push) => { - return await this.live.incrementalQuery( - sql, - [...parameters], - ref, - (data) => push(data.rows), + return this.createLiveRepeater((push) => { + return this.live.incrementalQuery(sql, [...parameters], ref, (data) => + push(data.rows), ) }) } @@ -70,18 +75,12 @@ export class KyselyLive { const subscribe = new Repeater(async (push, stop) => { const res = await liveMethod((data) => push(data)) - // Store the refresh and unsubscribe functions for external access refresh = res.refresh unsubscribe = res.unsubscribe - // Handle the stop signal to clean up the live query subscription - stop.then(() => { - res.unsubscribe() // Ensure that we unsubscribe when the Repeater stops - }) + await stop - return () => { - res.unsubscribe() // Ensure cleanup if Repeater completes or errors - } + res.unsubscribe() }) return { diff --git a/src/kysely-pglite.ts b/src/kysely-pglite.ts index da7fb73..f78bf4b 100644 --- a/src/kysely-pglite.ts +++ b/src/kysely-pglite.ts @@ -4,6 +4,9 @@ import { type PGliteOptions, } from '@electric-sql/pglite' +import { ensureDataDirExist } from '#utils/create-kysely.js' +import { isString } from '@sindresorhus/is' +import fs from 'fs-extra' import { Kysely, PostgresAdapter, @@ -11,7 +14,6 @@ import { PostgresQueryCompiler, type Dialect, } from 'kysely' - import { PGliteDriver } from './pglite-driver.js' export class KyselyPGlite { @@ -29,6 +31,8 @@ export class KyselyPGlite { constructor(options?: O) constructor(client?: PGlite) constructor(dataDirOrClient?: string | PGlite | O, opts?: O) { + ensureDataDirExist(dataDirOrClient) + if (typeof dataDirOrClient === 'string') { // @ts-expect-error this.client = new PGlite(dataDirOrClient, opts) @@ -38,11 +42,11 @@ export class KyselyPGlite { this.client = dataDirOrClient } else { // @ts-expect-error - this.client = new PGlite(dataDirOrClient) + this.client = new PGlite(dataDirOrClient, opts) } } else { // @ts-expect-error - this.client = new PGlite() + this.client = new PGlite(dataDirOrClient, opts) } } @@ -53,11 +57,12 @@ export class KyselyPGlite { static async create( options?: PGliteOptions, ): Promise> - static async create( + static async create( dataDirOrPGliteOptions: string | PGliteOptions = {}, options: PGliteOptions = {}, - ) { + ): Promise> { let opts = options + ensureDataDirExist(dataDirOrPGliteOptions) if (typeof dataDirOrPGliteOptions === 'string') { opts.dataDir = dataDirOrPGliteOptions @@ -66,7 +71,7 @@ export class KyselyPGlite { } const pglite = await PGlite.create(opts) - return new KyselyPGlite(pglite) + return new KyselyPGlite(pglite) } dialect: Dialect = { diff --git a/src/utils/apply-logs-patch.ts b/src/utils/apply-logs-patch.ts new file mode 100644 index 0000000..4c449bb --- /dev/null +++ b/src/utils/apply-logs-patch.ts @@ -0,0 +1,23 @@ +const originalLog = console.log +const originalWarn = console.warn + +// Patching console.log and console.warn because of noisy logs from @electic/pglite +// https://github.com/electric-sql/pglite/issues/256 +export function applyLogsPatch() { + console.log = function (args) { + if ( + typeof args === 'string' && + args.includes('Running in main thread, faking onCustomMessage') + ) { + return + } + originalLog(args) + } + + console.warn = function (args) { + if (typeof args === 'string' && args.includes('prerun(C-node)')) { + return + } + originalWarn(args) + } +} diff --git a/src/utils/create-kysely.ts b/src/utils/create-kysely.ts index 6167f63..8c05e0a 100644 --- a/src/utils/create-kysely.ts +++ b/src/utils/create-kysely.ts @@ -1,6 +1,22 @@ +import type { PGliteOptions } from '@electric-sql/pglite' +import { isObject, isString } from '@sindresorhus/is' +import fs from 'fs-extra' import { Kysely } from 'kysely' import { KyselyPGlite } from '../kysely-pglite.js' +export function ensureDataDirExist(dataDir?: string | PGliteOptions) { + let path + if (isString(dataDir)) { + path = dataDir + } + if (isObject(dataDir) && dataDir.dataDir) { + path = dataDir.dataDir + } + if (path) { + const fo = fs.ensureDir(path) + } +} + export async function createKyselyPGlite(dataDir?: string) { const { dialect } = new KyselyPGlite({ dataDir }) diff --git a/src/utils/create-migrator.ts b/src/utils/create-migrator.ts new file mode 100644 index 0000000..b49e212 --- /dev/null +++ b/src/utils/create-migrator.ts @@ -0,0 +1,38 @@ +import { globby } from 'globby' +import { createJiti } from 'jiti' +import { Kysely, Migrator, type Migration } from 'kysely' +import { all, objectify } from 'radash' + +const jiti = createJiti(import.meta.filename, { + // prevent files from getting cached so the `watch` feature works. + moduleCache: false, +}) + +export async function createMigrator(db: Kysely, migrationsPath: string) { + return new Migrator({ + db, + provider: { + async getMigrations() { + const files = await globby(migrationsPath, { + expandDirectories: { + files: ['*.ts', '*.js'], + }, + + ignore: ['**/types.ts'], + objectMode: true, + absolute: true, + }) + + const migrations = objectify( + files, + (f) => f.name, + (f) => jiti.import(f.path), + ) + + // TODO: improve validating imported functions + const modules = (await all(migrations)) as Record + return modules + }, + }, + }) +} diff --git a/src/utils/get-database-error-info.ts b/src/utils/get-database-error-info.ts new file mode 100644 index 0000000..6ade6dd --- /dev/null +++ b/src/utils/get-database-error-info.ts @@ -0,0 +1,52 @@ +export interface NoticeOrError { + message: string | undefined + severity: string | undefined + code: string | undefined + detail: string | undefined + hint: string | undefined + position: string | undefined + internalPosition: string | undefined + internalQuery: string | undefined + where: string | undefined + schema: string | undefined + table: string | undefined + column: string | undefined + dataType: string | undefined + constraint: string | undefined + file: string | undefined + line: string | undefined + routine: string | undefined +} + +const databaseError: NoticeOrError = { + message: undefined, + severity: undefined, + code: undefined, + detail: undefined, + hint: undefined, + position: undefined, + internalPosition: undefined, + internalQuery: undefined, + where: undefined, + schema: undefined, + table: undefined, + column: undefined, + dataType: undefined, + constraint: undefined, + file: undefined, + line: undefined, + routine: undefined, +} + +export function getDatabaseErrorInfo(error: Error): NoticeOrError { + const err = { ...databaseError } + const message = error.message + for (const key of Object.keys(databaseError)) { + if (key in databaseError && key in error) { + // @ts-expect-error + err[key] = error[key] + } + } + + return err +} diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 0000000..25b214c --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,36 @@ +import consola from 'consola' +import { Logger } from 'kysely-codegen' + +export const enum LogLevel { + SILENT = 0, + INFO = 1, + WARN = 2, + ERROR = 3, + DEBUG = 4, +} + +export class CodegenLogger extends Logger { + debug(...values: [unknown, unknown]) { + if (this.logLevel >= LogLevel.DEBUG) { + consola.debug(...values) + } + } + + error(...values: [unknown, unknown]) { + if (this.logLevel >= LogLevel.ERROR) { + consola.error(...values) + } + } + + info(...values: [unknown, unknown]) { + if (this.logLevel >= LogLevel.INFO) { + consola.info(...values) + } + } + + log(...values: [unknown, unknown]): void { + if (this.logLevel >= LogLevel.INFO) { + consola.log(...values) + } + } +} diff --git a/src/utils/migrator.ts b/src/utils/migrator.ts deleted file mode 100644 index 6deae2e..0000000 --- a/src/utils/migrator.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { globby } from 'globby' -import { createJiti } from 'jiti' -import { Kysely, Migrator } from 'kysely' -import { all, objectify } from 'radash' - -const jiti = createJiti(import.meta.filename) - -export async function createMigrator(db: Kysely, migrationsPath: string) { - const files = await globby(migrationsPath, { - expandDirectories: { - files: ['*.ts', '*.js'], - }, - ignore: ['**/types.ts'], - gitignore: true, - objectMode: true, - absolute: true, - }) - - if (!files.length) { - console.warn(`No migration files found in ${migrationsPath}`) - } - - const migrations = objectify( - files, - (f) => f.name, - (f) => - jiti - .import(f.path) - .then((res) => { - // @ts-expect-error - return res.default - }) - .catch(console.error), - ) - - const modules = await all(migrations) - - return new Migrator({ - db, - provider: { - async getMigrations() { - return modules - }, - }, - }) -} diff --git a/tsconfig.json b/tsconfig.json index 32f5599..bdce699 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,9 @@ { "compilerOptions": { + "baseUrl": "./", + "paths": { + "#*": ["./src/*"] + }, "lib": ["ESNext"], "target": "ESNext", "module": "Preserve",