diff --git a/.changeset/quiet-vue-hearts.md b/.changeset/quiet-vue-hearts.md new file mode 100644 index 00000000..bbae68bf --- /dev/null +++ b/.changeset/quiet-vue-hearts.md @@ -0,0 +1,5 @@ +--- +"@tryabby/vue": minor +--- + +add Vue integration diff --git a/apps/docs/pages/integrations/_meta.json b/apps/docs/pages/integrations/_meta.json index 5cbf2999..1f8b2ad8 100644 --- a/apps/docs/pages/integrations/_meta.json +++ b/apps/docs/pages/integrations/_meta.json @@ -3,6 +3,7 @@ "react": "React", "remix": "Remix", "svelte": "Svelte", + "vue": "Vue", "angular": "Angular", "node": "Node", "express": "Express", diff --git a/apps/docs/pages/integrations/vue.mdx b/apps/docs/pages/integrations/vue.mdx new file mode 100644 index 00000000..db679076 --- /dev/null +++ b/apps/docs/pages/integrations/vue.mdx @@ -0,0 +1,205 @@ +import { Tab, Tabs, Callout } from "nextra-theme-docs"; + +# Vue + +Our Vue integration works with Vue 3.5 or later. It works with SPAs as well as server side rendered apps like [Nuxt](#nuxt). + +## Installation + +To get started make sure to install the package using your favorite package manager. + + + + ```bash + npm i @tryabby/vue + ``` + + + ```bash + yarn add @tryabby/vue + ``` + + + ```bash + pnpm i @tryabby/vue + ``` + + + +### Create your config + +To use Abby you need to create your config first. You can do this by creating a file called `abby.config.ts` in your `root` folder. This file will be used to configure your project. + +```ts +// abby.config.ts +import { defineConfig } from "@tryabby/vue"; + +export default defineConfig({ + projectId: "", + currentEnvironment: process.env.NODE_ENV, + environments: ["production", "development"], + tests: { + test: { variants: ["A", "B"] }, + footer: { variants: ["dark", "orange", "green"] }, + // ... your tests + }, + flags: ["darkMode", "newFeature"], + remoteConfig: { + customButtonText: "String", + }, +}); +``` + +### Create your Instance + +To use Abby in your code you will need to create typed composables and a plugin first. You can do this by using the `createAbby` function. +Please copy the id of your project from the dashboard to get started. + +```ts +// abby.ts +import { createAbby } from "@tryabby/vue"; +import abbyConfig from "../abby.config"; + +export const { AbbyPlugin, useAbby, useFeatureFlag, useRemoteConfig } = + createAbby(abbyConfig); +``` + +### Install the plugin + +The composables need the `AbbyPlugin` to be installed in your app. The plugin loads your project's data in the background. + +```ts +// main.ts +import { createApp } from "vue"; +import App from "./App.vue"; +import { AbbyPlugin } from "./abby"; + +createApp(App).use(AbbyPlugin).mount("#app"); +``` + +You can now import the composables created by `createAbby` and use them in your components. + +## Usage + +The composables return [computed refs](https://vuejs.org/api/reactivity-core.html#computed), so your components update automatically +when your data is loaded or when you change something in the [Devtools](/devtools). + +### useAbby + +The `useAbby` composable returns an object with the following values: + +- `variant` - A computed ref with the variant that is currently active +- `onAct` - A function that you can call to trigger any kind of interaction with the tested component (will be used to track conversions) + +```vue + + + +``` + +Optionally, you can pass a lookup object to automatically map the selected variant to a custom value. + +```vue + + + +``` + + + The variant is an empty string until the component is mounted and the project data is loaded. The server can't know which + variant a user is going to see, so the variant is selected in the browser, using the weights from your dashboard. + This way server side rendering won't cause any hydration mismatches. + + +### useFeatureFlag + +The `useFeatureFlag` composable returns a computed boolean that indicates if the flag is active or not. + +```vue + + + +``` + +### useRemoteConfig + +The `useRemoteConfig` composable returns a computed value as specified in your `abby.config.ts`. +The type of the returned value will automatically be inferred from your config. + +```vue + + + +``` + +### Devtools + +To use the [Devtools](/devtools) you need to install the `@tryabby/devtools` package and render the component created by `withDevtools`. +They are only shown in development unless you pass `dangerouslyForceShow: true`. + +```vue + + + +``` + +## Nuxt + +In Nuxt you install the plugin in a [Nuxt plugin](https://nuxt.com/docs/guide/directory-structure/plugins). If you load the data with `loadInitialData`, +your flags and remote config are already rendered on the server, and the client reuses the data from the payload instead of fetching it again. + +```ts +// plugins/abby.ts +import { AbbyPlugin, loadInitialData } from "~/abby"; + +export default defineNuxtPlugin(async (nuxtApp) => { + const { data } = await useAsyncData("abby", () => loadInitialData()); + nuxtApp.vueApp.use(AbbyPlugin, { initialData: data.value ?? undefined }); +}); +``` + +Make sure to also export `loadInitialData` from your `abby.ts`. diff --git a/packages/vue/.gitignore b/packages/vue/.gitignore new file mode 100644 index 00000000..53c37a16 --- /dev/null +++ b/packages/vue/.gitignore @@ -0,0 +1 @@ +dist \ No newline at end of file diff --git a/packages/vue/README.md b/packages/vue/README.md new file mode 100644 index 00000000..56dae148 --- /dev/null +++ b/packages/vue/README.md @@ -0,0 +1,48 @@ +# @tryabby/vue + +Vue integration for [Abby](https://tryabby.com): feature flags, remote config and A/B tests with full type safety. Works with Vue 3.5+ and Nuxt. + +## Installation + +```bash +npm i @tryabby/vue +``` + +## Usage + +```ts +// abby.ts +import { createAbby } from "@tryabby/vue"; + +export const { AbbyPlugin, useAbby, useFeatureFlag, useRemoteConfig } = + createAbby({ + projectId: "", + currentEnvironment: "production", + environments: ["production", "development"], + tests: { footer: { variants: ["dark", "orange", "green"] } }, + flags: ["newFeature"], + remoteConfig: { customButtonText: "String" }, + }); +``` + +```ts +// main.ts +createApp(App).use(AbbyPlugin).mount("#app"); +``` + +```vue + + + +``` + +All composables return computed refs. Check out the [documentation](https://docs.tryabby.com/integrations/vue) for the full API, the devtools and the Nuxt setup. diff --git a/packages/vue/package.json b/packages/vue/package.json new file mode 100644 index 00000000..93f5abb6 --- /dev/null +++ b/packages/vue/package.json @@ -0,0 +1,47 @@ +{ + "name": "@tryabby/vue", + "version": "0.0.0", + "description": "Vue integration for Abby: feature flags, remote config and A/B tests", + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": ["dist"], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "scripts": { + "build": "tsup src/", + "dev": "pnpm run build --watch", + "test": "tsc --noEmit && vitest" + }, + "homepage": "https://docs.tryabby.com", + "keywords": ["vue", "nuxt", "feature-flags", "ab-testing", "remote-config"], + "author": "", + "license": "ISC", + "devDependencies": { + "@tryabby/devtools": "workspace:*", + "@types/js-cookie": "^3.0.3", + "@vue/server-renderer": "^3.5.13", + "@vue/test-utils": "^2.4.6", + "jsdom": "^20.0.3", + "msw": "^0.49.1", + "node-fetch": "^3.3.0", + "tsconfig": "workspace:*", + "tsup": "^6.5.0", + "typescript": "5.5.4", + "vite": "5.4.0", + "vitest": "2.0.5", + "vue": "^3.5.13" + }, + "peerDependencies": { + "vue": "^3.5.0" + }, + "dependencies": { + "@tryabby/core": "workspace:*", + "js-cookie": "^3.0.1" + } +} diff --git a/packages/vue/src/StorageService.ts b/packages/vue/src/StorageService.ts new file mode 100644 index 00000000..7633105e --- /dev/null +++ b/packages/vue/src/StorageService.ts @@ -0,0 +1,76 @@ +import { + type IStorageService, + type StorageServiceOptions, + getABStorageKey, + getFFStorageKey, + getRCStorageKey, +} from "@tryabby/core"; +import Cookie from "js-cookie"; + +const DEFAULT_COOKIE_AGE = 365; + +class ABStorageService implements IStorageService { + get(projectId: string, testName: string): string | null { + const retrievedValue = Cookie.get(getABStorageKey(projectId, testName)); + if (!retrievedValue) return null; + + return retrievedValue; + } + + set( + projectId: string, + testName: string, + value: string, + options?: StorageServiceOptions + ): void { + Cookie.set(getABStorageKey(projectId, testName), value, { + expires: options?.expiresInDays + ? options.expiresInDays + : DEFAULT_COOKIE_AGE, + }); + } + + remove(projectId: string, testName: string): void { + Cookie.remove(getABStorageKey(projectId, testName)); + } +} + +class FFStorageService implements IStorageService { + get(projectId: string, flagName: string): string | null { + const retrievedValue = Cookie.get(getFFStorageKey(projectId, flagName)); + if (!retrievedValue) return null; + + return retrievedValue; + } + + set(projectId: string, flagName: string, value: string): void { + Cookie.set(getFFStorageKey(projectId, flagName), value, { + expires: DEFAULT_COOKIE_AGE, + }); + } + + remove(projectId: string, flagName: string): void { + Cookie.remove(getFFStorageKey(projectId, flagName)); + } +} + +class RCStorageService implements IStorageService { + get(projectId: string, key: string): string | null { + const retrievedValue = Cookie.get(getRCStorageKey(projectId, key)); + return retrievedValue ?? null; + } + + set(projectId: string, key: string, value: string): void { + Cookie.set(getRCStorageKey(projectId, key), value, { + expires: DEFAULT_COOKIE_AGE, + }); + } + + remove(projectId: string, key: string): void { + Cookie.remove(getRCStorageKey(projectId, key)); + } +} + +export const TestStorageService = new ABStorageService(); +export const FlagStorageService = new FFStorageService(); +export const RemoteConfigStorageService = new RCStorageService(); diff --git a/packages/vue/src/createAbby.ts b/packages/vue/src/createAbby.ts new file mode 100644 index 00000000..a5edd6d4 --- /dev/null +++ b/packages/vue/src/createAbby.ts @@ -0,0 +1,442 @@ +import { + type ABConfig, + Abby, + type AbbyConfig, + type AbbyDataResponse, + AbbyEventType, + HttpService, + type RemoteConfigValueString, + type RemoteConfigValueStringToType, + type ValidatorType, +} from "@tryabby/core"; +import type { Infer } from "@tryabby/core/validation"; +import type { AbbyDevtoolProps, DevtoolsFactory } from "@tryabby/devtools"; +import { + type App, + type Component, + type ComputedRef, + type InjectionKey, + type ShallowRef, + computed, + defineComponent, + hasInjectionContext, + inject, + onMounted, + onUnmounted, + shallowRef, + watch, +} from "vue"; +import { + FlagStorageService, + RemoteConfigStorageService, + TestStorageService, +} from "./StorageService"; + +export type ABTestReturnValue = Lookup extends undefined + ? TestVariant + : TestVariant extends keyof Lookup + ? Lookup[TestVariant] + : never; + +export type AbbyPluginOptions = { + /** + * Project data that was loaded beforehand, e.g. on the server with `loadInitialData()`. + * If it's not passed, the data is loaded in the browser once the plugin is installed. + */ + initialData?: AbbyDataResponse; +}; + +export type withDevtoolsFunction = ( + factory: DevtoolsFactory, + props?: Omit & { + dangerouslyForceShow?: boolean; + } +) => Component; + +export function createAbby< + const FlagName extends string, + const TestName extends string, + const Tests extends Record, + const RemoteConfig extends Record, + const RemoteConfigName extends Extract, + const User extends Record = Record< + string, + ValidatorType + >, +>( + abbyConfig: AbbyConfig< + FlagName, + Tests, + string[], + RemoteConfigName, + RemoteConfig, + User + > +) { + // we need to keep the config as a const so that the types are narrowed + const config = abbyConfig; + + const abby = new Abby< + FlagName, + TestName, + Tests, + RemoteConfig, + RemoteConfigName, + string[], + User + >( + abbyConfig, + { + get: (key: string) => { + if (typeof window === "undefined") return null; + return TestStorageService.get(config.projectId, key); + }, + set: (key: string, value: any) => { + if (typeof window === "undefined" || config.cookies?.disableByDefault) + return; + TestStorageService.set(config.projectId, key, value); + }, + }, + { + get: (key: string) => { + if (typeof window === "undefined") return null; + return FlagStorageService.get(config.projectId, key); + }, + set: (key: string, value: any) => { + if (typeof window === "undefined") return; + FlagStorageService.set(config.projectId, key, value); + }, + }, + { + get: (key: string) => { + if (typeof window === "undefined") return null; + return RemoteConfigStorageService.get(config.projectId, key); + }, + set: (key: string, value: any) => { + if (typeof window === "undefined") return; + RemoteConfigStorageService.set(config.projectId, key, value); + }, + } + ); + + type AbbyProjectData = ReturnType; + + const AbbyDataKey: InjectionKey> = Symbol("abby"); + + /** + * `getProjectData()` selects and stores a variant for every test. Before the + * weights are loaded from the API that would use equal weights and keep that + * variant forever, so the tests are left out until the data is loaded. + */ + const getDataWithoutTests = (): AbbyProjectData => ({ + tests: {} as AbbyProjectData["tests"], + flags: Object.fromEntries( + abby.getFeatureFlags().map(({ name, value }) => [name, { value }]) + ) as AbbyProjectData["flags"], + remoteConfig: Object.fromEntries( + abby + .getRemoteConfigVariables() + // the generic value type can't be narrowed here + .map(({ name, value }) => [name, { value: value as unknown }]) + ) as AbbyProjectData["remoteConfig"], + }); + + /** + * Vue plugin that makes the Abby data available to the composables. + * The state lives on the app (not in this module) so every app, and every + * request when rendering on the server, gets its own copy. + */ + const AbbyPlugin = { + install(app: App, options: AbbyPluginOptions = {}) { + // The core mutates its data in place and hands out the same nested objects, + // so the data is kept in a shallowRef and replaced with a fresh snapshot + // on every change. A deep ref would not notice the core's updates. + const data = shallowRef( + options.initialData + ? abby.init(options.initialData) + : getDataWithoutTests() + ); + app.provide(AbbyDataKey, data); + + // On the server the app is rendered once and never unmounted, so there + // is nothing to update and a subscription would leak on every request. + if (typeof window === "undefined") return; + + const unsubscribe = abby.subscribe((newData) => { + data.value = newData as AbbyProjectData; + }); + app.onUnmount(unsubscribe); + + if (!options.initialData) { + // `init()` notifies the subscribers before it applies the local overrides + // from the cookies, so the resolved data is set once more afterwards. + // If the data can't be loaded, the tests fall back to the configured variants. + abby.loadProjectData().then((loadedData) => { + data.value = loadedData ?? abby.getProjectData(); + }); + } + }, + }; + + const useAbbyData = () => { + const data = hasInjectionContext() ? inject(AbbyDataKey, null) : null; + + if (!data) { + throw new Error( + "Abby composables must be used inside the setup of a component in an app that uses the AbbyPlugin. Call app.use(AbbyPlugin) to fix this error." + ); + } + + return data; + }; + + const useAbby = < + K extends keyof Tests, + TestVariant extends Tests[K]["variants"][number], + LookupValue, + const Lookup extends + | Record + | undefined = undefined, + >( + name: K, + lookupObject?: Lookup + ): { + variant: ComputedRef>; + onAct: () => void; + } => { + const data = useAbbyData(); + + // The server can't know which variant a user gets (it's stored in a cookie), + // so like the React integration the variant stays empty until the component + // is mounted. This avoids hydration mismatches. + const isMounted = shallowRef(false); + onMounted(() => { + isMounted.value = true; + }); + + const selectedVariant = computed(() => + isMounted.value + ? (data.value.tests[name as unknown as TestName]?.selectedVariant ?? "") + : "" + ); + + // tell the server which variant was rendered, again whenever it changes + watch(selectedVariant, (variant) => { + if (!variant) return; + + HttpService.sendData({ + url: config.apiUrl, + type: AbbyEventType.PING, + data: { + projectId: config.projectId, + selectedVariant: variant, + testName: name as string, + }, + }); + }); + + const onAct = () => { + if (!selectedVariant.value) return; + + HttpService.sendData({ + url: config.apiUrl, + type: AbbyEventType.ACT, + data: { + projectId: config.projectId, + selectedVariant: selectedVariant.value, + testName: name as string, + }, + }); + }; + + const variant = computed(() => + lookupObject + ? lookupObject[selectedVariant.value as TestVariant] + : selectedVariant.value + ) as ComputedRef>; + + return { + variant, + /** + * This function can be called to indicate that something that + * uses the selected variant has been interacted with. + * It will automatically send the selected variant to the server. + */ + onAct, + }; + }; + + const useFeatureFlag = (name: FlagName): ComputedRef => { + const data = useAbbyData(); + return computed(() => data.value.flags[name].value); + }; + + /** + * Returns an Array of all flags with their name and value + */ + const useFeatureFlags = () => { + const data = useAbbyData(); + return computed(() => + (Object.keys(data.value.flags) as Array).map((flagName) => ({ + name: flagName, + value: data.value.flags[flagName].value, + })) + ); + }; + + const useRemoteConfig = < + T extends RemoteConfigName, + Config extends RemoteConfig[T], + >( + remoteConfigName: T + ): ComputedRef> => { + const data = useAbbyData(); + return computed( + () => + data.value.remoteConfig[remoteConfigName] + .value as RemoteConfigValueStringToType + ); + }; + + /** + * Returns an Array of all remote config variables with their name and value + */ + const useRemoteConfigVariables = () => { + const data = useAbbyData(); + return computed( + () => + (Object.keys(data.value.remoteConfig) as Array).map( + (configName) => ({ + name: configName, + value: data.value.remoteConfig[configName].value, + }) + ) as Array<{ + name: RemoteConfigName; + value: RemoteConfigValueStringToType; + }> + ); + }; + + /** + * Loads the project data from the Abby API, e.g. on the server to pass it + * as `initialData` to the plugin so the first render already has the right values + */ + const loadInitialData = () => + HttpService.getProjectData({ + projectId: config.projectId, + environment: config.currentEnvironment as string, + url: config.apiUrl, + fetch: config.fetch, + __experimentalCdnUrl: config.__experimentalCdnUrl + ? `${config.__experimentalCdnUrl}/${config.projectId}/${config.currentEnvironment}` + : undefined, + }); + + const getFeatureFlagValue = (name: FlagName) => { + return abby.getFeatureFlag(name); + }; + + const getRemoteConfig = < + T extends RemoteConfigName, + Config extends RemoteConfig[T], + >( + remoteConfigName: T + ): RemoteConfigValueStringToType => { + return abby.getRemoteConfig(remoteConfigName); + }; + + const getABTestValue = < + TestName extends keyof Tests, + TestVariant extends Tests[TestName]["variants"][number], + LookupValue, + const Lookup extends + | Record + | undefined = undefined, + >( + testName: TestName, + lookupObject?: Lookup + ): ABTestReturnValue => { + const variant = abby.getTestVariant(testName); + // Typescript looses its typing here, so we cast as any in favor of having + // better type inference for the user + if (lookupObject === undefined) { + return variant as any; + } + + return lookupObject[variant as TestVariant] as any; + }; + + /** + * Creates a component that renders the Abby devtools. + * They are only shown in development unless `dangerouslyForceShow` is set. + */ + const withDevtools: withDevtoolsFunction = (factory, props) => + defineComponent({ + name: "AbbyDevtools", + setup() { + let destroy: (() => void) | undefined; + + onMounted(() => { + if ( + !props?.dangerouslyForceShow && + process.env.NODE_ENV !== "development" + ) { + return; + } + destroy = factory.create({ ...props, abby }); + }); + + onUnmounted(() => { + destroy?.(); + }); + + return () => null; + }, + }); + + /** + * Simple helper function create a function that resets an AB test + * @param name the name of the test + * @returns A function that can be called to reset the test + */ + const getABResetFunction = (name: T) => { + return () => { + TestStorageService.remove(config.projectId, name as string); + }; + }; + + /** + * Simple helper function to get a list of all variants for a given test + * @param name the name of the test + * @returns an array of all variants + */ + const getVariants = (name: T) => { + return abby.getVariants(name); + }; + + const updateUserProperties = ( + user: Partial<{ + -readonly [K in keyof User]: Infer; + }> + ) => { + abby.updateUserProperties(user); + }; + + return { + AbbyPlugin, + useAbby, + useFeatureFlag, + useFeatureFlags, + useRemoteConfig, + useRemoteConfigVariables, + loadInitialData, + getFeatureFlagValue, + getRemoteConfig, + getABTestValue, + getABResetFunction, + getVariants, + updateUserProperties, + withDevtools, + __abby__: abby, + }; +} diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts new file mode 100644 index 00000000..a62b597a --- /dev/null +++ b/packages/vue/src/index.ts @@ -0,0 +1,7 @@ +export { + createAbby, + type ABTestReturnValue, + type AbbyPluginOptions, + type withDevtoolsFunction, +} from "./createAbby"; +export { type ABConfig, type AbbyConfig, defineConfig } from "@tryabby/core"; diff --git a/packages/vue/tests/mocks/handlers.ts b/packages/vue/tests/mocks/handlers.ts new file mode 100644 index 00000000..f2028cb7 --- /dev/null +++ b/packages/vue/tests/mocks/handlers.ts @@ -0,0 +1,42 @@ +import { ABBY_BASE_URL, type AbbyDataResponse } from "@tryabby/core"; +import { rest } from "msw"; + +const returnData: AbbyDataResponse = { + tests: [ + { + name: "test", + weights: [1, 1, 1, 1], + }, + { + name: "test2", + weights: [1, 0], + }, + ], + flags: [ + { + name: "flag1", + value: true, + }, + { + name: "flag2", + value: false, + }, + ], + remoteConfig: [ + { + name: "remoteConfig1", + value: "FooBar", + }, + ], +}; +export const handlers = [ + rest.get( + `${ABBY_BASE_URL}api/dashboard/:projectId/data`, + (_req, res, ctx) => { + return res(ctx.json(returnData)); + } + ), + rest.get(`${ABBY_BASE_URL}api/v2/data/:projectId`, (_req, res, ctx) => { + return res(ctx.json(returnData)); + }), +]; diff --git a/packages/vue/tests/mocks/server.ts b/packages/vue/tests/mocks/server.ts new file mode 100644 index 00000000..9bb8a870 --- /dev/null +++ b/packages/vue/tests/mocks/server.ts @@ -0,0 +1,5 @@ +import { setupServer } from "msw/node"; +import { handlers } from "./handlers"; + +// This configures a request mocking server with the given request handlers. +export const server = setupServer(...handlers); diff --git a/packages/vue/tests/setup.ts b/packages/vue/tests/setup.ts new file mode 100644 index 00000000..0066b826 --- /dev/null +++ b/packages/vue/tests/setup.ts @@ -0,0 +1,30 @@ +import { enableAutoUnmount } from "@vue/test-utils"; +import fetch from "node-fetch"; +import { afterAll, afterEach, beforeAll } from "vitest"; +import { server } from "./mocks/server"; + +/// @ts-ignore +global.fetch = fetch; + +// Establish API mocking before all tests. +beforeAll(() => server.listen()); + +// Clean up after the tests are finished. +afterAll(() => server.close()); + +// unmount every mounted app, which also runs the plugin's cleanup +enableAutoUnmount(afterEach); + +afterEach(() => { + // Reset any request handlers that we may add during the tests, + // so they don't affect other tests. + server.resetHandlers(); + vi.restoreAllMocks(); + // remove all cookies so tests don't share stored variants + if (typeof document === "undefined") return; + for (const cookie of document.cookie.split(";")) { + const name = cookie.split("=")[0]?.trim(); + if (name) + document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`; + } +}); diff --git a/packages/vue/tests/ssr.test.ts b/packages/vue/tests/ssr.test.ts new file mode 100644 index 00000000..acdaa1c2 --- /dev/null +++ b/packages/vue/tests/ssr.test.ts @@ -0,0 +1,71 @@ +// @vitest-environment node + +import { HttpService } from "@tryabby/core"; +import { renderToString } from "@vue/server-renderer"; +import { createSSRApp, defineComponent, h } from "vue"; +import { createAbby } from "../src"; +import { initialData } from "./utils"; + +const createTestAbby = () => + createAbby({ + projectId: "123", + environments: [""], + currentEnvironment: "", + tests: { test: { variants: ["A", "B", "C"] } }, + flags: ["flag1", "flag2"], + remoteConfig: { text: "String" }, + }); + +describe("server side rendering", () => { + it("renders flags and remote config but no variant", async () => { + const { AbbyPlugin, useAbby, useFeatureFlag, useRemoteConfig } = + createTestAbby(); + + const app = createSSRApp( + defineComponent({ + setup() { + const { variant } = useAbby("test"); + const flag1 = useFeatureFlag("flag1"); + const text = useRemoteConfig("text"); + return () => + h("div", [ + h("span", { id: "variant" }, variant.value), + h("span", { id: "flag" }, String(flag1.value)), + h("span", { id: "text" }, text.value), + ]); + }, + }) + ).use(AbbyPlugin, { initialData }); + + const html = await renderToString(app); + + // the server doesn't know the user's variant, it's selected after hydration + expect(html).toContain(''); + expect(html).toContain('true'); + expect(html).toContain('Hello'); + }); + + it("doesn't subscribe, load data or send events on the server", async () => { + const { AbbyPlugin, useAbby, __abby__ } = createTestAbby(); + const subscribeSpy = vi.spyOn(__abby__, "subscribe"); + const loadSpy = vi.spyOn(__abby__, "loadProjectData"); + const sendSpy = vi.spyOn(HttpService, "sendData"); + + for (const options of [{ initialData }, undefined]) { + const app = createSSRApp( + defineComponent({ + setup() { + const { variant } = useAbby("test"); + return () => h("p", variant.value); + }, + }) + ).use(AbbyPlugin, options); + + await renderToString(app); + } + + expect(subscribeSpy).not.toHaveBeenCalled(); + expect(loadSpy).not.toHaveBeenCalled(); + expect(sendSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/vue/tests/types.test-d.ts b/packages/vue/tests/types.test-d.ts new file mode 100644 index 00000000..e37fb840 --- /dev/null +++ b/packages/vue/tests/types.test-d.ts @@ -0,0 +1,97 @@ +import type { AbbyDataResponse } from "@tryabby/core"; +import { type ComputedRef, createApp } from "vue"; +import { createAbby } from "../src"; + +const { AbbyPlugin, useAbby, useFeatureFlag, useRemoteConfig, ...abby } = + createAbby({ + projectId: "123", + environments: ["production"], + currentEnvironment: "production", + tests: { + test: { variants: ["ONLY_ONE_VARIANT"] }, + test2: { variants: ["A", "B"] }, + }, + flags: ["flag1"], + remoteConfig: { + text: "String", + count: "Number", + json: "JSON", + }, + }); + +describe("useAbby", () => { + it("returns the variants as a computed ref", () => { + expectTypeOf(useAbby("test").variant).toEqualTypeOf< + ComputedRef<"ONLY_ONE_VARIANT"> + >(); + expectTypeOf(useAbby("test2").variant).toEqualTypeOf< + ComputedRef<"A" | "B"> + >(); + expectTypeOf(useAbby("test").onAct).toEqualTypeOf<() => void>(); + }); + + it("uses the types of the lookup object", () => { + expectTypeOf(useAbby("test2", { A: 1, B: 2 }).variant).toEqualTypeOf< + ComputedRef<1 | 2> + >(); + }); + + it("rejects unknown tests and incomplete lookup objects", () => { + // @ts-expect-error unknown test + useAbby("unknown"); + // @ts-expect-error variant B is missing + useAbby("test2", { A: 1 }); + }); +}); + +describe("useFeatureFlag", () => { + it("returns a boolean computed ref", () => { + expectTypeOf(useFeatureFlag("flag1")).toEqualTypeOf>(); + }); + + it("rejects unknown flags", () => { + // @ts-expect-error unknown flag + useFeatureFlag("unknown"); + }); +}); + +describe("useRemoteConfig", () => { + it("infers the type from the config", () => { + expectTypeOf(useRemoteConfig("text")).toEqualTypeOf>(); + expectTypeOf(useRemoteConfig("count")).toEqualTypeOf>(); + expectTypeOf(useRemoteConfig("json")).toEqualTypeOf< + ComputedRef> + >(); + }); + + it("rejects unknown remote config", () => { + // @ts-expect-error unknown remote config + useRemoteConfig("unknown"); + }); +}); + +describe("helpers", () => { + it("are typed like the composables", () => { + expectTypeOf(abby.getFeatureFlagValue("flag1")).toEqualTypeOf(); + expectTypeOf(abby.getRemoteConfig("count")).toEqualTypeOf(); + expectTypeOf(abby.getABTestValue("test2")).toEqualTypeOf<"A" | "B">(); + expectTypeOf( + abby.getABTestValue("test2", { A: "a", B: "b" }) + ).toEqualTypeOf<"a" | "b">(); + expectTypeOf(abby.useFeatureFlags().value).toEqualTypeOf< + Array<{ name: "flag1"; value: boolean }> + >(); + // @ts-expect-error unknown flag + abby.getFeatureFlagValue("unknown"); + }); +}); + +describe("AbbyPlugin", () => { + it("can be installed with and without initial data", () => { + const initialData = {} as AbbyDataResponse; + createApp({}).use(AbbyPlugin); + createApp({}).use(AbbyPlugin, { initialData }); + // @ts-expect-error wrong option type + createApp({}).use(AbbyPlugin, { initialData: "nope" }); + }); +}); diff --git a/packages/vue/tests/useAbby.test.ts b/packages/vue/tests/useAbby.test.ts new file mode 100644 index 00000000..60f8c972 --- /dev/null +++ b/packages/vue/tests/useAbby.test.ts @@ -0,0 +1,462 @@ +import { + ABBY_BASE_URL, + AbbyEventType, + HttpService, + getABStorageKey, + getFFStorageKey, +} from "@tryabby/core"; +import { mount } from "@vue/test-utils"; +import { rest } from "msw"; +import { defineComponent, h, nextTick, watch } from "vue"; +import { createAbby } from "../src"; +import { server } from "./mocks/server"; +import { initialData, setStoredVariant, withSetup } from "./utils"; + +const OLD_ENV = process.env; + +beforeEach(() => { + process.env = { ...OLD_ENV }; +}); + +afterAll(() => { + process.env = OLD_ENV; +}); + +const createTestAbby = () => + createAbby({ + projectId: "123", + environments: [""], + currentEnvironment: "", + tests: { test: { variants: ["A", "B", "C"] } }, + flags: ["flag1", "flag2"], + remoteConfig: { text: "String", count: "Number" }, + }); + +describe("useAbby", () => { + it("returns one of the variants once mounted", () => { + const { AbbyPlugin, useAbby } = createTestAbby(); + + const { result } = withSetup(() => useAbby("test"), AbbyPlugin, { + initialData, + }); + + expect(["A", "B", "C"]).toContain(result.variant.value); + }); + + it("uses the stored variant", () => { + setStoredVariant("123", "test", "B"); + const { AbbyPlugin, useAbby } = createTestAbby(); + + const { result } = withSetup(() => useAbby("test"), AbbyPlugin, { + initialData, + }); + + expect(result.variant.value).toBe("B"); + }); + + it("looks up the selected variant in the lookup object", () => { + setStoredVariant("123", "test", "C"); + const { AbbyPlugin, useAbby } = createTestAbby(); + + const { result } = withSetup( + () => useAbby("test", { A: "red", B: "green", C: "blue" }), + AbbyPlugin, + { initialData } + ); + + expect(result.variant.value).toBe("blue"); + }); + + it("renders the variant in a component", async () => { + setStoredVariant("123", "test", "A"); + const { AbbyPlugin, useAbby } = createTestAbby(); + + const wrapper = mount( + defineComponent({ + setup() { + const { variant } = useAbby("test"); + return () => h("p", variant.value); + }, + }), + { global: { plugins: [[AbbyPlugin, { initialData }]] } } + ); + await nextTick(); + + expect(wrapper.text()).toBe("A"); + }); + + it("pings the server with the selected variant after mount", async () => { + setStoredVariant("123", "test", "B"); + const spy = vi + .spyOn(HttpService, "sendData") + .mockImplementation(() => undefined); + const { AbbyPlugin, useAbby } = createTestAbby(); + + withSetup(() => useAbby("test"), AbbyPlugin, { initialData }); + await nextTick(); + + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ + type: AbbyEventType.PING, + data: { projectId: "123", selectedVariant: "B", testName: "test" }, + }) + ); + }); + + it("notifies the server with onAct", async () => { + setStoredVariant("123", "test", "C"); + const spy = vi + .spyOn(HttpService, "sendData") + .mockImplementation(() => undefined); + const { AbbyPlugin, useAbby } = createTestAbby(); + + const { result } = withSetup(() => useAbby("test"), AbbyPlugin, { + initialData, + }); + result.onAct(); + + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ + type: AbbyEventType.ACT, + data: { projectId: "123", selectedVariant: "C", testName: "test" }, + }) + ); + }); + + it("updates the variant and pings again when it changes", async () => { + setStoredVariant("123", "test", "A"); + const spy = vi + .spyOn(HttpService, "sendData") + .mockImplementation(() => undefined); + const { AbbyPlugin, useAbby, __abby__ } = createTestAbby(); + + const { result } = withSetup(() => useAbby("test"), AbbyPlugin, { + initialData, + }); + await nextTick(); + + // this is what the devtools do + __abby__.updateLocalVariant("test", "C"); + await nextTick(); + + expect(result.variant.value).toBe("C"); + expect(spy).toHaveBeenLastCalledWith( + expect.objectContaining({ + type: AbbyEventType.PING, + data: { projectId: "123", selectedVariant: "C", testName: "test" }, + }) + ); + }); + + it("uses the weights from the API for new visitors", async () => { + // a random value close to 1 picks the last variant when all weights are equal + vi.spyOn(window.crypto, "getRandomValues").mockImplementation((array) => { + (array as Uint32Array)[0] = 0xffffffff; + return array; + }); + const { AbbyPlugin, useAbby } = createAbby({ + projectId: "123", + environments: [""], + currentEnvironment: "", + tests: { test2: { variants: ["A", "B"] } }, + }); + + // the mocked API gives B a weight of 0 + const { result } = withSetup(() => useAbby("test2"), AbbyPlugin); + expect(result.variant.value).toBe(""); + + await vi.waitFor(() => expect(result.variant.value).not.toBe("")); + expect(result.variant.value).toBe("A"); + expect(document.cookie).toContain(`${getABStorageKey("123", "test2")}=A`); + }); + + it("still selects a variant if the data can't be loaded", async () => { + server.use( + rest.get(`${ABBY_BASE_URL}api/v2/data/:projectId`, (_req, res, ctx) => + res(ctx.status(500)) + ) + ); + const { AbbyPlugin, useAbby } = createTestAbby(); + + const { result } = withSetup(() => useAbby("test"), AbbyPlugin); + + await vi.waitFor(() => + expect(["A", "B", "C"]).toContain(result.variant.value) + ); + }); + + it("throws a helpful error without the plugin", () => { + const { useAbby } = createTestAbby(); + + expect(() => useAbby("test")).toThrowError(/app\.use\(AbbyPlugin\)/); + expect(() => + mount( + defineComponent({ + setup() { + useAbby("test"); + return () => null; + }, + }) + ) + ).toThrowError(/app\.use\(AbbyPlugin\)/); + }); +}); + +describe("useFeatureFlag", () => { + it("returns the flag values", () => { + const { AbbyPlugin, useFeatureFlag } = createTestAbby(); + + const { result } = withSetup( + () => ({ + flag1: useFeatureFlag("flag1"), + flag2: useFeatureFlag("flag2"), + }), + AbbyPlugin, + { initialData } + ); + + expect(result.flag1.value).toBe(true); + expect(result.flag2.value).toBe(false); + }); + + it("loads the data from the API without initialData", async () => { + const { AbbyPlugin, useFeatureFlag } = createTestAbby(); + + const { result } = withSetup(() => useFeatureFlag("flag1"), AbbyPlugin); + expect(result.value).toBe(false); + + await vi.waitFor(() => expect(result.value).toBe(true)); + }); + + it("uses the devOverrides in development", () => { + process.env.NODE_ENV = "development"; + const { AbbyPlugin, useFeatureFlag } = createAbby({ + projectId: "123", + environments: [""], + currentEnvironment: "", + flags: ["flag1"], + settings: { flags: { devOverrides: { flag1: false } } }, + }); + + const { result } = withSetup(() => useFeatureFlag("flag1"), AbbyPlugin, { + initialData, + }); + + expect(result.value).toBe(false); + }); + + it("re-renders when a flag changes", async () => { + const { AbbyPlugin, useFeatureFlag, __abby__ } = createTestAbby(); + + const wrapper = mount( + defineComponent({ + setup() { + const flag1 = useFeatureFlag("flag1"); + return () => h("p", flag1.value ? "on" : "off"); + }, + }), + { global: { plugins: [[AbbyPlugin, { initialData }]] } } + ); + expect(wrapper.text()).toBe("on"); + + // this is what the devtools do + __abby__.updateFlag("flag1", false); + await nextTick(); + + expect(wrapper.text()).toBe("off"); + }); + + it("returns all flags with useFeatureFlags", async () => { + const { AbbyPlugin, useFeatureFlags, __abby__ } = createTestAbby(); + + const { result } = withSetup(() => useFeatureFlags(), AbbyPlugin, { + initialData, + }); + + expect(result.value).toEqual([ + { name: "flag1", value: true }, + { name: "flag2", value: false }, + ]); + + __abby__.updateFlag("flag2", true); + await nextTick(); + + expect(result.value).toEqual([ + { name: "flag1", value: true }, + { name: "flag2", value: true }, + ]); + }); +}); + +describe("useRemoteConfig", () => { + it("returns the remote config values", () => { + const { AbbyPlugin, useRemoteConfig } = createTestAbby(); + + const { result } = withSetup( + () => ({ + text: useRemoteConfig("text"), + count: useRemoteConfig("count"), + }), + AbbyPlugin, + { initialData } + ); + + expect(result.text.value).toBe("Hello"); + expect(result.count.value).toBe(42); + }); + + it("uses the default values until the data is loaded", () => { + const { AbbyPlugin, useRemoteConfig } = createAbby({ + projectId: "123", + environments: [""], + currentEnvironment: "", + remoteConfig: { text: "String" }, + settings: { remoteConfig: { defaultValues: { String: "Default" } } }, + }); + + const { result } = withSetup(() => useRemoteConfig("text"), AbbyPlugin); + + expect(result.value).toBe("Default"); + }); + + it("uses the devOverrides in development", () => { + process.env.NODE_ENV = "development"; + const { AbbyPlugin, useRemoteConfig } = createAbby({ + projectId: "123", + environments: [""], + currentEnvironment: "", + remoteConfig: { text: "String" }, + settings: { remoteConfig: { devOverrides: { text: "Overridden" } } }, + }); + + const { result } = withSetup(() => useRemoteConfig("text"), AbbyPlugin, { + initialData, + }); + + expect(result.value).toBe("Overridden"); + }); + + it("updates when a value changes", async () => { + const { AbbyPlugin, useRemoteConfig, __abby__ } = createTestAbby(); + + const { result } = withSetup(() => useRemoteConfig("text"), AbbyPlugin, { + initialData, + }); + + __abby__.updateRemoteConfig("text", "Changed"); + await nextTick(); + + expect(result.value).toBe("Changed"); + }); + + it("returns all variables with useRemoteConfigVariables", () => { + const { AbbyPlugin, useRemoteConfigVariables } = createTestAbby(); + + const { result } = withSetup(() => useRemoteConfigVariables(), AbbyPlugin, { + initialData, + }); + + expect(result.value).toEqual([ + { name: "text", value: "Hello" }, + { name: "count", value: 42 }, + ]); + }); +}); + +describe("AbbyPlugin", () => { + it("unsubscribes from the core when the app is unmounted", () => { + const { AbbyPlugin, useFeatureFlag, __abby__ } = createTestAbby(); + const unsubscribe = vi.fn(); + vi.spyOn(__abby__, "subscribe").mockReturnValue(unsubscribe); + + const { wrapper } = withSetup(() => useFeatureFlag("flag1"), AbbyPlugin, { + initialData, + }); + expect(__abby__.subscribe).toHaveBeenCalledTimes(1); + + wrapper.unmount(); + + expect(unsubscribe).toHaveBeenCalledTimes(1); + }); + + it("applies the cookie overrides once the data is loaded", async () => { + // the API returns `true` for flag1, the override (e.g. from the devtools) says `false` + document.cookie = `${getFFStorageKey("123", "flag1")}=false`; + const { AbbyPlugin, useFeatureFlag, __abby__ } = createTestAbby(); + const loadSpy = vi.spyOn(__abby__, "loadProjectData"); + + const { result } = withSetup(() => { + const flag1 = useFeatureFlag("flag1"); + // a sync watcher reads the value at the moment the core notifies, + // which is before the overrides are applied + watch(flag1, () => {}, { flush: "sync" }); + return flag1; + }, AbbyPlugin); + await vi.waitFor(() => expect(loadSpy).toHaveBeenCalledTimes(1)); + await loadSpy.mock.results[0]?.value; + + expect(result.value).toBe(false); + }); +}); + +describe("helpers", () => { + it("returns the values without a component", () => { + const { + __abby__, + getFeatureFlagValue, + getRemoteConfig, + getABTestValue, + getVariants, + } = createTestAbby(); + __abby__.init(initialData); + + expect(getFeatureFlagValue("flag1")).toBe(true); + expect(getRemoteConfig("text")).toBe("Hello"); + expect(getVariants("test")).toEqual(["A", "B", "C"]); + expect(["A", "B", "C"]).toContain(getABTestValue("test")); + expect(["a", "b", "c"]).toContain( + getABTestValue("test", { A: "a", B: "b", C: "c" }) + ); + }); + + it("resets a test with getABResetFunction", () => { + setStoredVariant("123", "test", "B"); + const { getABResetFunction } = createTestAbby(); + + getABResetFunction("test")(); + + expect(document.cookie).not.toContain("_test=B"); + }); +}); + +describe("withDevtools", () => { + const createFactory = () => { + const destroy = vi.fn(); + const factory = { create: vi.fn(() => destroy) }; + return { factory, destroy }; + }; + + it("creates the devtools on mount and destroys them on unmount", () => { + const { withDevtools } = createTestAbby(); + const { factory, destroy } = createFactory(); + + const wrapper = mount( + withDevtools(factory as any, { dangerouslyForceShow: true }) + ); + expect(factory.create).toHaveBeenCalledTimes(1); + + wrapper.unmount(); + expect(destroy).toHaveBeenCalledTimes(1); + }); + + it("does not show the devtools outside of development", () => { + process.env.NODE_ENV = "production"; + const { withDevtools } = createTestAbby(); + const { factory } = createFactory(); + + mount(withDevtools(factory as any)); + + expect(factory.create).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/vue/tests/utils.ts b/packages/vue/tests/utils.ts new file mode 100644 index 00000000..5bee329d --- /dev/null +++ b/packages/vue/tests/utils.ts @@ -0,0 +1,47 @@ +import { type AbbyDataResponse, getABStorageKey } from "@tryabby/core"; +import { mount } from "@vue/test-utils"; +import { type Plugin, defineComponent } from "vue"; + +export const initialData: AbbyDataResponse = { + tests: [{ name: "test", weights: [1, 0, 0] }], + flags: [ + { name: "flag1", value: true }, + { name: "flag2", value: false }, + ], + remoteConfig: [ + { name: "text", value: "Hello" }, + { name: "count", value: 42 }, + ], +}; + +/** + * Runs a composable inside the setup of a component that is mounted + * in an app with the given plugin installed. + */ +export function withSetup( + composable: () => T, + plugin: Plugin<[options?: { initialData?: AbbyDataResponse }]>, + options?: { initialData?: AbbyDataResponse } +) { + let result!: T; + + const wrapper = mount( + defineComponent({ + setup() { + result = composable(); + return () => null; + }, + }), + { global: { plugins: [[plugin, options]] } } + ); + + return { result, wrapper }; +} + +export function setStoredVariant( + projectId: string, + testName: string, + variant: string +) { + document.cookie = `${getABStorageKey(projectId, testName)}=${variant}`; +} diff --git a/packages/vue/tsconfig.json b/packages/vue/tsconfig.json new file mode 100644 index 00000000..7015f100 --- /dev/null +++ b/packages/vue/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "lib": ["ESNext", "DOM"], + "module": "ESNext", + "moduleResolution": "bundler", + "target": "es6", + "types": ["vitest/globals"] + }, + "include": ["src", "tests"] +} diff --git a/packages/vue/tsup.config.ts b/packages/vue/tsup.config.ts new file mode 100644 index 00000000..bf89044f --- /dev/null +++ b/packages/vue/tsup.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + dts: true, + clean: true, + format: ["cjs", "esm"], + sourcemap: true, + treeshake: true, +}); diff --git a/packages/vue/vite.config.ts b/packages/vue/vite.config.ts new file mode 100644 index 00000000..c5e5a986 --- /dev/null +++ b/packages/vue/vite.config.ts @@ -0,0 +1,13 @@ +/// + +import { defineConfig } from "vite"; + +export default defineConfig({ + test: { + globals: true, + environment: "jsdom", + setupFiles: "./tests/setup.ts", + // the type tests in *.test-d.ts are checked by `tsc`, see the test script + include: ["tests/**/*.test.ts"], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69fa896e..fc59fd25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -327,7 +327,7 @@ importers: version: 3.1.55(@next/env@14.2.4)(next@14.1.1(@babel/core@7.24.7)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8)) next-themes: specifier: ^0.2.1 - version: 0.2.1(next@14.1.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 0.2.1(next@14.1.1(@babel/core@7.24.7)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) nextjs-cors: specifier: ^2.1.2 version: 2.1.2(next@14.1.1(@babel/core@7.24.7)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8)) @@ -620,7 +620,7 @@ importers: version: 2.4.2 tsup: specifier: ^6.5.0 - version: 6.7.0(postcss@8.4.41)(ts-node@10.9.1(@types/node@20.3.1)(typescript@5.5.4))(typescript@5.5.4) + version: 6.7.0(postcss@8.5.32)(ts-node@10.9.1(@types/node@20.3.1)(typescript@5.5.4))(typescript@5.5.4) unconfig: specifier: ^0.3.10 version: 0.3.10 @@ -681,7 +681,7 @@ importers: version: link:../tsconfig tsup: specifier: ^6.5.0 - version: 6.7.0(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) + version: 6.7.0(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) typescript: specifier: 5.5.4 version: 5.5.4 @@ -757,7 +757,7 @@ importers: version: 3.59.1 svelte-check: specifier: ^2.10.3 - version: 2.10.3(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.4.41)(sass@1.77.8)(svelte@3.59.1) + version: 2.10.3(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.32)(sass@1.77.8)(svelte@3.59.1) tslib: specifier: ^2.5.0 version: 2.5.3 @@ -827,7 +827,7 @@ importers: version: link:../tsconfig tsup: specifier: ^6.5.0 - version: 6.7.0(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) + version: 6.7.0(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) typescript: specifier: 5.5.4 version: 5.5.4 @@ -876,7 +876,7 @@ importers: version: link:../tsconfig tsup: specifier: ^6.5.0 - version: 6.7.0(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) + version: 6.7.0(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) typescript: specifier: 5.5.4 version: 5.5.4 @@ -940,7 +940,7 @@ importers: version: link:../tsconfig tsup: specifier: ^6.5.0 - version: 6.7.0(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) + version: 6.7.0(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) typescript: specifier: 5.5.4 version: 5.5.4 @@ -1004,7 +1004,7 @@ importers: version: link:../tsconfig tsup: specifier: ^6.5.0 - version: 6.7.0(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) + version: 6.7.0(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) typescript: specifier: 5.5.4 version: 5.5.4 @@ -1089,7 +1089,7 @@ importers: version: 3.59.1 svelte-check: specifier: ^2.10.3 - version: 2.10.3(@babel/core@7.24.9)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.4.41)(sass@1.77.8)(svelte@3.59.1) + version: 2.10.3(@babel/core@7.24.9)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.32)(sass@1.77.8)(svelte@3.59.1) tslib: specifier: ^2.5.0 version: 2.5.3 @@ -1108,6 +1108,55 @@ importers: packages/tsconfig: {} + packages/vue: + dependencies: + '@tryabby/core': + specifier: workspace:* + version: link:../core + js-cookie: + specifier: ^3.0.1 + version: 3.0.5 + devDependencies: + '@tryabby/devtools': + specifier: workspace:* + version: link:../devtools + '@types/js-cookie': + specifier: ^3.0.3 + version: 3.0.3 + '@vue/server-renderer': + specifier: ^3.5.13 + version: 3.5.44 + '@vue/test-utils': + specifier: ^2.4.6 + version: 2.5.1(@vue/compiler-dom@3.5.44)(@vue/server-renderer@3.5.44)(vue@3.5.44(typescript@5.5.4)) + jsdom: + specifier: ^20.0.3 + version: 20.0.3 + msw: + specifier: ^0.49.1 + version: 0.49.3(encoding@0.1.13)(typescript@5.5.4) + node-fetch: + specifier: ^3.3.0 + version: 3.3.2 + tsconfig: + specifier: workspace:* + version: link:../tsconfig + tsup: + specifier: ^6.5.0 + version: 6.7.0(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4) + typescript: + specifier: 5.5.4 + version: 5.5.4 + vite: + specifier: 5.4.0 + version: 5.4.0(@types/node@22.1.0)(less@4.2.0)(sass@1.77.8)(terser@5.31.1) + vitest: + specifier: 2.0.5 + version: 2.0.5(@types/node@22.1.0)(jsdom@20.0.3)(less@4.2.0)(sass@1.77.8)(terser@5.31.1) + vue: + specifier: ^3.5.13 + version: 3.5.44(typescript@5.5.4) + packages: '@adobe/css-tools@4.2.0': @@ -1582,6 +1631,10 @@ packages: resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.11': + resolution: {integrity: sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.22.20': resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} @@ -1590,6 +1643,10 @@ packages: resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.22.5': resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} engines: {node: '>=6.9.0'} @@ -1656,11 +1713,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@7.24.4': - resolution: {integrity: sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/parser@7.24.7': resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} engines: {node: '>=6.0.0'} @@ -1671,6 +1723,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.11': + resolution: {integrity: sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.3': resolution: {integrity: sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==} engines: {node: '>=6.9.0'} @@ -2673,6 +2730,10 @@ packages: resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.11': + resolution: {integrity: sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -2892,7 +2953,7 @@ packages: '@emotion/use-insertion-effect-with-fallbacks@1.0.1': resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==} peerDependencies: - react: ^18.2.0 + react: '>=16.8.0' '@esbuild-plugins/node-globals-polyfill@0.2.3': resolution: {integrity: sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==} @@ -3750,6 +3811,9 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/trace-mapping@0.3.18': resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} @@ -3845,7 +3909,7 @@ packages: '@mdx-js/react@2.3.0': resolution: {integrity: sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==} peerDependencies: - react: ^18.2.0 + react: '>=16' '@mdx-js/react@3.0.0': resolution: {integrity: sha512-nDctevR9KyYFyV+m+/+S4cpzCWHqj+iHDHq3QrsWezcC+B17uZdIWgCguESUkwFhM3n/56KxWVE3V6EokrmONQ==} @@ -4295,6 +4359,9 @@ packages: resolution: {integrity: sha512-TUkJLtI163Bz5+JK0O+zDkQpn4gKwN+BovclUvCj6pI/6RXrFqQvUMRS2M+Rt8Rv0qR3wjoMoOPmpJKeOh0nBg==} engines: {node: '>= 18'} + '@one-ini/wasm@0.2.1': + resolution: {integrity: sha512-TUqERXGNTifZ9y2g3wPxQrw3HpHv/02DsW3D90T9x0hhonrL1ZqpSmNrU2XkoIq0fP1N6gZfVQzy2Fw1ZvGBNg==} + '@open-draft/until@1.0.3': resolution: {integrity: sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==} @@ -4671,8 +4738,8 @@ packages: '@radix-ui/react-direction@1.1.0': resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} peerDependencies: - '@types/react': 18.0.26 - react: ^18.2.0 + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -5161,8 +5228,8 @@ packages: '@radix-ui/react-use-layout-effect@1.1.0': resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} peerDependencies: - '@types/react': 18.0.26 - react: ^18.2.0 + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -6690,9 +6757,21 @@ packages: '@vue/compiler-core@3.4.24': resolution: {integrity: sha512-vbW/tgbwJYj62N/Ww99x0zhFTkZDTcGh3uwJEuadZ/nF9/xuFMC4693P9r+3sxGXISABpDKvffY5ApH9pmdd1A==} + '@vue/compiler-core@3.5.44': + resolution: {integrity: sha512-k34nCytbK8PQ1gR2mGROYOw6Pnv/mumqIdrZ3x2tbyXZDWm7ViKGLTfdB+DRrWqEQsLgGJvn0WN9jBif5zCTLQ==} + '@vue/compiler-dom@3.4.24': resolution: {integrity: sha512-4XgABML/4cNndVsQndG6BbGN7+EoisDwi3oXNovqL/4jdNhwvP8/rfRMTb6FxkxIxUUtg6AI1/qZvwfSjxJiWA==} + '@vue/compiler-dom@3.5.44': + resolution: {integrity: sha512-W5AoBFWdw1lQAKiHowkfgFKFab82oSRT6sGsPfw/q1GRjsy/ZJFLHGhJyBRIFN9APLTUduDH9U2JAiPOIyKuYg==} + + '@vue/compiler-sfc@3.5.44': + resolution: {integrity: sha512-dzHmQ4jXTpdr5FjIQUeykVakN/uzqFbUo9/C5ikBFg3a3PeM6uRxeczx1ZbXBML0/zJstEh3ygYipzR5Z7b2Pw==} + + '@vue/compiler-ssr@3.5.44': + resolution: {integrity: sha512-xr5gb1JVHlfXJwSlRz1FeP6uxoazdzHuMTVLE7up+4Vrqxk219CvxLPxD9NXBt47yvZW7+p4DXfyp4O1fkByIA==} + '@vue/language-core@1.8.27': resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} peerDependencies: @@ -6701,9 +6780,34 @@ packages: typescript: optional: true + '@vue/reactivity@3.5.44': + resolution: {integrity: sha512-u+QJdKeBBEADkPdevsL+6E12QlYVfBD8Zcw5qjDQKtamI6l14KyqazJMobhCKelhWN5zpyKUiEDoTyHE93cqWw==} + + '@vue/runtime-core@3.5.44': + resolution: {integrity: sha512-uEa/ndwjhrrhnOfb2p+sZy6zeoamJOISqaLSJGk/8R6nABT1ZldpzcJoPzKj+zHVomVBPcmQYyC2r4LtvDV6CQ==} + + '@vue/runtime-dom@3.5.44': + resolution: {integrity: sha512-3fc4ARcXVhVBaJ3ujWiNZJARD6pkyRcwG+ZYhMzjKtKUp7MAY0HLwr7r3Smep3Li6ZaWT6ewdmx8XqdaD5oNZg==} + + '@vue/server-renderer@3.5.44': + resolution: {integrity: sha512-R4S1UoH4h5Frrauv8Z5XE5TxV9oBiUSgWIC4TyVK1FK+RocRnOLwFA5Xcmo2011RlgJp+AbZCnw6afrhlgRuUQ==} + '@vue/shared@3.4.24': resolution: {integrity: sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw==} + '@vue/shared@3.5.44': + resolution: {integrity: sha512-H82XLH0DjmSOdbEwO15x3QTo1f5PWEtv237eLpYKc0mqLu+B8hnq16lFWmuBmNurkeiG3wr+oM1FhOll/YVFRg==} + + '@vue/test-utils@2.5.1': + resolution: {integrity: sha512-V/4a99odJ7hapr0bRt35XxljGk8hefq31gSOnlKTcEq2fUeoj2qb+FcpJYfQu4qfYhW9Ug+O2+CZ3QmZ7gXDzA==} + peerDependencies: + '@vue/compiler-dom': 3.x + '@vue/server-renderer': 3.x + vue: 3.x + peerDependenciesMeta: + '@vue/server-renderer': + optional: true + '@web3-storage/multipart-parser@1.0.0': resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==} @@ -6786,6 +6890,10 @@ packages: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + abbrev@5.0.0: + resolution: {integrity: sha512-/XrFJgzQQQHpti1raDJC6m4ws6aNktmjBlhk8Fdlk7LwCEuDoieEJJY9OFHjfiFJFFRM2tK+Ky/IsfbbmlMu1w==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -7141,6 +7249,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -7206,6 +7318,10 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} + braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -7576,6 +7692,10 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -7796,6 +7916,9 @@ packages: csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + csv-generate@3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} @@ -8276,6 +8399,11 @@ packages: resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==} hasBin: true + editorconfig@3.0.2: + resolution: {integrity: sha512-T0ix8GhtxyKVfUFEcvdNDt3YGqlwkFHbD4/5bgFUDgFmxhI/cSRAeJ87/Sz//Cq8Eam6JX/e23RkoFO71P7aAA==} + engines: {node: '>=20'} + hasBin: true + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -8345,6 +8473,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -8994,6 +9126,10 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} deprecated: Glob versions prior to v9 are no longer supported @@ -9882,10 +10018,18 @@ packages: engines: {node: '>=12'} hasBin: true + js-beautify@2.0.3: + resolution: {integrity: sha512-cyFbh3tkPhknnTD/0bLf0T0yy2ZIbqL05mttzbt4y1Zfr7NxqXQZ62dkBLKs3oHH/lpjmDRAnciJiSUyOy8XwQ==} + engines: {node: '>=14'} + hasBin: true + js-cookie@3.0.5: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} + js-cookie@3.0.8: + resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==} + js-levenshtein@1.1.6: resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} engines: {node: '>=0.10.0'} @@ -10220,6 +10364,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.5.3: + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} + engines: {node: 20 || >=22} + lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -10261,6 +10409,9 @@ packages: magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@0.30.8: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} @@ -10713,6 +10864,10 @@ packages: minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + minimatch@3.0.8: resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} @@ -10774,6 +10929,10 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + minizlib@2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} @@ -10884,6 +11043,11 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.20: + resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -11131,6 +11295,11 @@ packages: resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} hasBin: true + nopt@10.0.1: + resolution: {integrity: sha512-df3sBr/6ax9hSGuC3CspvLlbnX8cP5L5nZwXF8cGN8l0zSWR6BvzmQ6jPUKjvo6+/xdpkNvEcucBNUdBeeV13g==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} + hasBin: true + nopt@6.0.0: resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -11516,6 +11685,10 @@ packages: resolution: {integrity: sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==} engines: {node: '>=16 || 14 >=14.17'} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} @@ -11569,6 +11742,9 @@ packages: picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -11735,6 +11911,10 @@ packages: resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.32: + resolution: {integrity: sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==} + engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -12414,11 +12594,6 @@ packages: rollup-pluginutils@2.8.2: resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} - rollup@3.25.0: - resolution: {integrity: sha512-FnJkNRst2jEZGw7f+v4hFo6UTzpDKrAKcHZWcEfm5/GJQ5CK7wgb4moNLNAe7npKUev7yQn1AY/YbZRIxOv6Qg==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} - hasBin: true - rollup@3.27.1: resolution: {integrity: sha512-tXNDFwOkN6C2w5Blj1g6ForKeFw6c1mDu5jxoeDO3/pmYjgt+8yvIFjKzH5FQUq70OKZBkOt0zzv0THXL7vwzQ==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -12587,6 +12762,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} @@ -12771,6 +12951,10 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} + source-map-loader@5.0.0: resolution: {integrity: sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==} engines: {node: '>= 18.12.0'} @@ -12791,6 +12975,7 @@ packages: source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions sourcemap-codec@1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} @@ -13964,6 +14149,9 @@ packages: vscode-textmate@8.0.0: resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} + vue-component-type-helpers@3.3.13: + resolution: {integrity: sha512-02KCxSmKDhIITjKyJ4QT5csP6L0WoRborLqY/34a40+S9uvMvdNgMV0yVjO3p2Gkz3ZoPmFz/0JsGHacwJYTxw==} + vue-template-compiler@2.7.16: resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} @@ -13973,6 +14161,14 @@ packages: peerDependencies: typescript: '*' + vue@3.5.44: + resolution: {integrity: sha512-dvvmaOCG7lAMkJTPqPwWeGMsPS6DIsFsfZDUgBp9V6mHf9b3+/wyVwYR4Es+WV6uJC0FKRxp81Obcq0ZwUcGFQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} @@ -14046,7 +14242,7 @@ packages: engines: {node: '>= 18.12.0'} hasBin: true peerDependencies: - webpack: 5.93.0 + webpack: ^5.0.0 webpack-cli: '*' peerDependenciesMeta: webpack: @@ -14099,6 +14295,7 @@ packages: whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-fetch@3.6.2: resolution: {integrity: sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==} @@ -14708,7 +14905,7 @@ snapshots: '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.21.8) '@babel/helpers': 7.24.4 - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@babel/template': 7.24.0 '@babel/traverse': 7.24.1 '@babel/types': 7.24.7 @@ -14748,7 +14945,7 @@ snapshots: '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.4) '@babel/helpers': 7.24.4 - '@babel/parser': 7.24.4 + '@babel/parser': 7.25.3 '@babel/template': 7.24.0 '@babel/traverse': 7.24.1 '@babel/types': 7.24.0 @@ -14816,7 +15013,7 @@ snapshots: '@babel/generator@7.24.4': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 @@ -14845,7 +15042,7 @@ snapshots: '@babel/helper-builder-binary-assignment-operator-visitor@7.22.5': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': dependencies: @@ -15008,7 +15205,7 @@ snapshots: '@babel/helper-function-name@7.23.0': dependencies: '@babel/template': 7.24.7 - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-function-name@7.24.7': dependencies: @@ -15017,7 +15214,7 @@ snapshots: '@babel/helper-hoist-variables@7.22.5': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-hoist-variables@7.24.7': dependencies: @@ -15025,7 +15222,7 @@ snapshots: '@babel/helper-member-expression-to-functions@7.22.5': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-member-expression-to-functions@7.24.8': dependencies: @@ -15142,7 +15339,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.22.5': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-optimise-call-expression@7.24.7': dependencies: @@ -15160,7 +15357,7 @@ snapshots: '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-wrap-function': 7.22.5 - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color @@ -15170,7 +15367,7 @@ snapshots: '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-wrap-function': 7.22.5 - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color @@ -15190,7 +15387,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.22.5 '@babel/template': 7.24.7 '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color @@ -15216,7 +15413,7 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers@7.22.5': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-skip-transparent-expression-wrappers@7.24.7': dependencies: @@ -15239,10 +15436,14 @@ snapshots: '@babel/helper-string-parser@7.24.8': {} + '@babel/helper-string-parser@7.29.11': {} + '@babel/helper-validator-identifier@7.22.20': {} '@babel/helper-validator-identifier@7.24.7': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.22.5': {} '@babel/helper-validator-option@7.23.5': {} @@ -15256,7 +15457,7 @@ snapshots: '@babel/helper-function-name': 7.23.0 '@babel/template': 7.22.15 '@babel/traverse': 7.23.6 - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color @@ -15280,7 +15481,7 @@ snapshots: dependencies: '@babel/template': 7.24.7 '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color @@ -15328,10 +15529,6 @@ snapshots: dependencies: '@babel/types': 7.23.6 - '@babel/parser@7.24.4': - dependencies: - '@babel/types': 7.24.7 - '@babel/parser@7.24.7': dependencies: '@babel/types': 7.24.7 @@ -15340,6 +15537,10 @@ snapshots: dependencies: '@babel/types': 7.25.2 + '@babel/parser@7.29.11': + dependencies: + '@babel/types': 7.29.11 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.3(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 @@ -17057,14 +17258,14 @@ snapshots: '@babel/template@7.22.15': dependencies: '@babel/code-frame': 7.24.7 - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@babel/types': 7.24.7 '@babel/template@7.24.0': dependencies: '@babel/code-frame': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 + '@babel/parser': 7.25.3 + '@babel/types': 7.25.2 '@babel/template@7.24.7': dependencies: @@ -17086,7 +17287,7 @@ snapshots: '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@babel/types': 7.24.7 debug: 4.3.5 globals: 11.12.0 @@ -17101,7 +17302,7 @@ snapshots: '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@babel/types': 7.24.7 debug: 4.3.5 globals: 11.12.0 @@ -17116,8 +17317,8 @@ snapshots: '@babel/helper-function-name': 7.24.7 '@babel/helper-hoist-variables': 7.24.7 '@babel/helper-split-export-declaration': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 + '@babel/parser': 7.25.3 + '@babel/types': 7.25.2 debug: 4.3.5 globals: 11.12.0 transitivePeerDependencies: @@ -17180,6 +17381,11 @@ snapshots: '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + '@babel/types@7.29.11': + dependencies: + '@babel/helper-string-parser': 7.29.11 + '@babel/helper-validator-identifier': 7.29.7 + '@bcoe/v8-coverage@0.2.3': {} '@biomejs/biome@1.9.4': @@ -18088,6 +18294,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/trace-mapping@0.3.18': dependencies: '@jridgewell/resolve-uri': 3.1.0 @@ -18691,6 +18899,8 @@ snapshots: '@octokit/request-error': 6.1.4 '@octokit/webhooks-methods': 5.1.0 + '@one-ini/wasm@0.2.1': {} + '@open-draft/until@1.0.3': {} '@openpanel/nextjs@1.0.3(next@14.1.1(@babel/core@7.24.7)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': @@ -20702,7 +20912,7 @@ snapshots: ts-dedent: 2.2.0 util-deprecate: 1.0.2 watchpack: 2.4.1 - ws: 8.13.0 + ws: 8.18.0 transitivePeerDependencies: - bufferutil - encoding @@ -21291,7 +21501,7 @@ snapshots: '@types/babel__core@7.20.1': dependencies: - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@babel/types': 7.24.7 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 @@ -21315,7 +21525,7 @@ snapshots: '@types/babel__template@7.4.1': dependencies: - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@babel/types': 7.24.7 '@types/babel__template@7.4.4': @@ -21841,17 +22051,47 @@ snapshots: '@vue/compiler-core@3.4.24': dependencies: - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@vue/shared': 3.4.24 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.0 + '@vue/compiler-core@3.5.44': + dependencies: + '@babel/parser': 7.29.11 + '@vue/shared': 3.5.44 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.2 + '@vue/compiler-dom@3.4.24': dependencies: '@vue/compiler-core': 3.4.24 '@vue/shared': 3.4.24 + '@vue/compiler-dom@3.5.44': + dependencies: + '@vue/compiler-core': 3.5.44 + '@vue/shared': 3.5.44 + + '@vue/compiler-sfc@3.5.44': + dependencies: + '@babel/parser': 7.29.11 + '@vue/compiler-core': 3.5.44 + '@vue/compiler-dom': 3.5.44 + '@vue/compiler-ssr': 3.5.44 + '@vue/shared': 3.5.44 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.32 + source-map-js: 1.2.2 + + '@vue/compiler-ssr@3.5.44': + dependencies: + '@vue/compiler-dom': 3.5.44 + '@vue/shared': 3.5.44 + '@vue/language-core@1.8.27(typescript@5.5.4)': dependencies: '@volar/language-core': 1.11.1 @@ -21866,8 +22106,41 @@ snapshots: optionalDependencies: typescript: 5.5.4 + '@vue/reactivity@3.5.44': + dependencies: + '@vue/shared': 3.5.44 + + '@vue/runtime-core@3.5.44': + dependencies: + '@vue/reactivity': 3.5.44 + '@vue/shared': 3.5.44 + + '@vue/runtime-dom@3.5.44': + dependencies: + '@vue/reactivity': 3.5.44 + '@vue/runtime-core': 3.5.44 + '@vue/shared': 3.5.44 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.44': + dependencies: + '@vue/compiler-ssr': 3.5.44 + '@vue/runtime-dom': 3.5.44 + '@vue/shared': 3.5.44 + '@vue/shared@3.4.24': {} + '@vue/shared@3.5.44': {} + + '@vue/test-utils@2.5.1(@vue/compiler-dom@3.5.44)(@vue/server-renderer@3.5.44)(vue@3.5.44(typescript@5.5.4))': + dependencies: + '@vue/compiler-dom': 3.5.44 + js-beautify: 2.0.3 + vue: 3.5.44(typescript@5.5.4) + vue-component-type-helpers: 3.3.13 + optionalDependencies: + '@vue/server-renderer': 3.5.44 + '@web3-storage/multipart-parser@1.0.0': {} '@webassemblyjs/ast@1.12.1': @@ -21968,6 +22241,8 @@ snapshots: abbrev@2.0.0: {} + abbrev@5.0.0: {} + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -21979,7 +22254,7 @@ snapshots: acorn-globals@7.0.1: dependencies: - acorn: 8.11.3 + acorn: 8.12.0 acorn-walk: 8.3.2 acorn-import-assertions@1.9.0(acorn@8.12.0): @@ -21991,10 +22266,6 @@ snapshots: dependencies: acorn: 8.12.0 - acorn-jsx@5.3.2(acorn@8.11.3): - dependencies: - acorn: 8.11.3 - acorn-jsx@5.3.2(acorn@8.12.0): dependencies: acorn: 8.12.0 @@ -22327,6 +22598,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base64-js@1.5.1: {} base64id@2.0.0: {} @@ -22407,6 +22680,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.12: + dependencies: + balanced-match: 4.0.4 + braces@3.0.2: dependencies: fill-range: 7.0.1 @@ -22798,6 +23075,8 @@ snapshots: commander@12.1.0: {} + commander@14.0.3: {} + commander@2.20.3: {} commander@4.1.1: {} @@ -23018,6 +23297,8 @@ snapshots: csstype@3.1.2: {} + csstype@3.2.3: {} + csv-generate@3.4.3: {} csv-parse@4.16.3: {} @@ -23508,6 +23789,13 @@ snapshots: semver: 5.7.1 sigmund: 1.0.1 + editorconfig@3.0.2: + dependencies: + '@one-ini/wasm': 0.2.1 + commander: 14.0.3 + minimatch: 10.2.6 + semver: 7.8.5 + ee-first@1.1.1: {} ejs@3.1.9: @@ -23575,6 +23863,8 @@ snapshots: entities@4.5.0: {} + entities@7.0.1: {} + env-paths@2.2.1: {} envinfo@7.8.1: {} @@ -23874,8 +24164,8 @@ snapshots: espree@9.2.0: dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) + acorn: 8.12.0 + acorn-jsx: 5.3.2(acorn@8.12.0) eslint-visitor-keys: 3.4.1 espree@9.5.2: @@ -24460,6 +24750,12 @@ snapshots: package-json-from-dist: 1.0.0 path-scurry: 1.11.1 + glob@13.0.6: + dependencies: + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 + glob@7.1.6: dependencies: fs.realpath: 1.0.0 @@ -25507,8 +25803,18 @@ snapshots: glob: 8.1.0 nopt: 6.0.0 + js-beautify@2.0.3: + dependencies: + config-chain: 1.1.13 + editorconfig: 3.0.2 + glob: 13.0.6 + js-cookie: 3.0.8 + nopt: 10.0.1 + js-cookie@3.0.5: {} + js-cookie@3.0.8: {} + js-levenshtein@1.1.6: {} js-tokens@4.0.0: {} @@ -25527,7 +25833,7 @@ snapshots: jscodeshift@0.14.0(@babel/preset-env@7.21.5(@babel/core@7.21.8)): dependencies: '@babel/core': 7.24.4 - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.4) '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.4) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.4) @@ -25552,7 +25858,7 @@ snapshots: jscodeshift@0.14.0(@babel/preset-env@7.22.5(@babel/core@7.23.6)): dependencies: '@babel/core': 7.24.4 - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.4) '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.4) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.4) @@ -25577,7 +25883,7 @@ snapshots: jsdom@20.0.3: dependencies: abab: 2.0.6 - acorn: 8.11.2 + acorn: 8.12.0 acorn-globals: 7.0.1 cssom: 0.5.0 cssstyle: 2.3.0 @@ -25600,7 +25906,7 @@ snapshots: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.13.0 + ws: 8.18.0 xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -25961,6 +26267,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.5.3: {} + lru-cache@4.1.5: dependencies: pseudomap: 1.0.2 @@ -26000,6 +26308,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + magic-string@0.30.8: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -26602,8 +26914,8 @@ snapshots: micromark-extension-mdxjs@1.0.1: dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) + acorn: 8.12.0 + acorn-jsx: 5.3.2(acorn@8.12.0) micromark-extension-mdx-expression: 1.0.8 micromark-extension-mdx-jsx: 1.0.5 micromark-extension-mdx-md: 1.0.1 @@ -26613,8 +26925,8 @@ snapshots: micromark-extension-mdxjs@3.0.0: dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) + acorn: 8.12.0 + acorn-jsx: 5.3.2(acorn@8.12.0) micromark-extension-mdx-expression: 3.0.0 micromark-extension-mdx-jsx: 3.0.0 micromark-extension-mdx-md: 2.0.0 @@ -26948,6 +27260,10 @@ snapshots: minimalistic-assert@1.0.1: {} + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.12 + minimatch@3.0.8: dependencies: brace-expansion: 1.1.11 @@ -27010,6 +27326,8 @@ snapshots: minipass@7.1.2: {} + minipass@7.1.3: {} + minizlib@2.1.2: dependencies: minipass: 3.3.6 @@ -27036,7 +27354,7 @@ snapshots: mlly@1.6.1: dependencies: - acorn: 8.11.3 + acorn: 8.12.0 pathe: 1.1.2 pkg-types: 1.0.3 ufo: 1.5.3 @@ -27085,7 +27403,7 @@ snapshots: '@types/cookie': 0.4.1 '@types/js-levenshtein': 1.1.1 chalk: 4.1.1 - chokidar: 3.5.3 + chokidar: 3.6.0 cookie: 0.4.2 graphql: 16.6.0 headers-polyfill: 3.1.2 @@ -27150,6 +27468,8 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + nanoid@3.3.20: {} + nanoid@3.3.6: {} nanoid@3.3.7: {} @@ -27227,7 +27547,7 @@ snapshots: minimist: 1.2.8 next: 14.1.1(@babel/core@7.24.7)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) - next-themes@0.2.1(next@14.1.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + next-themes@0.2.1(next@14.1.1(@babel/core@7.24.7)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: next: 14.1.1(@babel/core@7.24.7)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) react: 18.2.0 @@ -27306,7 +27626,7 @@ snapshots: match-sorter: 6.3.1 next: 14.1.1(@babel/core@7.24.7)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) next-seo: 6.4.0(next@14.1.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - next-themes: 0.2.1(next@14.1.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + next-themes: 0.2.1(next@14.1.1(@babel/core@7.24.7)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) nextra: 2.13.2(next@14.1.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -27475,6 +27795,10 @@ snapshots: dependencies: abbrev: 1.1.1 + nopt@10.0.1: + dependencies: + abbrev: 5.0.0 + nopt@6.0.0: dependencies: abbrev: 1.1.1 @@ -27922,6 +28246,11 @@ snapshots: lru-cache: 9.1.2 minipass: 5.0.0 + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.3 + minipass: 7.1.3 + path-to-regexp@0.1.7: {} path-to-regexp@6.2.1: {} @@ -27968,6 +28297,8 @@ snapshots: picocolors@1.0.1: {} + picocolors@1.1.1: {} + picomatch@2.3.1: {} picomatch@4.0.2: {} @@ -28033,20 +28364,20 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.4.24 - postcss-load-config@3.1.4(postcss@8.4.41)(ts-node@10.9.1(@types/node@20.3.1)(typescript@5.5.4)): + postcss-load-config@3.1.4(postcss@8.5.32)(ts-node@10.9.1(@types/node@20.3.1)(typescript@5.5.4)): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: - postcss: 8.4.41 + postcss: 8.5.32 ts-node: 10.9.1(@types/node@20.3.1)(typescript@5.5.4) - postcss-load-config@3.1.4(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)): + postcss-load-config@3.1.4(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: - postcss: 8.4.41 + postcss: 8.5.32 ts-node: 10.9.1(@types/node@22.1.0)(typescript@5.5.4) postcss-load-config@4.0.1(postcss@8.4.24)(ts-node@10.9.1(@types/node@18.16.17)(typescript@5.5.4)): @@ -28066,12 +28397,12 @@ snapshots: ts-node: 10.9.1(@types/node@20.3.1)(typescript@5.5.4) optional: true - postcss-load-config@4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)): + postcss-load-config@4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)): dependencies: lilconfig: 2.1.0 yaml: 2.3.1 optionalDependencies: - postcss: 8.4.41 + postcss: 8.5.32 ts-node: 10.9.1(@types/node@22.1.0)(typescript@5.5.4) optional: true @@ -28156,6 +28487,12 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + postcss@8.5.32: + dependencies: + nanoid: 3.3.20 + picocolors: 1.1.1 + source-map-js: 1.2.2 + postgres-array@2.0.0: {} postgres-bytea@1.0.0: {} @@ -28943,10 +29280,6 @@ snapshots: dependencies: estree-walker: 0.6.1 - rollup@3.25.0: - optionalDependencies: - fsevents: 2.3.3 - rollup@3.27.1: optionalDependencies: fsevents: 2.3.3 @@ -29118,6 +29451,8 @@ snapshots: semver@7.6.2: {} + semver@7.8.5: {} + send@0.18.0: dependencies: debug: 2.6.9 @@ -29393,6 +29728,8 @@ snapshots: source-map-js@1.2.0: {} + source-map-js@1.2.2: {} + source-map-loader@5.0.0(webpack@5.92.1(esbuild@0.21.5)): dependencies: iconv-lite: 0.6.3 @@ -29689,7 +30026,7 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@2.10.3(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.4.41)(sass@1.77.8)(svelte@3.59.1): + svelte-check@2.10.3(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.32)(sass@1.77.8)(svelte@3.59.1): dependencies: '@jridgewell/trace-mapping': 0.3.18 chokidar: 3.5.3 @@ -29698,7 +30035,7 @@ snapshots: picocolors: 1.0.0 sade: 1.8.1 svelte: 3.59.1 - svelte-preprocess: 4.10.7(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.4.41)(sass@1.77.8)(svelte@3.59.1)(typescript@5.5.4) + svelte-preprocess: 4.10.7(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.32)(sass@1.77.8)(svelte@3.59.1)(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - '@babel/core' @@ -29712,7 +30049,7 @@ snapshots: - stylus - sugarss - svelte-check@2.10.3(@babel/core@7.24.9)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.4.41)(sass@1.77.8)(svelte@3.59.1): + svelte-check@2.10.3(@babel/core@7.24.9)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.32)(sass@1.77.8)(svelte@3.59.1): dependencies: '@jridgewell/trace-mapping': 0.3.18 chokidar: 3.5.3 @@ -29721,7 +30058,7 @@ snapshots: picocolors: 1.0.0 sade: 1.8.1 svelte: 3.59.1 - svelte-preprocess: 4.10.7(@babel/core@7.24.9)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.4.41)(sass@1.77.8)(svelte@3.59.1)(typescript@5.5.4) + svelte-preprocess: 4.10.7(@babel/core@7.24.9)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.32)(sass@1.77.8)(svelte@3.59.1)(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - '@babel/core' @@ -29739,7 +30076,7 @@ snapshots: dependencies: svelte: 3.59.1 - svelte-preprocess@4.10.7(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.4.41)(sass@1.77.8)(svelte@3.59.1)(typescript@5.5.4): + svelte-preprocess@4.10.7(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.32)(sass@1.77.8)(svelte@3.59.1)(typescript@5.5.4): dependencies: '@types/pug': 2.0.6 '@types/sass': 1.45.0 @@ -29751,12 +30088,12 @@ snapshots: optionalDependencies: '@babel/core': 7.23.6 less: 4.2.0 - postcss: 8.4.41 - postcss-load-config: 4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)) + postcss: 8.5.32 + postcss-load-config: 4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)) sass: 1.77.8 typescript: 5.5.4 - svelte-preprocess@4.10.7(@babel/core@7.24.9)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.4.41)(sass@1.77.8)(svelte@3.59.1)(typescript@5.5.4): + svelte-preprocess@4.10.7(@babel/core@7.24.9)(less@4.2.0)(postcss-load-config@4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.32)(sass@1.77.8)(svelte@3.59.1)(typescript@5.5.4): dependencies: '@types/pug': 2.0.6 '@types/sass': 1.45.0 @@ -29768,8 +30105,8 @@ snapshots: optionalDependencies: '@babel/core': 7.24.9 less: 4.2.0 - postcss: 8.4.41 - postcss-load-config: 4.0.1(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)) + postcss: 8.5.32 + postcss-load-config: 4.0.1(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)) sass: 1.77.8 typescript: 5.5.4 @@ -30151,47 +30488,47 @@ snapshots: tsscmp@1.0.6: {} - tsup@6.7.0(postcss@8.4.41)(ts-node@10.9.1(@types/node@20.3.1)(typescript@5.5.4))(typescript@5.5.4): + tsup@6.7.0(postcss@8.5.32)(ts-node@10.9.1(@types/node@20.3.1)(typescript@5.5.4))(typescript@5.5.4): dependencies: bundle-require: 4.0.1(esbuild@0.17.19) cac: 6.7.14 - chokidar: 3.5.3 - debug: 4.3.4 + chokidar: 3.6.0 + debug: 4.3.5 esbuild: 0.17.19 execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 3.1.4(postcss@8.4.41)(ts-node@10.9.1(@types/node@20.3.1)(typescript@5.5.4)) + postcss-load-config: 3.1.4(postcss@8.5.32)(ts-node@10.9.1(@types/node@20.3.1)(typescript@5.5.4)) resolve-from: 5.0.0 - rollup: 3.25.0 + rollup: 3.29.4 source-map: 0.8.0-beta.0 sucrase: 3.32.0 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.4.41 + postcss: 8.5.32 typescript: 5.5.4 transitivePeerDependencies: - supports-color - ts-node - tsup@6.7.0(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4): + tsup@6.7.0(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4))(typescript@5.5.4): dependencies: bundle-require: 4.0.1(esbuild@0.17.19) cac: 6.7.14 - chokidar: 3.5.3 - debug: 4.3.4 + chokidar: 3.6.0 + debug: 4.3.5 esbuild: 0.17.19 execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 3.1.4(postcss@8.4.41)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)) + postcss-load-config: 3.1.4(postcss@8.5.32)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)) resolve-from: 5.0.0 - rollup: 3.25.0 + rollup: 3.29.4 source-map: 0.8.0-beta.0 sucrase: 3.32.0 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.4.41 + postcss: 8.5.32 typescript: 5.5.4 transitivePeerDependencies: - supports-color @@ -30905,6 +31242,8 @@ snapshots: vscode-textmate@8.0.0: {} + vue-component-type-helpers@3.3.13: {} + vue-template-compiler@2.7.16: dependencies: de-indent: 1.0.2 @@ -30917,6 +31256,16 @@ snapshots: semver: 7.6.0 typescript: 5.5.4 + vue@3.5.44(typescript@5.5.4): + dependencies: + '@vue/compiler-dom': 3.5.44 + '@vue/compiler-sfc': 3.5.44 + '@vue/runtime-dom': 3.5.44 + '@vue/server-renderer': 3.5.44 + '@vue/shared': 3.5.44 + optionalDependencies: + typescript: 5.5.4 + w3c-keyname@2.2.8: {} w3c-xmlserializer@4.0.0: