From 15b4dc468d17ebbc478fde968d0e119f0268f727 Mon Sep 17 00:00:00 2001 From: NathanosDev Date: Sun, 21 Apr 2024 01:42:02 +0200 Subject: [PATCH] feat: separate pic server management from instance management --- .github/actions/setup-bun/action.yml | 2 - .github/actions/setup-dfx/action.yml | 6 +- .github/actions/setup-nodejs/action.yml | 4 +- .github/actions/setup-pnpm/action.yml | 12 +- .github/workflows/lint.yml | 2 +- .github/workflows/release-beta.yml | 2 +- .github/workflows/release-latest.yml | 2 +- .github/workflows/test-bun.yml | 2 +- .github/workflows/test-nodejs.yml | 2 +- .github/workflows/update-changelog.yml | 2 +- bun.lockb | Bin 174752 -> 210064 bytes docs/docs/guides/01-getting-started.md | 45 +- docs/docs/guides/02-using-jest.md | 208 +++++ docs/docs/guides/03-using-vitest.md | 184 +++++ .../{02-using-bun.md => 04-using-bun.md} | 79 +- ...-the-nns.md => 05-working-with-the-nns.md} | 12 +- ...rations.md => 06-canister-declarations.md} | 0 examples/clock/tests/global-setup.ts | 9 + examples/clock/tests/global-teardown.ts | 3 + examples/clock/tests/jest.config.ts | 2 + examples/clock/tests/src/clock.spec.ts | 3 +- examples/clock/tests/tsconfig.json | 7 +- examples/clock/tests/types.d.ts | 11 + examples/counter/tests/global-setup.ts | 15 + examples/counter/tests/jest.config.ts | 9 - examples/counter/tests/package.json | 1 + examples/counter/tests/src/counter.spec.ts | 6 +- examples/counter/tests/tsconfig.json | 4 +- examples/counter/tests/types.d.ts | 5 + examples/counter/tests/vitest.config.ts | 8 + examples/multicanister/tests/global-setup.ts | 9 + .../multicanister/tests/global-teardown.ts | 3 + examples/multicanister/tests/jest.config.ts | 2 + .../tests/src/multicanister.spec.ts | 5 +- examples/multicanister/tests/tsconfig.json | 7 +- examples/multicanister/tests/types.d.ts | 11 + examples/nns_proxy/tests/global-setup.ts | 9 + examples/nns_proxy/tests/global-teardown.ts | 3 + examples/nns_proxy/tests/jest.config.ts | 2 + .../nns_proxy/tests/src/nns-proxy.spec.ts | 2 +- examples/nns_proxy/tests/tsconfig.json | 7 +- examples/nns_proxy/tests/types.d.ts | 11 + examples/todo/tests/global-setup.ts | 9 + examples/todo/tests/global-teardown.ts | 3 + examples/todo/tests/jest.config.ts | 2 + examples/todo/tests/src/todo.spec.ts | 2 +- examples/todo/tests/tsconfig.json | 7 +- examples/todo/tests/types.d.ts | 11 + package.json | 5 +- packages/pic/src/index.ts | 1 + packages/pic/src/pocket-ic-server.ts | 14 +- packages/pic/src/pocket-ic.ts | 36 +- pnpm-lock.yaml | 769 +++++++++++++++++- 53 files changed, 1460 insertions(+), 117 deletions(-) create mode 100644 docs/docs/guides/02-using-jest.md create mode 100644 docs/docs/guides/03-using-vitest.md rename docs/docs/guides/{02-using-bun.md => 04-using-bun.md} (66%) rename docs/docs/guides/{04-working-with-the-nns.md => 05-working-with-the-nns.md} (98%) rename docs/docs/guides/{03-canister-declarations.md => 06-canister-declarations.md} (100%) create mode 100644 examples/clock/tests/global-setup.ts create mode 100644 examples/clock/tests/global-teardown.ts create mode 100644 examples/clock/tests/types.d.ts create mode 100644 examples/counter/tests/global-setup.ts delete mode 100644 examples/counter/tests/jest.config.ts create mode 100644 examples/counter/tests/types.d.ts create mode 100644 examples/counter/tests/vitest.config.ts create mode 100644 examples/multicanister/tests/global-setup.ts create mode 100644 examples/multicanister/tests/global-teardown.ts create mode 100644 examples/multicanister/tests/types.d.ts create mode 100644 examples/nns_proxy/tests/global-setup.ts create mode 100644 examples/nns_proxy/tests/global-teardown.ts create mode 100644 examples/nns_proxy/tests/types.d.ts create mode 100644 examples/todo/tests/global-setup.ts create mode 100644 examples/todo/tests/global-teardown.ts create mode 100644 examples/todo/tests/types.d.ts diff --git a/.github/actions/setup-bun/action.yml b/.github/actions/setup-bun/action.yml index f4d017f..4bae42a 100644 --- a/.github/actions/setup-bun/action.yml +++ b/.github/actions/setup-bun/action.yml @@ -6,8 +6,6 @@ runs: steps: - name: Setup Bun uses: oven-sh/setup-bun@v1 - with: - bun-version: '1.0.26' - name: Install Dependencies shell: bash diff --git a/.github/actions/setup-dfx/action.yml b/.github/actions/setup-dfx/action.yml index 7c02bf8..a74463a 100644 --- a/.github/actions/setup-dfx/action.yml +++ b/.github/actions/setup-dfx/action.yml @@ -4,11 +4,7 @@ description: Setup DFX runs: using: 'composite' steps: - - name: Get DFX version - shell: bash - run: echo "dfx_version=$(cat dfx.json | jq -r .dfx)" >> "$GITHUB_ENV" - - name: Setup DFX uses: dfinity/setup-dfx@main with: - dfx-version: ${{ env.dfx_version }} + dfx-version: 'auto' diff --git a/.github/actions/setup-nodejs/action.yml b/.github/actions/setup-nodejs/action.yml index b3b9352..8beea97 100644 --- a/.github/actions/setup-nodejs/action.yml +++ b/.github/actions/setup-nodejs/action.yml @@ -5,7 +5,7 @@ runs: using: 'composite' steps: - name: Install NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 20 + node-version-file: '.node-version' registry-url: 'https://registry.npmjs.org' diff --git a/.github/actions/setup-pnpm/action.yml b/.github/actions/setup-pnpm/action.yml index cbcd150..5013b09 100644 --- a/.github/actions/setup-pnpm/action.yml +++ b/.github/actions/setup-pnpm/action.yml @@ -6,25 +6,23 @@ runs: steps: - uses: ./.github/actions/setup-nodejs - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 name: Install pnpm id: pnpm-install with: run_install: false - name: Get pnpm store directory - id: pnpm-cache shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - uses: actions/cache@v3 + - uses: actions/cache@v4 name: Setup pnpm cache with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + path: ${{ env.STORE_PATH }} key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | - ${{ runner.os }}-pnpm-store + ${{ runner.os }}-pnpm-store- - name: Install dependencies shell: bash diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 55bd760..4f0cfb7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/release-beta.yml b/.github/workflows/release-beta.yml index 8a3c2fa..7855184 100644 --- a/.github/workflows/release-beta.yml +++ b/.github/workflows/release-beta.yml @@ -13,7 +13,7 @@ jobs: url: https://www.npmjs.com/package/@hadronous/pic steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/release-latest.yml b/.github/workflows/release-latest.yml index 0e3810f..fe978f5 100644 --- a/.github/workflows/release-latest.yml +++ b/.github/workflows/release-latest.yml @@ -65,7 +65,7 @@ jobs: needs: release_latest steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: ./.github/actions/setup-pnpm diff --git a/.github/workflows/test-bun.yml b/.github/workflows/test-bun.yml index 3a5e35b..dc224b4 100644 --- a/.github/workflows/test-bun.yml +++ b/.github/workflows/test-bun.yml @@ -17,7 +17,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: ./.github/actions/setup-bun diff --git a/.github/workflows/test-nodejs.yml b/.github/workflows/test-nodejs.yml index d463470..9b75a28 100644 --- a/.github/workflows/test-nodejs.yml +++ b/.github/workflows/test-nodejs.yml @@ -17,7 +17,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: ./.github/actions/setup-pnpm diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml index 7a7e633..d6808a9 100644 --- a/.github/workflows/update-changelog.yml +++ b/.github/workflows/update-changelog.yml @@ -13,7 +13,7 @@ jobs: contents: write steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/bun.lockb b/bun.lockb index f067fd7f8d066393fb4763c1a89d102411f0e3c1..12ed0239385c0a754242153cbd5de25826f1745d 100755 GIT binary patch delta 63371 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Φj(d1KKpen3 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 Kcc7)qr~d~?C(e2R diff --git a/docs/docs/guides/01-getting-started.md b/docs/docs/guides/01-getting-started.md index 29bf967..683a64b 100644 --- a/docs/docs/guides/01-getting-started.md +++ b/docs/docs/guides/01-getting-started.md @@ -1,21 +1,35 @@ # Getting started +[PocketIC](https://github.com/dfinity/pocketic) is a deterministic, lightweight and versatile testing solution for [Internet Computer canisters](https://internetcomputer.org/how-it-works/canister-lifecycle/). + +PicJS provides bindings to interact with [PocketIC](https://github.com/dfinity/pocketic) from Typescript and JavaScript. + ## JavaScript runtime environment -Tests written with PicJS are executed in a JavaScript runtime environment, such as [NodeJS](https://nodejs.org/en). To get started with PicJS, you will need to have a JavaScript runtime environment installed on your system. +Tests written with PicJS are executed in a JavaScript runtime environment, such as [NodeJS](https://nodejs.org/en). -[Bun](https://bun.sh/) is an alternative JavaScript runtime environment that is compatible with PicJS. Bun has several features that make it a great choice for running PicJS tests, such as a built-in test runner and assertion library in addition to being much more performant than NodeJS. +To get started with PicJS, you will need to have a JavaScript runtime environment installed on your system. If you're new to JavaScript, then [NodeJS](https://nodejs.org/en) is the recommended choice. -[Deno](https://deno.com/) in theory should also work, but it is not officially supported and compatibility is not actively tested. If you choose Deno and run into issues, please open an issue on the [GitHub repository](https://github.com/hadronous/pic-js/issues). +[Bun](https://bun.sh/) is an alternative JavaScript runtime environment that is compatible with PicJS. Bun has several features that make it a great choice for running PicJS tests, such as a built-in test runner and assertion library in addition to being much more performant than [NodeJS](https://nodejs.org/en). Bun is not very widely used yet, so it is not recommended for beginners. + +[Deno](https://deno.com/) in theory should also work, but it is not officially supported and compatibility is not actively tested. If you choose Deno and run into issues, please open an issue on the [GitHub repository](https://github.com/hadronous/pic-js/issues). Deno is also not widely used, so it is not recommended for developers that are unfamiliar with it. ## Package manager PicJS is a JavaScript/TypeScript package distributed on [NPM](https://www.npmjs.com/package/@hadronous/pic). To install and manage NPM packages, you will need to have an NPM-compatible package manager. -- [npm](https://nodejs.org/en/learn/getting-started/an-introduction-to-the-npm-package-manager) is the official package manager for NodeJS and comes pre-installed with NodeJS. -- [pnpm](https://pnpm.io/) is a fast, disk space-efficient package manager. -- [Yarn](https://yarnpkg.com/) is a package manager that doubles down as a project manager. -- [Bun](https://bun.sh/) also includes a built-in package manager. +- [npm](https://nodejs.org/en/learn/getting-started/an-introduction-to-the-npm-package-manager) + - This is the official package manager for [NodeJS](https://nodejs.org/en) and comes pre-installed. + - Beginners should stick with this option. +- [pnpm](https://pnpm.io/) + - A fast, disk space-efficient package manager. + - A great alternative to [npm](https://nodejs.org/en/learn/getting-started/an-introduction-to-the-npm-package-manager) for more experienced developers. +- [Yarn](https://yarnpkg.com/) + - A package manager that doubles down as a project manager. + - Another great alternative to [npm](https://nodejs.org/en/learn/getting-started/an-introduction-to-the-npm-package-manager) for more experienced developers. +- [Bun](https://bun.sh/) + - Bun also includes a built-in package manager. + - This is convenient if you are already using Bun as your runtime environment or test runner. ## DFX @@ -30,3 +44,20 @@ sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)" You can also check out the official [DFX installation documentation](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) for more information. DFX version 0.16.0 or later is recommended when writing PicJS tests. If you are on an earlier version then make sure to check out the [Canister declarations guide](./canister-declarations) to see how declarations should be used with older versions with DFX. + +## Test runner + +PicJS tests can be run with any test runner that runs on [NodeJS](https://nodejs.org/en) or [Bun](https://bun.sh/) (in theory the same should be true for [Deno](https://deno.com/), but that is not actively tested). + +The following test runners are actively tested and officially supported: + +- [Jest](https://jestjs.io/) + - Recommended if you're new to JavaScript testing because it has the largest community and is the most widely used. + - See the [Jest guide](./using-jest) for details on getting started with Jest and PicJS. +- [Vitest](https://vitest.dev/) + - If you're already using [Vite](https://vitejs.dev/) and Vitest for your frontend, then this is a good choice to reduce your dev dependencies. + - See the [Vitest guide](./using-vitest) for details on getting started with Vitest and PicJS. +- [Bun](https://bun.sh/) + - If you're already using Bun, or want to try it out, then this is a good choice. + - This is not recommended for beginners because it is less widely used and still immature compared to the other options. + - See the [Bun guide](./using-bun) for details on getting started with Bun and PicJS. diff --git a/docs/docs/guides/02-using-jest.md b/docs/docs/guides/02-using-jest.md new file mode 100644 index 0000000..ceb037e --- /dev/null +++ b/docs/docs/guides/02-using-jest.md @@ -0,0 +1,208 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Using Jest + +[Jest](https://jestjs.io) is a JavaScript testing framework that is widely used in the JavaScript community. It is recommended for beginners because it has the largest community and is the most widely used. Jest is also the officially supported test runner for PicJS. + +## Setup + +To get started with [Jest](https://jestjs.io), install the relevant packages using your preferred package manager: + + + + ```shell + npm i -D jest @types/jest @types/node ts-jest + ``` + + + + ```shell + pnpm i -D jest @types/jest @types/node ts-jest + ``` + + + + ```shell + yarn add -D jest @types/jest @types/node ts-jest + ``` + + + + ```shell + bun add -d jest @types/jest @types/node ts-jest + ``` + + + +Create a `tsconfig.json` file: + +```json title="tsconfig.json" +{ + "compilerOptions": { + // enable latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "allowJs": true, // allow importing `.js` from `.ts` + "types": ["jest", "node"], + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags + "useUnknownInCatchVariables": true, + "noPropertyAccessFromIndexSignature": true + }, + "include": [ + "./src/**/*.ts", + "./global-setup.ts", + "./global-teardown.ts", + "./types.d.ts" + ] +} +``` + +Create a `jest.config.ts` file: + +```ts title="jest.config.ts" +import type { Config } from 'jest'; + +const config: Config = { + watch: false, + preset: 'ts-jest/presets/js-with-ts', + testEnvironment: 'node', + globalSetup: '/global-setup.ts', + globalTeardown: '/global-teardown.ts', +}; + +export default config; +``` + +You can also check out the official the [`ts-jest` documentation](https://kulshekhar.github.io/ts-jest/docs/) for more information on configuring this file. + +Then, add a `test` script to your `package.json`: + +```json title="package.json" +{ + "scripts": { + "test": "jest" + } +} +``` + +The PocketIC server needs to be started before running tests and stopped once they're finished running. This can be done by creating `global-setup.ts` and `global-teardown.ts` files in your project's root directory: + +```ts title="global-setup.ts" +import { PocketIcServer } from '@hadronous/pic'; + +module.exports = async function (): Promise { + const pic = await PocketIcServer.start(); + const url = pic.getUrl(); + + process.env.PIC_URL = url; + global.__PIC__ = pic; +}; +``` + +```ts title="global-teardown.ts" +module.exports = async function () { + await global.__PIC__.stop(); +}; +``` + +To improve type-safety for `process.env.PIC_URL` and `global.__PIC__`, create a `types.d.ts` file: + +```ts title="types.d.ts" +import { PocketIcServer } from '@hadronous/pic'; + +declare global { + declare var __PIC__: PocketIcServer; + + namespace NodeJS { + interface ProcessEnv { + PIC_URL: string; + } + } +} +``` + +## Writing tests + +[Jest](https://jestjs.io) tests are very similar to tests written with [Jasmine](https://jasmine.github.io), or [Vitest](https://vitest.dev) so they will feel very familiar to developers who have used these frameworks before. + +The basic skeleton of all PicJS tests written with [Jest](https://jestjs.io) will look something like this: + +```ts title="tests/example.spec.ts" +// Import generated types for your canister +import { type _SERVICE } from '../../declarations/backend/backend.did'; + +// Define the path to your canister's WASM file +export const WASM_PATH = resolve( + import.meta.dir, + '..', + '..', + 'target', + 'wasm32-unknown-unknown', + 'release', + 'backend.wasm', +); + +// The `describe` function is used to group tests together +// and is completely optional. +describe('Test suite name', () => { + // Define variables to hold our PocketIC instance, canister ID, + // and an actor to interact with our canister. + let pic: PocketIc; + let canisterId: Principal; + let actor: Actor<_SERVICE>; + + // The `beforeEach` hook runs before each test. + // + // This can be replaced with a `beforeAll` hook to persist canister + // state between tests. + beforeEach(async () => { + // create a new PocketIC instance + pic = await PocketIc.create(process.env.PIC_URL); + + // Setup the canister and actor + const fixture = await pic.setupCanister<_SERVICE>({ + idlFactory, + wasm: WASM_PATH, + }); + + // Save the actor and canister ID for use in tests + actor = fixture.actor; + canisterId = fixture.canisterId; + }); + + // The `afterEach` hook runs after each test. + // + // This should be replaced with an `afterAll` hook if you use + // a `beforeAll` hook instead of a `beforeEach` hook. + afterEach(async () => { + // tear down the PocketIC instance + await pic.tearDown(); + }); + + // The `it` function is used to define individual tests + it('should do something cool', async () => { + const response = await actor.do_something_cool(); + + expect(response).toEqual('cool'); + }); +}); +``` + +You can also check out the official [Jest getting started documentation](https://jestjs.io/docs/getting-started) for more information on writing tests. diff --git a/docs/docs/guides/03-using-vitest.md b/docs/docs/guides/03-using-vitest.md new file mode 100644 index 0000000..52b8ad0 --- /dev/null +++ b/docs/docs/guides/03-using-vitest.md @@ -0,0 +1,184 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Using Vitest + +[Vitest](https://vitest.dev/) is a modern JavaScript testing framework that integrates with the [Vite](https://vitejs.dev/) bundler. It is recommended for developers that are already using [Vite](https://vitejs.dev/) or [Vitest](https://vitest.dev/) on their project. + +## Setup + +To get started with [Vitest](https://vitest.dev/), install the relevant packages using your preferred package manager: + + + + ```shell + npm i -D vitest @types/node + ``` + + + + ```shell + pnpm i -D vitest @types/node + ``` + + + + ```shell + yarn add -D vitest @types/node + ``` + + + + ```shell + bun add -d vitest @types/node + ``` + + + +Create a `tsconfig.json` file: + +```json title="tsconfig.json" +{ + "compilerOptions": { + // enable latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "allowJs": true, // allow importing `.js` from `.ts` + "types": ["node"], + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags + "useUnknownInCatchVariables": true, + "noPropertyAccessFromIndexSignature": true + }, + "include": ["./src/**/*.ts", "./global-setup.ts", "./types.d.ts"] +} +``` + +The PocketIC server needs to be started before running tests and stopped once they're finished running. This can be done by creating a `global-setup.ts` file in your project's root directory: + +```ts title="global-setup.ts" +import type { GlobalSetupContext } from 'vitest/node'; +import { PocketIcServer } from '@hadronous/pic'; + +let pic: PocketIcServer | undefined; + +export async function setup(ctx: GlobalSetupContext): Promise { + pic = await PocketIcServer.start(); + const url = pic.getUrl(); + + ctx.provide('PIC_URL', url); +} + +export async function teardown(): Promise { + await pic?.stop(); +} +``` + +To improve type-safety for `ctx.provide('PIC_URL')` and (later) `inject('PIC_URL')`, create a `types.d.ts` file: + +```typescript title="types.d.ts" +export declare module 'vitest' { + export interface ProvidedContext { + PIC_URL: string; + } +} +``` + +Create a `vitest.config.ts` file: + +```typescript title="vitest.config.ts" +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + root: 'examples/counter/tests', + globalSetup: './global-setup.ts', + }, +}); +``` + +## Writing tests + +[Vitest](https://vitest.dev/) tests are very similar to tests written with [Jest](https://jestjs.io/) or [Jasmine](https://jasmine.github.io), so they will feel very familiar to developers who have used these frameworks before. + +The basic skeleton of all PicJS tests written with [Vitest](https://vitest.dev/) will look something like this: + +```typescript title="tests/example.spec.ts" +import { describe, beforeEach, afterEach, it, expect, inject } from 'vitest'; + +// Import generated types for your canister +import { type _SERVICE } from '../../declarations/backend/backend.did'; + +// Define the path to your canister's WASM file +export const WASM_PATH = resolve( + import.meta.dir, + '..', + '..', + 'target', + 'wasm32-unknown-unknown', + 'release', + 'backend.wasm', +); + +// The `describe` function is used to group tests together +// and is completely optional. +describe('Test suite name', () => { + // Define variables to hold our PocketIC instance, canister ID, + // and an actor to interact with our canister. + let pic: PocketIc; + let canisterId: Principal; + let actor: Actor<_SERVICE>; + + // The `beforeEach` hook runs before each test. + // + // This can be replaced with a `beforeAll` hook to persist canister + // state between tests. + beforeEach(async () => { + // create a new PocketIC instance + pic = await PocketIc.create(inject('PIC_URL')); + + // Setup the canister and actor + const fixture = await pic.setupCanister<_SERVICE>({ + idlFactory, + wasm: WASM_PATH, + }); + + // Save the actor and canister ID for use in tests + actor = fixture.actor; + canisterId = fixture.canisterId; + }); + + // The `afterEach` hook runs after each test. + // + // This should be replaced with an `afterAll` hook if you use + // a `beforeAll` hook instead of a `beforeEach` hook. + afterEach(async () => { + // tear down the PocketIC instance + await pic.tearDown(); + }); + + // The `it` function is used to define individual tests + it('should do something cool', async () => { + const response = await actor.do_something_cool(); + + expect(response).toEqual('cool'); + }); +}); +``` + +You can check out the official [Vitest documentation](https://vitest.dev/) for more information writing tests. diff --git a/docs/docs/guides/02-using-bun.md b/docs/docs/guides/04-using-bun.md similarity index 66% rename from docs/docs/guides/02-using-bun.md rename to docs/docs/guides/04-using-bun.md index c1b2f04..88d5c92 100644 --- a/docs/docs/guides/02-using-bun.md +++ b/docs/docs/guides/04-using-bun.md @@ -7,7 +7,7 @@ import TabItem from '@theme/TabItem'; ## Installing -Installing Bun is as simple as running: +Installing [Bun](https://bun.sh) is as simple as running: ```shell curl -fsSL https://bun.sh/install | bash @@ -17,16 +17,15 @@ You can also check out the official [Bun installation documentation](https://bun ## As a test runner -### TypeScript +### Installation -To get started with Bun as a test runner with TypeScript, install the `@types/bun` package using your preferred package manager: +To get started with [Bun](https://bun.sh) as a test runner, install the `@types/bun` package using your preferred package manager: ```shell npm i -D @types/bun ``` - @@ -50,9 +49,8 @@ To get started with Bun as a test runner with TypeScript, install the `@types/bu Create a `tsconfig.json` file: -```json +```json title="tsconfig.json" { - "include": ["./**/*.ts"], "compilerOptions": { // enable latest features "lib": ["ESNext"], @@ -77,13 +75,14 @@ Create a `tsconfig.json` file: // Some stricter flags "useUnknownInCatchVariables": true, "noPropertyAccessFromIndexSignature": true - } + }, + "include": ["./src/**/*.ts", "./global-setup.ts", "./types.d.ts"] } ``` Then, add a `test` script to your `package.json`: -```json +```json title="package.json" { "scripts": { "test": "tsc && bun test" @@ -93,29 +92,58 @@ Then, add a `test` script to your `package.json`: Running `tsc` is optional, but it is recommended to catch any TypeScript errors before running your tests. -And that's it! Bun will automatically detect the `tsconfig.json` file and use it to compile your TypeScript files. - You can also check out the official [Bun documentation for TypeScript](https://bun.sh/docs/typescript) for more information. -### JavaScript +### Global test setup -Add a `test` script to your `package.json`: +The PocketIC server needs to be started before running tests and stopped once they're finished running. This can be done by creating a `global-setup.ts` file in your project's root directory: -```json -{ - "scripts": { - "test": "bun test" +```ts title="global-setup.ts" +import { beforeAll, afterAll } from 'bun:test'; +import { PocketIcServer } from '@hadronous/pic'; + +let pic: PocketIcServer | undefined; + +beforeAll(async () => { + pic = await PocketIcServer.start(); + const url = pic.getUrl(); + + process.env.PIC_URL = url; +}); + +afterAll(async () => { + await pic?.stop(); +}); +``` + +This file can be configured to run with `bun test` by creating a `bunfig.toml` file in your project's root directory: + +```toml title="bunfig.toml" +[test] +preload = ["./global-setup.ts"] +``` + +To improve the type-safety of using `process.env.PIC_URL`, add a `types.d.ts` file in your project's root directory: + +```ts title="types.d.ts" +declare global { + namespace NodeJS { + interface ProcessEnv { + PIC_URL: string; + } } } + +export {}; ``` ### Writing tests -Bun tests are very similar to tests written with [Jest](https://jestjs.io), [Jasmine](https://jasmine.github.io), or [Vitest](https://vitest.dev) so they will feel very familiar to developers who have used these frameworks before. +[Bun](https://bun.sh) tests are very similar to tests written with [Jest](https://jestjs.io), [Jasmine](https://jasmine.github.io), or [Vitest](https://vitest.dev) so they will feel very familiar to developers who have used these frameworks before. -The basic skeleton of all PicJS tests written with Bun will look something like this: +The basic skeleton of all PicJS tests written with [Bun](https://bun.sh) will look something like this: -```typescript +```ts title="tests/example.spec.ts" // Import Bun testing globals import { beforeEach, describe, expect, it } from 'bun:test'; @@ -148,7 +176,7 @@ describe('Test suite name', () => { // state between tests. beforeEach(async () => { // create a new PocketIC instance - pic = await PocketIc.create(); + pic = await PocketIc.create(process.env.PIC_URL); // Setup the canister and actor const fixture = await pic.setupCanister<_SERVICE>({ @@ -161,6 +189,15 @@ describe('Test suite name', () => { canisterId = fixture.canisterId; }); + // The `afterEach` hook runs after each test. + // + // This should be replaced with an `afterAll` hook if you use + // a `beforeAll` hook instead of a `beforeEach` hook. + afterEach(async () => { + // tear down the PocketIC instance + await pic.tearDown(); + }); + // The `it` function is used to define individual tests it('should do something cool', async () => { const response = await actor.do_something_cool(); @@ -176,7 +213,7 @@ You can also check out the official [Bun test runner documentation](https://bun. PicJS leverages a [`postinstall`](https://docs.npmjs.com/cli/v9/using-npm/scripts#npm-install) script to download the `pocket-ic` binary. This is done to avoid bundling the binary with the library. If you are using [bun](https://bun.sh) to manage your project's dependencies, then you will need to add `@hadronous/pic` as a [trusted dependency](https://bun.sh/docs/install/lifecycle#trusteddependencies) in your `package.json`: -```json +```json title="package.json" { "trustedDependencies": ["@hadronous/pic"] } diff --git a/docs/docs/guides/04-working-with-the-nns.md b/docs/docs/guides/05-working-with-the-nns.md similarity index 98% rename from docs/docs/guides/04-working-with-the-nns.md rename to docs/docs/guides/05-working-with-the-nns.md index 267cd30..1f80768 100644 --- a/docs/docs/guides/04-working-with-the-nns.md +++ b/docs/docs/guides/05-working-with-the-nns.md @@ -20,7 +20,7 @@ cd nns_state Add a local system network to the `dfx.json` file. This will create the appropriate network configuration for the NNS without affecting any other projects that are running on DFX: -```json +```json title="dfx.json" { // redacted... "networks": { @@ -170,7 +170,7 @@ tar -xvf path/to/tests/state/nns_state.tar.gz -C path/to/tests/state This could be done with an `npm` `postinstall` script, by adding the following to your `package.json`: -```json +```json title="package.json" { // redacted... "scripts": { @@ -185,14 +185,14 @@ This could be done with an `npm` `postinstall` script, by adding the following t You'll need the subnet Id that you recored earlier: -```typescript +```ts const NNS_SUBNET_ID = 'nt6ha-vabpm-j6nog-bkr62-vbgbt-swwzc-u54zn-odtoy-igwlu-ab7uj-4qe'; ``` And you'll need to reference the path to the NNS state: -```typescript +```ts const NNS_STATE_PATH = resolve( __dirname, '..', @@ -205,7 +205,7 @@ const NNS_STATE_PATH = resolve( Now you can setup your PocketIC instance to use the NNS state: -```typescript +```ts const pic = await PocketIc.create({ nns: { fromPath: NNS_STATE_PATH, @@ -216,7 +216,7 @@ const pic = await PocketIc.create({ After creating the instance, make sure to set the PocketIc time to be the same or greater than the time that you created the NNS state: -```typescript +```ts await pic.setTime(new Date(2024, 1, 30).getTime()); await pic.tick(); ``` diff --git a/docs/docs/guides/03-canister-declarations.md b/docs/docs/guides/06-canister-declarations.md similarity index 100% rename from docs/docs/guides/03-canister-declarations.md rename to docs/docs/guides/06-canister-declarations.md diff --git a/examples/clock/tests/global-setup.ts b/examples/clock/tests/global-setup.ts new file mode 100644 index 0000000..2781a92 --- /dev/null +++ b/examples/clock/tests/global-setup.ts @@ -0,0 +1,9 @@ +import { PocketIcServer } from '@hadronous/pic'; + +module.exports = async function (): Promise { + const pic = await PocketIcServer.start(); + const url = pic.getUrl(); + + process.env.PIC_URL = url; + global.__PIC__ = pic; +}; diff --git a/examples/clock/tests/global-teardown.ts b/examples/clock/tests/global-teardown.ts new file mode 100644 index 0000000..487cdff --- /dev/null +++ b/examples/clock/tests/global-teardown.ts @@ -0,0 +1,3 @@ +module.exports = async function () { + await global.__PIC__.stop(); +}; diff --git a/examples/clock/tests/jest.config.ts b/examples/clock/tests/jest.config.ts index ba9ef6a..57850cf 100644 --- a/examples/clock/tests/jest.config.ts +++ b/examples/clock/tests/jest.config.ts @@ -4,6 +4,8 @@ const config: Config = { watch: false, preset: 'ts-jest/presets/js-with-ts', testEnvironment: 'node', + globalSetup: '/global-setup.ts', + globalTeardown: '/global-teardown.ts', }; export default config; diff --git a/examples/clock/tests/src/clock.spec.ts b/examples/clock/tests/src/clock.spec.ts index aede16c..853609b 100644 --- a/examples/clock/tests/src/clock.spec.ts +++ b/examples/clock/tests/src/clock.spec.ts @@ -1,6 +1,7 @@ import { resolve } from 'node:path'; import { Principal } from '@dfinity/principal'; import { Actor, PocketIc } from '@hadronous/pic'; + import { _SERVICE, idlFactory } from '../../declarations/clock.did'; const WASM_PATH = resolve( @@ -22,7 +23,7 @@ describe('Clock', () => { let canisterId: Principal; beforeEach(async () => { - pic = await PocketIc.create(); + pic = await PocketIc.create(process.env.PIC_URL); const fixture = await pic.setupCanister<_SERVICE>({ idlFactory: idlFactory, wasm: WASM_PATH, diff --git a/examples/clock/tests/tsconfig.json b/examples/clock/tests/tsconfig.json index e85fbe5..b09bcf8 100644 --- a/examples/clock/tests/tsconfig.json +++ b/examples/clock/tests/tsconfig.json @@ -4,5 +4,10 @@ "allowJs": true, "types": ["jest", "node"] }, - "include": ["./src/**/*.ts"] + "include": [ + "./src/**/*.ts", + "./global-setup.ts", + "./global-teardown.ts", + "./types.d.ts" + ] } diff --git a/examples/clock/tests/types.d.ts b/examples/clock/tests/types.d.ts new file mode 100644 index 0000000..b8adfa6 --- /dev/null +++ b/examples/clock/tests/types.d.ts @@ -0,0 +1,11 @@ +import { PocketIcServer } from '@hadronous/pic'; + +declare global { + declare var __PIC__: PocketIcServer; + + namespace NodeJS { + interface ProcessEnv { + PIC_URL: string; + } + } +} diff --git a/examples/counter/tests/global-setup.ts b/examples/counter/tests/global-setup.ts new file mode 100644 index 0000000..6b6744d --- /dev/null +++ b/examples/counter/tests/global-setup.ts @@ -0,0 +1,15 @@ +import type { GlobalSetupContext } from 'vitest/node'; +import { PocketIcServer } from '@hadronous/pic'; + +let pic: PocketIcServer | undefined; + +export async function setup(ctx: GlobalSetupContext): Promise { + pic = await PocketIcServer.start(); + const url = pic.getUrl(); + + ctx.provide('PIC_URL', url); +} + +export async function teardown(): Promise { + await pic?.stop(); +} diff --git a/examples/counter/tests/jest.config.ts b/examples/counter/tests/jest.config.ts deleted file mode 100644 index ba9ef6a..0000000 --- a/examples/counter/tests/jest.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Config } from 'jest'; - -const config: Config = { - watch: false, - preset: 'ts-jest/presets/js-with-ts', - testEnvironment: 'node', -}; - -export default config; diff --git a/examples/counter/tests/package.json b/examples/counter/tests/package.json index 6c5f97a..bb68043 100644 --- a/examples/counter/tests/package.json +++ b/examples/counter/tests/package.json @@ -1,5 +1,6 @@ { "name": "counter-tests", + "type": "module", "devDependencies": { "@hadronous/pic": "workspace:*" } diff --git a/examples/counter/tests/src/counter.spec.ts b/examples/counter/tests/src/counter.spec.ts index 94f284f..e42a838 100644 --- a/examples/counter/tests/src/counter.spec.ts +++ b/examples/counter/tests/src/counter.spec.ts @@ -2,7 +2,9 @@ import { resolve } from 'node:path'; import { Principal } from '@dfinity/principal'; import { Actor, PocketIc } from '@hadronous/pic'; import { IDL } from '@dfinity/candid'; -import { _SERVICE, idlFactory, init } from '../../declarations/counter.did'; +import { describe, beforeEach, afterEach, it, expect, inject } from 'vitest'; + +import { _SERVICE, idlFactory, init } from '../../declarations/counter.did.js'; const WASM_PATH = resolve( __dirname, @@ -25,7 +27,7 @@ describe('Counter', () => { const countInitArg = 1n; beforeEach(async () => { - pic = await PocketIc.create(); + pic = await PocketIc.create(inject('PIC_URL')); const fixture = await pic.setupCanister<_SERVICE>({ idlFactory, wasm: WASM_PATH, diff --git a/examples/counter/tests/tsconfig.json b/examples/counter/tests/tsconfig.json index e85fbe5..29957a5 100644 --- a/examples/counter/tests/tsconfig.json +++ b/examples/counter/tests/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "allowJs": true, - "types": ["jest", "node"] + "types": ["node"] }, - "include": ["./src/**/*.ts"] + "include": ["./src/**/*.ts", "./global-setup.ts", "types.d.ts"] } diff --git a/examples/counter/tests/types.d.ts b/examples/counter/tests/types.d.ts new file mode 100644 index 0000000..8b8ef52 --- /dev/null +++ b/examples/counter/tests/types.d.ts @@ -0,0 +1,5 @@ +export declare module 'vitest' { + export interface ProvidedContext { + PIC_URL: string; + } +} diff --git a/examples/counter/tests/vitest.config.ts b/examples/counter/tests/vitest.config.ts new file mode 100644 index 0000000..5611772 --- /dev/null +++ b/examples/counter/tests/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + root: 'examples/counter/tests', + globalSetup: './global-setup.ts', + }, +}); diff --git a/examples/multicanister/tests/global-setup.ts b/examples/multicanister/tests/global-setup.ts new file mode 100644 index 0000000..2781a92 --- /dev/null +++ b/examples/multicanister/tests/global-setup.ts @@ -0,0 +1,9 @@ +import { PocketIcServer } from '@hadronous/pic'; + +module.exports = async function (): Promise { + const pic = await PocketIcServer.start(); + const url = pic.getUrl(); + + process.env.PIC_URL = url; + global.__PIC__ = pic; +}; diff --git a/examples/multicanister/tests/global-teardown.ts b/examples/multicanister/tests/global-teardown.ts new file mode 100644 index 0000000..487cdff --- /dev/null +++ b/examples/multicanister/tests/global-teardown.ts @@ -0,0 +1,3 @@ +module.exports = async function () { + await global.__PIC__.stop(); +}; diff --git a/examples/multicanister/tests/jest.config.ts b/examples/multicanister/tests/jest.config.ts index ba9ef6a..57850cf 100644 --- a/examples/multicanister/tests/jest.config.ts +++ b/examples/multicanister/tests/jest.config.ts @@ -4,6 +4,8 @@ const config: Config = { watch: false, preset: 'ts-jest/presets/js-with-ts', testEnvironment: 'node', + globalSetup: '/global-setup.ts', + globalTeardown: '/global-teardown.ts', }; export default config; diff --git a/examples/multicanister/tests/src/multicanister.spec.ts b/examples/multicanister/tests/src/multicanister.spec.ts index bdd4562..1385485 100644 --- a/examples/multicanister/tests/src/multicanister.spec.ts +++ b/examples/multicanister/tests/src/multicanister.spec.ts @@ -1,5 +1,7 @@ import { resolve } from 'path'; import { Actor, PocketIc } from '@hadronous/pic'; +import { IDL } from '@dfinity/candid'; + import { PhoneBookEntry, SuperHero, @@ -7,7 +9,6 @@ import { idlFactory, init, } from '../../declarations/multicanister/multicanister.did'; -import { IDL } from '@dfinity/candid'; const MAIN_WASM_PATH = resolve( __dirname, @@ -53,7 +54,7 @@ describe('Multicanister', () => { let actor: Actor<_SERVICE>; beforeEach(async () => { - pic = await PocketIc.create({ application: 2 }); + pic = await PocketIc.create(process.env.PIC_URL, { application: 2 }); const applicationSubnets = pic.getApplicationSubnets(); const mainSubnet = applicationSubnets[0]; diff --git a/examples/multicanister/tests/tsconfig.json b/examples/multicanister/tests/tsconfig.json index e85fbe5..b09bcf8 100644 --- a/examples/multicanister/tests/tsconfig.json +++ b/examples/multicanister/tests/tsconfig.json @@ -4,5 +4,10 @@ "allowJs": true, "types": ["jest", "node"] }, - "include": ["./src/**/*.ts"] + "include": [ + "./src/**/*.ts", + "./global-setup.ts", + "./global-teardown.ts", + "./types.d.ts" + ] } diff --git a/examples/multicanister/tests/types.d.ts b/examples/multicanister/tests/types.d.ts new file mode 100644 index 0000000..b8adfa6 --- /dev/null +++ b/examples/multicanister/tests/types.d.ts @@ -0,0 +1,11 @@ +import { PocketIcServer } from '@hadronous/pic'; + +declare global { + declare var __PIC__: PocketIcServer; + + namespace NodeJS { + interface ProcessEnv { + PIC_URL: string; + } + } +} diff --git a/examples/nns_proxy/tests/global-setup.ts b/examples/nns_proxy/tests/global-setup.ts new file mode 100644 index 0000000..2781a92 --- /dev/null +++ b/examples/nns_proxy/tests/global-setup.ts @@ -0,0 +1,9 @@ +import { PocketIcServer } from '@hadronous/pic'; + +module.exports = async function (): Promise { + const pic = await PocketIcServer.start(); + const url = pic.getUrl(); + + process.env.PIC_URL = url; + global.__PIC__ = pic; +}; diff --git a/examples/nns_proxy/tests/global-teardown.ts b/examples/nns_proxy/tests/global-teardown.ts new file mode 100644 index 0000000..487cdff --- /dev/null +++ b/examples/nns_proxy/tests/global-teardown.ts @@ -0,0 +1,3 @@ +module.exports = async function () { + await global.__PIC__.stop(); +}; diff --git a/examples/nns_proxy/tests/jest.config.ts b/examples/nns_proxy/tests/jest.config.ts index ba9ef6a..57850cf 100644 --- a/examples/nns_proxy/tests/jest.config.ts +++ b/examples/nns_proxy/tests/jest.config.ts @@ -4,6 +4,8 @@ const config: Config = { watch: false, preset: 'ts-jest/presets/js-with-ts', testEnvironment: 'node', + globalSetup: '/global-setup.ts', + globalTeardown: '/global-teardown.ts', }; export default config; diff --git a/examples/nns_proxy/tests/src/nns-proxy.spec.ts b/examples/nns_proxy/tests/src/nns-proxy.spec.ts index 434d137..709dfc3 100644 --- a/examples/nns_proxy/tests/src/nns-proxy.spec.ts +++ b/examples/nns_proxy/tests/src/nns-proxy.spec.ts @@ -38,7 +38,7 @@ describe('NNS Proxy', () => { const proposerIdentity = generateRandomIdentity(); beforeEach(async () => { - pic = await PocketIc.create({ + pic = await PocketIc.create(process.env.PIC_URL, { nns: { fromPath: NNS_STATE_PATH, subnetId: Principal.fromText(NNS_SUBNET_ID), diff --git a/examples/nns_proxy/tests/tsconfig.json b/examples/nns_proxy/tests/tsconfig.json index e85fbe5..b09bcf8 100644 --- a/examples/nns_proxy/tests/tsconfig.json +++ b/examples/nns_proxy/tests/tsconfig.json @@ -4,5 +4,10 @@ "allowJs": true, "types": ["jest", "node"] }, - "include": ["./src/**/*.ts"] + "include": [ + "./src/**/*.ts", + "./global-setup.ts", + "./global-teardown.ts", + "./types.d.ts" + ] } diff --git a/examples/nns_proxy/tests/types.d.ts b/examples/nns_proxy/tests/types.d.ts new file mode 100644 index 0000000..b8adfa6 --- /dev/null +++ b/examples/nns_proxy/tests/types.d.ts @@ -0,0 +1,11 @@ +import { PocketIcServer } from '@hadronous/pic'; + +declare global { + declare var __PIC__: PocketIcServer; + + namespace NodeJS { + interface ProcessEnv { + PIC_URL: string; + } + } +} diff --git a/examples/todo/tests/global-setup.ts b/examples/todo/tests/global-setup.ts new file mode 100644 index 0000000..2781a92 --- /dev/null +++ b/examples/todo/tests/global-setup.ts @@ -0,0 +1,9 @@ +import { PocketIcServer } from '@hadronous/pic'; + +module.exports = async function (): Promise { + const pic = await PocketIcServer.start(); + const url = pic.getUrl(); + + process.env.PIC_URL = url; + global.__PIC__ = pic; +}; diff --git a/examples/todo/tests/global-teardown.ts b/examples/todo/tests/global-teardown.ts new file mode 100644 index 0000000..487cdff --- /dev/null +++ b/examples/todo/tests/global-teardown.ts @@ -0,0 +1,3 @@ +module.exports = async function () { + await global.__PIC__.stop(); +}; diff --git a/examples/todo/tests/jest.config.ts b/examples/todo/tests/jest.config.ts index ba9ef6a..57850cf 100644 --- a/examples/todo/tests/jest.config.ts +++ b/examples/todo/tests/jest.config.ts @@ -4,6 +4,8 @@ const config: Config = { watch: false, preset: 'ts-jest/presets/js-with-ts', testEnvironment: 'node', + globalSetup: '/global-setup.ts', + globalTeardown: '/global-teardown.ts', }; export default config; diff --git a/examples/todo/tests/src/todo.spec.ts b/examples/todo/tests/src/todo.spec.ts index 40bcf80..4c34def 100644 --- a/examples/todo/tests/src/todo.spec.ts +++ b/examples/todo/tests/src/todo.spec.ts @@ -26,7 +26,7 @@ describe('Todo', () => { const bob = createIdentity('superSecretBobPassword'); beforeEach(async () => { - pic = await PocketIc.create(); + pic = await PocketIc.create(process.env.PIC_URL); const fixture = await pic.setupCanister<_SERVICE>({ idlFactory, wasm: WASM_PATH, diff --git a/examples/todo/tests/tsconfig.json b/examples/todo/tests/tsconfig.json index e85fbe5..b09bcf8 100644 --- a/examples/todo/tests/tsconfig.json +++ b/examples/todo/tests/tsconfig.json @@ -4,5 +4,10 @@ "allowJs": true, "types": ["jest", "node"] }, - "include": ["./src/**/*.ts"] + "include": [ + "./src/**/*.ts", + "./global-setup.ts", + "./global-teardown.ts", + "./types.d.ts" + ] } diff --git a/examples/todo/tests/types.d.ts b/examples/todo/tests/types.d.ts new file mode 100644 index 0000000..b8adfa6 --- /dev/null +++ b/examples/todo/tests/types.d.ts @@ -0,0 +1,11 @@ +import { PocketIcServer } from '@hadronous/pic'; + +declare global { + declare var __PIC__: PocketIcServer; + + namespace NodeJS { + interface ProcessEnv { + PIC_URL: string; + } + } +} diff --git a/package.json b/package.json index 67002e9..7fa46ed 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "build:examples": "dfx generate phonebook && dfx generate superheroes && dfx generate && dfx build --all --check", "test:examples": "run-p test:counter test:clock test:todo test:multicanister test:nns-proxy", "build:counter": "dfx generate counter && dfx build counter --check", - "test:counter": "jest -c ./examples/counter/tests/jest.config.ts", + "test:counter": "vitest run -c ./examples/counter/tests/vitest.config.ts", "build:clock": "dfx generate clock && dfx build clock --check", "test:clock": "jest -c ./examples/clock/tests/jest.config.ts", "build:todo": "dfx generate todo && dfx build todo --check", @@ -40,6 +40,7 @@ "prettier": "3.1.0", "ts-jest": "^29.1.1", "ts-node": "^10.9.2", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "vitest": "^1.5.0" } } diff --git a/packages/pic/src/index.ts b/packages/pic/src/index.ts index 46e7f61..8d369f6 100644 --- a/packages/pic/src/index.ts +++ b/packages/pic/src/index.ts @@ -10,3 +10,4 @@ export type { SetupCanisterOptions, UpgradeCanisterOptions, } from './pocket-ic-types'; +export { PocketIcServer } from './pocket-ic-server'; diff --git a/packages/pic/src/pocket-ic-server.ts b/packages/pic/src/pocket-ic-server.ts index 451d27e..e7a4268 100644 --- a/packages/pic/src/pocket-ic-server.ts +++ b/packages/pic/src/pocket-ic-server.ts @@ -65,8 +65,18 @@ export class PocketIcServer { return this.url; } - public stop(): void { - this.serverProcess.unref(); + public async stop(): Promise { + return new Promise((resolve, reject) => { + this.serverProcess.on('exit', () => { + resolve(); + }); + + this.serverProcess.on('error', error => { + reject(error); + }); + + this.serverProcess.kill(); + }); } private static getBinPath(): string { diff --git a/packages/pic/src/pocket-ic.ts b/packages/pic/src/pocket-ic.ts index 9eb20cd..e5e67cc 100644 --- a/packages/pic/src/pocket-ic.ts +++ b/packages/pic/src/pocket-ic.ts @@ -1,7 +1,6 @@ import { Principal } from '@dfinity/principal'; import { IDL } from '@dfinity/candid'; import { optional, readFileAsBytes } from './util'; -import { PocketIcServer } from './pocket-ic-server'; import { PocketIcClient } from './pocket-ic-client'; import { ActorInterface, Actor, createActorClass } from './pocket-ic-actor'; import { @@ -71,14 +70,12 @@ import { * ``` */ export class PocketIc { - private constructor( - private readonly client: PocketIcClient, - private readonly server?: PocketIcServer, - ) {} + private constructor(private readonly client: PocketIcClient) {} /** - * Starts the PocketIC server and creates a PocketIC instance. + * Creates a PocketIC instance. * + * @param url The URL of an existing PocketIC server to connect to. * @param options Options for creating the PocketIC instance see {@link CreateInstanceOptions}. * @returns A new PocketIC instance. * @@ -86,33 +83,11 @@ export class PocketIc { * ```ts * import { PocketIc } from '@hadronous/pic'; * + * const url = 'http://localhost:8080'; * const pic = await PocketIc.create(); * ``` */ public static async create( - options?: CreateInstanceOptions, - ): Promise { - const server = await PocketIcServer.start(); - const client = await PocketIcClient.create(server.getUrl(), options); - - return new PocketIc(client, server); - } - - /** - * Creates a PocketIC instance that connects to an existing PocketIC server. - * - * @param url The URL of an existing PocketIC server to connect to. - * @returns A new PocketIC instance. - * - * @example - * ```ts - * import { PocketIc } from '@hadronous/pic'; - * - * const url = 'http://localhost:8080'; - * const pic = await PocketIc.createFromUrl(url); - * ``` - */ - public static async createFromUrl( url: string, options?: CreateInstanceOptions, ): Promise { @@ -663,7 +638,7 @@ export class PocketIc { } /** - * Deletes the PocketIC instance and disconnects from the server. + * Deletes the PocketIC instance. * * @example * ```ts @@ -675,7 +650,6 @@ export class PocketIc { */ public async tearDown(): Promise { await this.client.deleteInstance(); - this.server?.stop(); } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ffb1e6..05ca39d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: typescript: specifier: ^5.3.3 version: 5.3.3 + vitest: + specifier: ^1.5.0 + version: 1.5.0(@types/node@20.11.5) docs: dependencies: @@ -2442,6 +2445,213 @@ packages: - webpack-cli dev: false + /@esbuild/aix-ppc64@0.20.2: + resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.20.2: + resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.20.2: + resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.20.2: + resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.20.2: + resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.20.2: + resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.20.2: + resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.20.2: + resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.20.2: + resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.20.2: + resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.20.2: + resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.20.2: + resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.20.2: + resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.20.2: + resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.20.2: + resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.20.2: + resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.20.2: + resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.20.2: + resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.20.2: + resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.20.2: + resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.20.2: + resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.20.2: + resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.20.2: + resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@hapi/hoek@9.3.0: resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -2839,6 +3049,134 @@ packages: resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==} dev: false + /@rollup/rollup-android-arm-eabi@4.15.0: + resolution: {integrity: sha512-O63bJ7p909pRRQfOJ0k/Jp8gNFMud+ZzLLG5EBWquylHxmRT2k18M2ifg8WyjCgFVdpA7+rI0YZ8EkAtg6dSUw==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.15.0: + resolution: {integrity: sha512-5UywPdmC9jiVOShjQx4uuIcnTQOf85iA4jgg8bkFoH5NYWFfAfrJpv5eeokmTdSmYwUTT5IrcrBCJNkowhrZDA==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.15.0: + resolution: {integrity: sha512-hNkt75uFfWpRxHItCBmbS0ba70WnibJh6yz60WShSWITLlVRbkvAu1E/c7RlliPY4ajhqJd0UPZz//gNalTd4g==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.15.0: + resolution: {integrity: sha512-HnC5bTP7qdfO9nUw/mBhNcjOEZfbS8NwV+nFegiMhYOn1ATAGZF4kfAxR9BuZevBrebWCxMmxm8NCU1CUoz+wQ==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.15.0: + resolution: {integrity: sha512-QGOIQIJZeIIqMsc4BUGe8TnV4dkXhSW2EhaQ1G4LqMUNpkyeLztvlDlOoNHn7SR7a4dBANdcEbPkkEzz3rzjzA==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-musleabihf@4.15.0: + resolution: {integrity: sha512-PS/Cp8CinYgoysQ8i4UXYH/TZl06fXszvY/RDkyBYgUB1+tKyOMS925/4FZhfrhkl3XQEKjMc3BKtsxpB9Tz9Q==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.15.0: + resolution: {integrity: sha512-XzOsnD6lGDP+k+vGgTYAryVGu8N89qpjMN5BVFUj75dGVFP3FzIVAufJAraxirpDwEQZA7Gjs0Vo5p4UmnnjsA==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.15.0: + resolution: {integrity: sha512-+ScJA4Epbx/ZQGjDnbvTAcb8ZD06b+TlIka2UkujbKf1I/A+yrvEcJwG3/27zMmvcWMQyeCJhbL9TlSjzL0B7Q==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-powerpc64le-gnu@4.15.0: + resolution: {integrity: sha512-1cUSvYgnyTakM4FDyf/GxUCDcqmj/hUh1NOizEOJU7+D5xEfFGCxgcNOs3hYBeRMUCcGmGkt01EhD3ILgKpGHQ==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.15.0: + resolution: {integrity: sha512-3A1FbHDbBUvpJXFAZwVsiROIcstVHP9AX/cwnyIhAp+xyQ1cBCxywKtuzmw0Av1MDNNg/y/9dDHtNypfRa8bdw==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-s390x-gnu@4.15.0: + resolution: {integrity: sha512-hYPbhg9ow6/mXIkojc8LOeiip2sCTuw1taWyoOXTOWk9vawIXz8x7B4KkgWUAtvAElssxhSyEXr2EZycH/FGzQ==} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.15.0: + resolution: {integrity: sha512-511qln5mPSUKwv7HI28S1jCD1FK+2WbX5THM9A9annr3c1kzmfnf8Oe3ZakubEjob3IV6OPnNNcesfy+adIrmw==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.15.0: + resolution: {integrity: sha512-4qKKGTDIv2bQZ+afhPWqPL+94+dLtk4lw1iwbcylKlLNqQ/Yyjof2CFYBxf6npiDzPV+zf4EWRiHb26/4Vsm9w==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.15.0: + resolution: {integrity: sha512-nEtaFBHp1OnbOf+tz66DtID579sNRHGgMC23to8HUyVuOCpCMD0CvRNqiDGLErLNnwApWIUtUl1VvuovCWUxwg==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.15.0: + resolution: {integrity: sha512-5O49NykwSgX6iT2HgZ6cAoGHt6T/FqNMB5OqFOGxU/y1GyFSHquox1sK2OqApQc0ANxiHFQEMNDLNVCL7AUDnQ==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.15.0: + resolution: {integrity: sha512-YA0hTwCunmKNeTOFWdJuKhdXse9jBqgo34FDo+9aS0spfCkp+wj0o1bCcOOTu+0P48O95GTfkLTAaVonwNuIdQ==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@sideway/address@4.1.5: resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} dependencies: @@ -3392,6 +3730,45 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: false + /@vitest/expect@1.5.0: + resolution: {integrity: sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==} + dependencies: + '@vitest/spy': 1.5.0 + '@vitest/utils': 1.5.0 + chai: 4.4.1 + dev: true + + /@vitest/runner@1.5.0: + resolution: {integrity: sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==} + dependencies: + '@vitest/utils': 1.5.0 + p-limit: 5.0.0 + pathe: 1.1.2 + dev: true + + /@vitest/snapshot@1.5.0: + resolution: {integrity: sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==} + dependencies: + magic-string: 0.30.10 + pathe: 1.1.2 + pretty-format: 29.7.0 + dev: true + + /@vitest/spy@1.5.0: + resolution: {integrity: sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==} + dependencies: + tinyspy: 2.2.1 + dev: true + + /@vitest/utils@1.5.0: + resolution: {integrity: sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==} + dependencies: + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + /@webassemblyjs/ast@1.11.6: resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==} dependencies: @@ -3721,6 +4098,10 @@ packages: pvutils: 1.1.3 tslib: 2.6.2 + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /astring@1.8.6: resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==} hasBin: true @@ -4051,6 +4432,11 @@ packages: engines: {node: '>= 0.8'} dev: false + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + /cacheable-lookup@7.0.0: resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} engines: {node: '>=14.16'} @@ -4117,6 +4503,19 @@ packages: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} dev: false + /chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.3 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -4157,6 +4556,12 @@ packages: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} dev: false + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: true + /cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} dependencies: @@ -4354,6 +4759,10 @@ packages: /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + /confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + dev: true + /config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} dependencies: @@ -4775,6 +5184,13 @@ packages: optional: true dev: true + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + /deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -5124,6 +5540,37 @@ packages: is-symbol: 1.0.4 dev: true + /esbuild@0.20.2: + resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.20.2 + '@esbuild/android-arm': 0.20.2 + '@esbuild/android-arm64': 0.20.2 + '@esbuild/android-x64': 0.20.2 + '@esbuild/darwin-arm64': 0.20.2 + '@esbuild/darwin-x64': 0.20.2 + '@esbuild/freebsd-arm64': 0.20.2 + '@esbuild/freebsd-x64': 0.20.2 + '@esbuild/linux-arm': 0.20.2 + '@esbuild/linux-arm64': 0.20.2 + '@esbuild/linux-ia32': 0.20.2 + '@esbuild/linux-loong64': 0.20.2 + '@esbuild/linux-mips64el': 0.20.2 + '@esbuild/linux-ppc64': 0.20.2 + '@esbuild/linux-riscv64': 0.20.2 + '@esbuild/linux-s390x': 0.20.2 + '@esbuild/linux-x64': 0.20.2 + '@esbuild/netbsd-x64': 0.20.2 + '@esbuild/openbsd-x64': 0.20.2 + '@esbuild/sunos-x64': 0.20.2 + '@esbuild/win32-arm64': 0.20.2 + '@esbuild/win32-ia32': 0.20.2 + '@esbuild/win32-x64': 0.20.2 + dev: true + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -5228,7 +5675,6 @@ packages: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} dependencies: '@types/estree': 1.0.5 - dev: false /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} @@ -5275,6 +5721,21 @@ packages: signal-exit: 3.0.7 strip-final-newline: 2.0.0 + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: true + /exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} @@ -5606,6 +6067,10 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: true + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true + /get-intrinsic@1.2.2: resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} dependencies: @@ -5627,6 +6092,11 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + /get-symbol-description@1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} @@ -6118,6 +6588,11 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true + /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -6460,6 +6935,11 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -7019,6 +7499,10 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + /js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + dev: true + /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -7151,6 +7635,14 @@ packages: engines: {node: '>= 12.13.0'} dev: false + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + dependencies: + mlly: 1.6.1 + pkg-types: 1.1.0 + dev: true + /locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -7205,6 +7697,12 @@ packages: dependencies: js-tokens: 4.0.0 + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: true + /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: @@ -7231,6 +7729,12 @@ packages: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} dev: true + /magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -7940,6 +8444,11 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + /mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -7981,6 +8490,15 @@ packages: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: false + /mlly@1.6.1: + resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} + dependencies: + acorn: 8.11.3 + pathe: 1.1.2 + pkg-types: 1.1.0 + ufo: 1.5.3 + dev: true + /mrmime@2.0.0: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} engines: {node: '>=10'} @@ -8009,7 +8527,6 @@ packages: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - dev: false /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -8106,6 +8623,13 @@ packages: dependencies: path-key: 3.1.1 + /npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + /nprogress@0.2.0: resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} dev: false @@ -8163,6 +8687,13 @@ packages: dependencies: mimic-fn: 2.1.0 + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + /open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -8201,6 +8732,13 @@ packages: yocto-queue: 1.0.0 dev: false + /p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + dependencies: + yocto-queue: 1.0.0 + dev: true + /p-locate@3.0.0: resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} engines: {node: '>=6'} @@ -8362,6 +8900,11 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -8391,6 +8934,14 @@ packages: engines: {node: '>=8'} dev: false + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + /periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} dependencies: @@ -8436,6 +8987,14 @@ packages: find-up: 6.3.0 dev: false + /pkg-types@1.1.0: + resolution: {integrity: sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==} + dependencies: + confbox: 0.1.7 + mlly: 1.6.1 + pathe: 1.1.2 + dev: true + /pkg-up@3.1.0: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} @@ -8853,6 +9412,15 @@ packages: source-map-js: 1.0.2 dev: false + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + dev: true + /prettier@3.1.0: resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==} engines: {node: '>=14'} @@ -9478,6 +10046,32 @@ packages: glob: 7.2.3 dev: false + /rollup@4.15.0: + resolution: {integrity: sha512-i0ir57IMF5o7YvNYyUNeIGG+IZaaucnGZAOsSctO2tPLXlCEaZzyBa+QhpHNSgtpyLMoDev2DyN6a7J1dQA8Tw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.15.0 + '@rollup/rollup-android-arm64': 4.15.0 + '@rollup/rollup-darwin-arm64': 4.15.0 + '@rollup/rollup-darwin-x64': 4.15.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.15.0 + '@rollup/rollup-linux-arm-musleabihf': 4.15.0 + '@rollup/rollup-linux-arm64-gnu': 4.15.0 + '@rollup/rollup-linux-arm64-musl': 4.15.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.15.0 + '@rollup/rollup-linux-riscv64-gnu': 4.15.0 + '@rollup/rollup-linux-s390x-gnu': 4.15.0 + '@rollup/rollup-linux-x64-gnu': 4.15.0 + '@rollup/rollup-linux-x64-musl': 4.15.0 + '@rollup/rollup-win32-arm64-msvc': 4.15.0 + '@rollup/rollup-win32-ia32-msvc': 4.15.0 + '@rollup/rollup-win32-x64-msvc': 4.15.0 + fsevents: 2.3.3 + dev: true + /rtl-detect@1.1.2: resolution: {integrity: sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==} dev: false @@ -9764,9 +10358,18 @@ packages: get-intrinsic: 1.2.2 object-inspect: 1.13.1 + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + /simple-cbor@0.4.1: resolution: {integrity: sha512-rijcxtwx2b4Bje3sqeIqw5EeW7UlOIC4YfOdwqIKacpvRQ/D78bWg/4/0m5e0U91oKvlGh7LlJuZCu07ISCC7w==} @@ -9827,6 +10430,11 @@ packages: engines: {node: '>=0.10.0'} dev: false + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: true + /source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: @@ -9921,6 +10529,10 @@ packages: escape-string-regexp: 2.0.0 dev: true + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + /statuses@1.5.0: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} @@ -9933,7 +10545,6 @@ packages: /std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - dev: false /string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} @@ -10053,6 +10664,11 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + /strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -10062,6 +10678,12 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + /strip-literal@2.1.0: + resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + dependencies: + js-tokens: 9.0.0 + dev: true + /style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} dependencies: @@ -10192,6 +10814,20 @@ packages: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} dev: false + /tinybench@2.7.0: + resolution: {integrity: sha512-Qgayeb106x2o4hNzNjsZEfFziw8IbKqtbXBjVh7VIZfBxfD5M4gWtpyx5+YTae2gJ6Y6Dz/KLepiv16RFeQWNA==} + dev: true + + /tinypool@0.8.4: + resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + dev: true + /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} dev: true @@ -10391,6 +11027,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + /ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + dev: true + /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -10630,6 +11270,119 @@ packages: vfile-message: 4.0.2 dev: false + /vite-node@1.5.0(@types/node@20.11.5): + resolution: {integrity: sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.2.10(@types/node@20.11.5) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite@5.2.10(@types/node@20.11.5): + resolution: {integrity: sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.11.5 + esbuild: 0.20.2 + postcss: 8.4.38 + rollup: 4.15.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitest@1.5.0(@types/node@20.11.5): + resolution: {integrity: sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 1.5.0 + '@vitest/ui': 1.5.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/node': 20.11.5 + '@vitest/expect': 1.5.0 + '@vitest/runner': 1.5.0 + '@vitest/snapshot': 1.5.0 + '@vitest/spy': 1.5.0 + '@vitest/utils': 1.5.0 + acorn-walk: 8.3.2 + chai: 4.4.1 + debug: 4.3.4 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.10 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.1.0 + tinybench: 2.7.0 + tinypool: 0.8.4 + vite: 5.2.10(@types/node@20.11.5) + vite-node: 1.5.0(@types/node@20.11.5) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} dev: true @@ -10870,6 +11623,15 @@ packages: dependencies: isexe: 2.0.0 + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + /widest-line@4.0.1: resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} engines: {node: '>=12'} @@ -11002,7 +11764,6 @@ packages: /yocto-queue@1.0.0: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} - dev: false /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}