From 3c1a258c371a674cbf951a80d4546440e550b2e3 Mon Sep 17 00:00:00 2001 From: Carlos Fadhel Date: Tue, 7 Jul 2026 03:18:21 -0400 Subject: [PATCH 1/2] feat(vue): add @tryabby/vue integration package A Vue 3 & Nuxt integration with feature parity to the React and Svelte packages. - useAbby: SSR-safe reactive variant (ComputedRef) + onAct - useFeatureFlag: Ref - useRemoteConfig: typed Ref - getFeatureFlagValue / getRemoteConfig / getABTestValue / getVariants / getABResetFunction / updateUserProperties - AbbyProvider: Vue plugin for client data loading + SSR hydration Reactivity is driven by abby.subscribe() with effect-scope cleanup (onScopeDispose); cookie storage via js-cookie mirrors the other packages. Built with tsup (cjs + esm + dts) and covered by 16 vitest tests including type-level assertions. Closes #68 Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/tryabby-vue-integration.md | 5 + packages/vue/.gitignore | 1 + packages/vue/README.md | 127 ++++++ packages/vue/package.json | 46 ++ packages/vue/src/StorageService.ts | 68 +++ packages/vue/src/createAbby.ts | 320 ++++++++++++++ packages/vue/src/index.ts | 6 + packages/vue/tests/featureFlags.test.ts | 40 ++ packages/vue/tests/mocks/handlers.ts | 36 ++ packages/vue/tests/mocks/server.ts | 5 + packages/vue/tests/remoteConfig.test.ts | 40 ++ packages/vue/tests/setup.ts | 25 ++ packages/vue/tests/types.test.ts | 67 +++ packages/vue/tests/useAbby.test.ts | 104 +++++ packages/vue/tests/utils.ts | 20 + packages/vue/tsconfig.json | 11 + packages/vue/tsup.config.ts | 10 + packages/vue/vitest.config.ts | 9 + pnpm-lock.yaml | 558 ++++++++++++++++++------ 19 files changed, 1370 insertions(+), 128 deletions(-) create mode 100644 .changeset/tryabby-vue-integration.md create mode 100644 packages/vue/.gitignore create mode 100644 packages/vue/README.md create mode 100644 packages/vue/package.json create mode 100644 packages/vue/src/StorageService.ts create mode 100644 packages/vue/src/createAbby.ts create mode 100644 packages/vue/src/index.ts create mode 100644 packages/vue/tests/featureFlags.test.ts create mode 100644 packages/vue/tests/mocks/handlers.ts create mode 100644 packages/vue/tests/mocks/server.ts create mode 100644 packages/vue/tests/remoteConfig.test.ts create mode 100644 packages/vue/tests/setup.ts create mode 100644 packages/vue/tests/types.test.ts create mode 100644 packages/vue/tests/useAbby.test.ts create mode 100644 packages/vue/tests/utils.ts create mode 100644 packages/vue/tsconfig.json create mode 100644 packages/vue/tsup.config.ts create mode 100644 packages/vue/vitest.config.ts diff --git a/.changeset/tryabby-vue-integration.md b/.changeset/tryabby-vue-integration.md new file mode 100644 index 00000000..1890ceea --- /dev/null +++ b/.changeset/tryabby-vue-integration.md @@ -0,0 +1,5 @@ +--- +"@tryabby/vue": minor +--- + +Add `@tryabby/vue`, a Vue 3 & Nuxt integration for Abby. It ships fully-typed `useAbby`, `useFeatureFlag`, and `useRemoteConfig` composables, an `AbbyProvider` plugin for data loading/SSR, and non-reactive getters — with the same API surface and type quality as the React and Svelte packages. diff --git a/packages/vue/.gitignore b/packages/vue/.gitignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/packages/vue/.gitignore @@ -0,0 +1 @@ +dist diff --git a/packages/vue/README.md b/packages/vue/README.md new file mode 100644 index 00000000..20f2f64e --- /dev/null +++ b/packages/vue/README.md @@ -0,0 +1,127 @@ +# @tryabby/vue + +The official [Abby](https://www.tryabby.com) integration for **Vue 3** and **Nuxt**. + +It gives you fully-typed composables for A/B tests, feature flags and remote config, with the same API surface and type quality as the React and Svelte packages. + +## Installation + +```bash +npm install @tryabby/vue +# or +pnpm add @tryabby/vue +``` + +## Quick start + +Create your Abby instance once and export the composables from it: + +```ts +// abby.ts +import { createAbby } from "@tryabby/vue"; + +export const { + useAbby, + useFeatureFlag, + useRemoteConfig, + AbbyProvider, + getFeatureFlagValue, + getRemoteConfig, + getABTestValue, +} = createAbby({ + projectId: "", + currentEnvironment: import.meta.env.MODE, + environments: ["development", "production"], + tests: { + "footer-test": { variants: ["old", "new"] }, + }, + flags: ["new-dashboard"], + remoteConfig: { theme: "String" }, +}); +``` + +Register the `AbbyProvider` plugin on your app. It loads your project data on +the client (and accepts server-fetched `initialData` for SSR): + +```ts +// main.ts +import { createApp } from "vue"; +import App from "./App.vue"; +import { AbbyProvider } from "./abby"; + +createApp(App).use(AbbyProvider).mount("#app"); +``` + +## Composables + +### `useAbby` + +Returns the reactive variant for a test and an `onAct` callback to report an +interaction. The variant is a `ComputedRef`, so it works in templates directly. + +```vue + + + +``` + +You can also pass a lookup object to map each variant to a value: + +```ts +const { variant } = useAbby("footer-test", { old: 0, new: 1 }); +// variant.value is 0 | 1 +``` + +### `useFeatureFlag` + +```vue + +``` + +### `useRemoteConfig` + +```vue + +``` + +## Non-reactive helpers + +For places outside of a component's reactive scope (route guards, plain +modules, event handlers) the instance also exposes plain getters: + +- `getFeatureFlagValue(name)` – the current value of a feature flag +- `getRemoteConfig(name)` – the current value of a remote config entry +- `getABTestValue(name, lookup?)` – the currently selected variant of a test +- `getVariants(name)` – all variants of a test +- `getABResetFunction(name)` – resets the persisted variant of a test +- `updateUserProperties(user)` – updates user properties for targeting + +## Nuxt / SSR + +To avoid hydration mismatches, `useAbby` renders an empty variant on the server +and resolves the real variant on mount. When you fetch project data on the +server, hand it to the provider so flags and remote config are available during +the first render: + +```ts +app.use(AbbyProvider, { initialData }); +``` + +## License + +ISC diff --git a/packages/vue/package.json b/packages/vue/package.json new file mode 100644 index 00000000..dc841ea8 --- /dev/null +++ b/packages/vue/package.json @@ -0,0 +1,46 @@ +{ + "name": "@tryabby/vue", + "version": "0.1.0", + "description": "Vue 3 & Nuxt integration for Abby", + "main": "dist/index.js", + "files": ["dist"], + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "homepage": "https://docs.tryabby.com", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "scripts": { + "build": "tsup src/index.ts", + "dev": "pnpm run build --watch", + "test": "vitest", + "typecheck": "tsc --noEmit" + }, + "keywords": ["abby", "vue", "nuxt", "feature-flags", "ab-testing"], + "author": "", + "license": "ISC", + "peerDependencies": { + "vue": "^3.3.0" + }, + "devDependencies": { + "@types/js-cookie": "^3.0.3", + "@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.4.0" + }, + "dependencies": { + "@tryabby/core": "workspace:*", + "js-cookie": "^3.0.5" + } +} diff --git a/packages/vue/src/StorageService.ts b/packages/vue/src/StorageService.ts new file mode 100644 index 00000000..6e61da3b --- /dev/null +++ b/packages/vue/src/StorageService.ts @@ -0,0 +1,68 @@ +import { + type IStorageService, + type StorageServiceOptions, + getABStorageKey, + getFFStorageKey, + getRCStorageKey, +} from "@tryabby/core"; +import Cookie from "js-cookie"; + +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 : 365, + }); + } + + 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); + } + + 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); + } + + 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..d0b3ba59 --- /dev/null +++ b/packages/vue/src/createAbby.ts @@ -0,0 +1,320 @@ +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 App, + type ComputedRef, + type InjectionKey, + type Ref, + computed, + getCurrentInstance, + getCurrentScope, + inject, + onMounted, + onScopeDispose, + shallowRef, +} from "vue"; +import { + FlagStorageService, + RemoteConfigStorageService, + TestStorageService, +} from "./StorageService"; + +export type ABTestReturnValue = Lookup extends undefined + ? TestVariant + : TestVariant extends keyof Lookup + ? Lookup[TestVariant] + : never; + +/** + * Injection key used by the `AbbyProvider` plugin to expose the underlying + * Abby instance to nested components via `useAbbyInstance`. + */ +export const ABBY_INJECTION_KEY: InjectionKey = Symbol("abby"); + +const isBrowser = typeof window !== "undefined"; + +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 + >, +>( + config: AbbyConfig< + FlagName, + Tests, + string[], + RemoteConfigName, + RemoteConfig, + User + > +) { + const abby = new Abby< + FlagName, + TestName, + Tests, + RemoteConfig, + RemoteConfigName, + string[], + User + >( + config, + { + get: (key: string) => { + if (!isBrowser) return null; + return TestStorageService.get(config.projectId, key); + }, + set: (key: string, value: string) => { + if (!isBrowser || config.cookies?.disableByDefault) return; + TestStorageService.set(config.projectId, key, value); + }, + }, + { + get: (key: string) => { + if (!isBrowser) return null; + return FlagStorageService.get(config.projectId, key); + }, + set: (key: string, value: string) => { + if (!isBrowser) return; + FlagStorageService.set(config.projectId, key, value); + }, + }, + { + get: (key: string) => { + if (!isBrowser) return null; + return RemoteConfigStorageService.get(config.projectId, key); + }, + set: (key: string, value: string) => { + if (!isBrowser) return; + RemoteConfigStorageService.set(config.projectId, key, value); + }, + } + ); + + /** + * Subscribes `listener` to Abby's data changes and automatically tears the + * subscription down when the surrounding Vue effect scope (a component's + * `setup`, an `effectScope`, ...) is disposed. On the server there is nothing + * to react to, so this is a no-op. + */ + const subscribeScoped = (listener: () => void) => { + if (!isBrowser) return; + const unsubscribe = abby.subscribe(() => listener()); + if (getCurrentScope()) { + onScopeDispose(unsubscribe); + } + return unsubscribe; + }; + + 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; + } => { + // Start with an empty variant so the server render and the first client + // render are identical, which avoids hydration mismatches. The real + // variant is resolved once we are safely on the client (on mount). + const selectedVariant = shallowRef(""); + + const resolve = () => { + selectedVariant.value = abby.getTestVariant(name); + }; + + if (isBrowser) { + const activate = () => { + resolve(); + if (!selectedVariant.value) return; + HttpService.sendData({ + url: config.apiUrl, + type: AbbyEventType.PING, + data: { + projectId: config.projectId, + selectedVariant: selectedVariant.value, + testName: name as string, + }, + }); + }; + + // Defer to `onMounted` inside components to keep hydration stable; + // resolve immediately when the composable is used outside of a component. + if (getCurrentInstance()) { + onMounted(activate); + } else { + activate(); + } + + subscribeScoped(resolve); + } + + const variant = computed( + () => + (lookupObject + ? lookupObject[selectedVariant.value as TestVariant] + : selectedVariant.value) as ABTestReturnValue + ); + + /** + * Notifies the server that the selected variant was acted upon + * (e.g. a button using the variant was clicked). + */ + 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, + }, + }); + }; + + return { variant, onAct }; + }; + + const useFeatureFlag = (name: FlagName): Ref => { + const flag = shallowRef(abby.getFeatureFlag(name)); + subscribeScoped(() => { + flag.value = abby.getFeatureFlag(name); + }); + return flag; + }; + + const useRemoteConfig = < + T extends RemoteConfigName, + Config extends RemoteConfig[T], + >( + name: T + ): Ref> => { + const remoteConfig = shallowRef(abby.getRemoteConfig(name)) as Ref< + RemoteConfigValueStringToType + >; + subscribeScoped(() => { + remoteConfig.value = abby.getRemoteConfig(name); + }); + return remoteConfig; + }; + + const getFeatureFlagValue = (name: FlagName) => abby.getFeatureFlag(name); + + const getRemoteConfig = < + T extends RemoteConfigName, + Config extends RemoteConfig[T], + >( + name: T + ): RemoteConfigValueStringToType => abby.getRemoteConfig(name); + + const getABTestValue = < + K extends keyof Tests, + TestVariant extends Tests[K]["variants"][number], + LookupValue, + const Lookup extends + | Record + | undefined = undefined, + >( + name: K, + lookupObject?: Lookup + ): ABTestReturnValue => { + const variant = abby.getTestVariant(name); + if (lookupObject === undefined) { + return variant as ABTestReturnValue; + } + return lookupObject[variant as TestVariant] as ABTestReturnValue< + Lookup, + TestVariant + >; + }; + + const getVariants = (name: K) => + abby.getVariants(name); + + /** + * Creates a function that resets the persisted variant for a given test. + */ + const getABResetFunction = (name: K) => { + return () => { + TestStorageService.remove(config.projectId, name as string); + }; + }; + + const updateUserProperties = ( + user: Partial<{ + -readonly [K in keyof User]: Infer; + }> + ) => { + abby.updateUserProperties(user); + }; + + /** + * Loads the project data from the Abby API. Called automatically by the + * `AbbyProvider` plugin in the browser and exposed for manual/SSR control. + */ + const loadProjectData = () => abby.loadProjectData(); + + /** + * A Vue plugin that hydrates Abby with server data (when provided) or loads + * it on the client, and exposes the instance through Vue's dependency + * injection so nested components can reach it via `useAbbyInstance`. + * + * @example + * app.use(AbbyProvider); + * // or, for SSR, pass data fetched on the server: + * app.use(AbbyProvider, { initialData }); + */ + const AbbyProvider = { + install(app: App, options?: { initialData?: AbbyDataResponse }) { + if (options?.initialData) { + abby.init(options.initialData); + } else if (isBrowser) { + void abby.loadProjectData(); + } + app.provide(ABBY_INJECTION_KEY, abby); + }, + }; + + /** + * Returns the underlying Abby instance provided by `AbbyProvider`, falling + * back to the instance created by this `createAbby` call. + */ + const useAbbyInstance = () => inject(ABBY_INJECTION_KEY, abby) as typeof abby; + + return { + useAbby, + useFeatureFlag, + useRemoteConfig, + getFeatureFlagValue, + getRemoteConfig, + getABTestValue, + getVariants, + getABResetFunction, + updateUserProperties, + loadProjectData, + AbbyProvider, + useAbbyInstance, + __abby__: abby, + }; +} diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts new file mode 100644 index 00000000..f7273f8b --- /dev/null +++ b/packages/vue/src/index.ts @@ -0,0 +1,6 @@ +export { + createAbby, + type ABTestReturnValue, + ABBY_INJECTION_KEY, +} from "./createAbby"; +export { type ABConfig, type AbbyConfig, defineConfig } from "@tryabby/core"; diff --git a/packages/vue/tests/featureFlags.test.ts b/packages/vue/tests/featureFlags.test.ts new file mode 100644 index 00000000..64a236ee --- /dev/null +++ b/packages/vue/tests/featureFlags.test.ts @@ -0,0 +1,40 @@ +import { flushPromises } from "@vue/test-utils"; +import { describe, expect, it } from "vitest"; +import { createAbby } from "../src"; +import { withSetup } from "./utils"; + +const createInstance = () => + createAbby({ + environments: [""], + currentEnvironment: "", + projectId: "123", + flags: ["flag1"], + }); + +describe("useFeatureFlag", () => { + it("returns the default value before the data is loaded", () => { + const { useFeatureFlag } = createInstance(); + const { result } = withSetup(() => useFeatureFlag("flag1")); + expect(result.value).toBe(false); + }); + + it("reactively reflects the flag value once the data loads", async () => { + const instance = createInstance(); + const { result } = withSetup(() => instance.useFeatureFlag("flag1")); + + expect(result.value).toBe(false); + + await instance.__abby__.loadProjectData(); + await flushPromises(); + + expect(result.value).toBe(true); + }); +}); + +describe("getFeatureFlagValue", () => { + it("returns the resolved flag value", async () => { + const instance = createInstance(); + await instance.__abby__.loadProjectData(); + expect(instance.getFeatureFlagValue("flag1")).toBe(true); + }); +}); diff --git a/packages/vue/tests/mocks/handlers.ts b/packages/vue/tests/mocks/handlers.ts new file mode 100644 index 00000000..6be59863 --- /dev/null +++ b/packages/vue/tests/mocks/handlers.ts @@ -0,0 +1,36 @@ +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, + }, + ], + remoteConfig: [ + { + name: "remoteConfig1", + value: "FooBar", + }, + ], +}; + +export const handlers = [ + rest.get(`${ABBY_BASE_URL}api/dashboard/:projectId/data`, (_req, res, ctx) => + res(ctx.json(returnData)) + ), + rest.get(`${ABBY_BASE_URL}api/v2/data/:projectId`, (_req, res, ctx) => + 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/remoteConfig.test.ts b/packages/vue/tests/remoteConfig.test.ts new file mode 100644 index 00000000..aa421f18 --- /dev/null +++ b/packages/vue/tests/remoteConfig.test.ts @@ -0,0 +1,40 @@ +import { flushPromises } from "@vue/test-utils"; +import { describe, expect, it } from "vitest"; +import { createAbby } from "../src"; +import { withSetup } from "./utils"; + +const createInstance = () => + createAbby({ + environments: [""], + currentEnvironment: "", + projectId: "123", + remoteConfig: { remoteConfig1: "String" }, + }); + +describe("useRemoteConfig", () => { + it("returns the default value before the data is loaded", () => { + const { useRemoteConfig } = createInstance(); + const { result } = withSetup(() => useRemoteConfig("remoteConfig1")); + expect(result.value).toBe(""); + }); + + it("reactively reflects the remote config value once the data loads", async () => { + const instance = createInstance(); + const { result } = withSetup(() => + instance.useRemoteConfig("remoteConfig1") + ); + + await instance.__abby__.loadProjectData(); + await flushPromises(); + + expect(result.value).toBe("FooBar"); + }); +}); + +describe("getRemoteConfig", () => { + it("returns the resolved remote config value", async () => { + const instance = createInstance(); + await instance.__abby__.loadProjectData(); + expect(instance.getRemoteConfig("remoteConfig1")).toBe("FooBar"); + }); +}); diff --git a/packages/vue/tests/setup.ts b/packages/vue/tests/setup.ts new file mode 100644 index 00000000..cdaa6169 --- /dev/null +++ b/packages/vue/tests/setup.ts @@ -0,0 +1,25 @@ +import { HttpService } from "@tryabby/core"; +import fetch from "node-fetch"; +import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest"; +import { server } from "./mocks/server"; + +// @ts-ignore node-fetch's types differ slightly from the DOM fetch signature +global.fetch = fetch; + +// Establish API mocking before all tests. +beforeAll(() => server.listen({ onUnhandledRequest: "bypass" })); + +// Silence the analytics ping/act network calls by default. Individual tests +// can still spy on `HttpService.sendData` to assert against the recorded calls. +beforeEach(() => { + vi.spyOn(HttpService, "sendData").mockImplementation(() => undefined); +}); + +// Clean up after every test case. +afterEach(() => { + vi.restoreAllMocks(); + server.resetHandlers(); +}); + +// Close the mocking server once the whole suite is done. +afterAll(() => server.close()); diff --git a/packages/vue/tests/types.test.ts b/packages/vue/tests/types.test.ts new file mode 100644 index 00000000..9aea7c10 --- /dev/null +++ b/packages/vue/tests/types.test.ts @@ -0,0 +1,67 @@ +import { describe, expectTypeOf, it } from "vitest"; +import type { ComputedRef, Ref } from "vue"; +import { createAbby } from "../src"; + +describe("useAbby types", () => { + it("infers the variant type and constrains the test name", () => { + const { useAbby } = createAbby({ + environments: [""], + currentEnvironment: "", + projectId: "123", + tests: { + test: { variants: ["ONLY_ONE_VARIANT"] }, + test2: { variants: ["A", "B"] }, + }, + }); + + expectTypeOf(useAbby).parameter(0).toEqualTypeOf<"test" | "test2">(); + + const single = useAbby("test"); + expectTypeOf(single.variant).toEqualTypeOf< + ComputedRef<"ONLY_ONE_VARIANT"> + >(); + expectTypeOf(single.onAct).toEqualTypeOf<() => void>(); + + const looked = useAbby("test2", { A: 1, B: 2 }); + expectTypeOf(looked.variant).toMatchTypeOf>(); + }); +}); + +describe("useFeatureFlag types", () => { + it("is a Ref and constrains the flag name", () => { + const { useFeatureFlag } = createAbby({ + environments: [""], + currentEnvironment: "", + projectId: "123", + flags: ["test"], + }); + + expectTypeOf(useFeatureFlag).parameter(0).toEqualTypeOf<"test">(); + expectTypeOf(useFeatureFlag("test")).toEqualTypeOf>(); + }); +}); + +describe("useRemoteConfig types", () => { + it("maps each remote config type to its value type", () => { + const { useRemoteConfig } = createAbby({ + environments: [""], + currentEnvironment: "", + projectId: "123", + remoteConfig: { + stringRc: "String", + numberRc: "Number", + jsonRc: "JSON", + }, + }); + + expectTypeOf(useRemoteConfig) + .parameter(0) + .toEqualTypeOf<"stringRc" | "numberRc" | "jsonRc">(); + + expectTypeOf(useRemoteConfig("stringRc")).toEqualTypeOf>(); + expectTypeOf(useRemoteConfig("numberRc")).toEqualTypeOf>(); + expectTypeOf(useRemoteConfig("jsonRc")).toEqualTypeOf< + Ref> + >(); + }); +}); diff --git a/packages/vue/tests/useAbby.test.ts b/packages/vue/tests/useAbby.test.ts new file mode 100644 index 00000000..2a000cb7 --- /dev/null +++ b/packages/vue/tests/useAbby.test.ts @@ -0,0 +1,104 @@ +import { AbbyEventType, HttpService } from "@tryabby/core"; +import { flushPromises } from "@vue/test-utils"; +import { describe, expect, it, vi } from "vitest"; +import { createAbby } from "../src"; +import { TestStorageService } from "../src/StorageService"; +import { withSetup } from "./utils"; + +const createTestInstance = () => + createAbby({ + environments: [""], + currentEnvironment: "", + projectId: "123", + tests: { + test: { + variants: ["SimonsText", "MatthiasText", "TomsText", "TimsText"], + }, + test2: { variants: ["A", "B"] }, + }, + }); + +describe("useAbby", () => { + it("returns a defined variant and an onAct callback", async () => { + const { useAbby } = createTestInstance(); + const { result } = withSetup(() => useAbby("test")); + await flushPromises(); + + expect(["SimonsText", "MatthiasText", "TomsText", "TimsText"]).toContain( + result.variant.value + ); + expect(typeof result.onAct).toBe("function"); + }); + + it("uses the persisted variant without overwriting it", async () => { + const persistedValue = "MatthiasText"; + const getSpy = vi + .spyOn(TestStorageService, "get") + .mockReturnValue(persistedValue); + const setSpy = vi.spyOn(TestStorageService, "set"); + + const { useAbby } = createTestInstance(); + const { result } = withSetup(() => useAbby("test")); + await flushPromises(); + + expect(getSpy).toHaveBeenCalled(); + expect(setSpy).not.toHaveBeenCalled(); + expect(result.variant.value).toBe(persistedValue); + }); + + it("resolves the value from a lookup object", async () => { + vi.spyOn(TestStorageService, "get").mockReturnValue("SimonsText"); + + const { useAbby } = createTestInstance(); + const { result } = withSetup(() => + useAbby("test", { + SimonsText: "a", + MatthiasText: "b", + TomsText: "c", + TimsText: "d", + }) + ); + await flushPromises(); + + expect(result.variant.value).toBe("a"); + }); + + it("pings the server with the selected variant on mount", async () => { + const spy = vi.spyOn(HttpService, "sendData"); + + const { useAbby } = createTestInstance(); + withSetup(() => useAbby("test")); + await flushPromises(); + + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ type: AbbyEventType.PING }) + ); + }); + + it("notifies the server with onAct", async () => { + const spy = vi.spyOn(HttpService, "sendData"); + + const { useAbby } = createTestInstance(); + const { result } = withSetup(() => useAbby("test")); + await flushPromises(); + result.onAct(); + + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ type: AbbyEventType.ACT }) + ); + }); +}); + +describe("getABTestValue / getVariants", () => { + it("returns all variants of a test", () => { + const { getVariants } = createTestInstance(); + expect(getVariants("test2")).toEqual(["A", "B"]); + }); + + it("resolves the lookup value for the currently selected variant", () => { + vi.spyOn(TestStorageService, "get").mockReturnValue("A"); + const { getABTestValue } = createTestInstance(); + + expect(getABTestValue("test2", { A: 1, B: 2 })).toBe(1); + }); +}); diff --git a/packages/vue/tests/utils.ts b/packages/vue/tests/utils.ts new file mode 100644 index 00000000..7face4cc --- /dev/null +++ b/packages/vue/tests/utils.ts @@ -0,0 +1,20 @@ +import { mount } from "@vue/test-utils"; +import { defineComponent, h } from "vue"; + +/** + * Mounts a throwaway component that runs `composable` inside a real Vue + * `setup` context (so lifecycle hooks and effect scopes behave normally) and + * returns its result alongside the mounted wrapper. + */ +export function withSetup(composable: () => T) { + let result!: T; + const wrapper = mount( + defineComponent({ + setup() { + result = composable(); + return () => h("div"); + }, + }) + ); + return { result, wrapper }; +} diff --git a/packages/vue/tsconfig.json b/packages/vue/tsconfig.json new file mode 100644 index 00000000..1e26c09b --- /dev/null +++ b/packages/vue/tsconfig.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "tsconfig/base.json", + "compilerOptions": { + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "target": "ESNext" + }, + "include": ["src", "tests"] +} diff --git a/packages/vue/tsup.config.ts b/packages/vue/tsup.config.ts new file mode 100644 index 00000000..d1994763 --- /dev/null +++ b/packages/vue/tsup.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + dts: true, + clean: true, + format: ["cjs", "esm"], + sourcemap: true, + treeshake: true, +}); diff --git a/packages/vue/vitest.config.ts b/packages/vue/vitest.config.ts new file mode 100644 index 00000000..ec3092d5 --- /dev/null +++ b/packages/vue/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globals: true, + environment: "jsdom", + setupFiles: ["./tests/setup.ts"], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69fa896e..9acc93aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 2.26.1 prettier: specifier: latest - version: 3.5.2 + version: 3.9.4 turbo: specifier: ^2.0.4 version: 2.0.4 @@ -50,7 +50,7 @@ importers: version: 18.2.4 next: specifier: 14.1.1 - version: 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) + version: 14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) next-plausible: specifier: ^3.11.3 version: 3.11.3(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) @@ -117,7 +117,7 @@ importers: version: 4.5.1(monaco-editor@0.39.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@next-auth/prisma-adapter': specifier: 1.0.5 - version: 1.0.5(@prisma/client@5.19.0(prisma@5.19.0))(next-auth@4.22.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))(nodemailer@6.9.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)) + version: 1.0.5(@prisma/client@5.19.0(prisma@5.19.0))(next-auth@4.22.1(next@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(nodemailer@6.9.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)) '@next/mdx': specifier: 14.0.4 version: 14.0.4(@mdx-js/loader@3.0.0(webpack@5.92.1))(@mdx-js/react@3.0.0(@types/react@18.0.14)(react@18.2.0)) @@ -126,7 +126,7 @@ importers: version: 13.3.0 '@openpanel/nextjs': specifier: ^1.0.3 - version: 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) + version: 1.0.3(next@14.1.1(@babel/core@7.24.9)(@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) '@prisma/client': specifier: 5.19.0 version: 5.19.0(prisma@5.19.0) @@ -204,7 +204,7 @@ importers: version: 0.0.3 '@sentry/nextjs': specifier: ^8 - version: 8.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(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)(webpack@5.92.1) + version: 8.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@14.1.1(@babel/core@7.24.9)(@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)(webpack@5.92.1) '@stripe/stripe-js': specifier: ^1.52.1 version: 1.54.0 @@ -234,7 +234,7 @@ importers: version: 10.30.0(@trpc/server@10.30.0) '@trpc/next': specifier: ^10.19.1 - version: 10.30.0(@tanstack/react-query@4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.30.0(@trpc/server@10.30.0))(@trpc/react-query@10.30.0(@tanstack/react-query@4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.30.0(@trpc/server@10.30.0))(@trpc/server@10.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/server@10.30.0)(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) + version: 10.30.0(@tanstack/react-query@4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.30.0(@trpc/server@10.30.0))(@trpc/react-query@10.30.0(@tanstack/react-query@4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.30.0(@trpc/server@10.30.0))(@trpc/server@10.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/server@10.30.0)(next@14.1.1(@babel/core@7.24.9)(@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) '@trpc/react-query': specifier: ^10.19.1 version: 10.30.0(@tanstack/react-query@4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.30.0(@trpc/server@10.30.0))(@trpc/server@10.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -312,31 +312,31 @@ importers: version: 2.1.3 next: specifier: 14.1.1 - version: 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) + version: 14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) next-auth: specifier: 4.22.1 - version: 4.22.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))(nodemailer@6.9.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 4.22.1(next@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(nodemailer@6.9.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) next-plausible: specifier: ^3.12.0 - version: 3.12.0(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) + version: 3.12.0(next@14.1.1(@babel/core@7.24.9)(@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-seo: specifier: ^5.15.0 - version: 5.15.0(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) + version: 5.15.0(next@14.1.1(@babel/core@7.24.9)(@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-sitemap: specifier: ^3.1.55 - 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)) + version: 3.1.55(@next/env@14.2.4)(next@14.1.1(@babel/core@7.24.9)(@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.9)(@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)) + version: 2.1.2(next@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8)) nodemailer: specifier: ^6.9.1 version: 6.9.3 nuqs: specifier: ^1.17.8 - version: 1.17.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)) + version: 1.17.8(next@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8)) octokit: specifier: ^4.0.2 version: 4.0.2 @@ -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.16)(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.16)(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.16)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.16)(sass@1.77.8)(svelte@3.59.1) tslib: specifier: ^2.5.0 version: 2.5.3 @@ -812,7 +812,7 @@ importers: version: 0.49.3(encoding@0.1.13)(typescript@5.5.4) next: specifier: '>=13' - version: 13.4.5(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) + version: 13.4.5(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) node-fetch: specifier: ^3.3.0 version: 3.3.1 @@ -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.16)(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.16)(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.16)(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.16)(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.16)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.16)(sass@1.77.8)(svelte@3.59.1) tslib: specifier: ^2.5.0 version: 2.5.3 @@ -1108,6 +1108,49 @@ importers: packages/tsconfig: {} + packages/vue: + dependencies: + '@tryabby/core': + specifier: workspace:* + version: link:../core + js-cookie: + specifier: ^3.0.5 + version: 3.0.5 + devDependencies: + '@types/js-cookie': + specifier: ^3.0.3 + version: 3.0.3 + '@vue/test-utils': + specifier: ^2.4.6 + version: 2.4.11(@vue/compiler-dom@3.5.39)(@vue/server-renderer@3.5.39(vue@3.5.39(typescript@5.5.4)))(vue@3.5.39(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.16)(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.4.0 + version: 3.5.39(typescript@5.5.4) + packages: '@adobe/css-tools@4.2.0': @@ -1582,6 +1625,10 @@ packages: resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + 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 +1637,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 +1707,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 +1717,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + 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 +2724,10 @@ packages: resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -2892,7 +2947,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 +3805,9 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.18': resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} @@ -3845,7 +3903,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 +4353,9 @@ packages: resolution: {integrity: sha512-TUkJLtI163Bz5+JK0O+zDkQpn4gKwN+BovclUvCj6pI/6RXrFqQvUMRS2M+Rt8Rv0qR3wjoMoOPmpJKeOh0nBg==} engines: {node: '>= 18'} + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@open-draft/until@1.0.3': resolution: {integrity: sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==} @@ -4671,8 +4732,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 +5222,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 @@ -5213,34 +5274,42 @@ packages: '@react-email/button@0.0.5': resolution: {integrity: sha512-k8yDJ5b9gFriy0543qR8zI2tyOBvob7YwaPxfVorDH0ajPucocjbzymopzxyHU13LMafdYbId/kPjoANwdSr+g==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@react-email/container@0.0.5': resolution: {integrity: sha512-zEPBl81MlOZXHYYpexyUCyQogtT6II2PmAJ8Y7hQ8xmNvdiPKdcIneCGqtYLYbBPATP9pGs4JNdL0SOG1BIqvw==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@react-email/head@0.0.3': resolution: {integrity: sha512-4V5GkOCp6MhuTSxqADtfl7zx6Ku6/fDR32DCV6XDRhBhr5TRwiPV/aOAf2eIWzOwLcLkrTMd4O0BPf97+uBaCA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@react-email/hr@0.0.3': resolution: {integrity: sha512-dTV1jm8q5BBzdjNnAAq8RRInxg7Q0V+9dk+v5zutAYcd85UtkFa6YpS7h8ElhIRUiWCtpy/Bze1V9PiDFQko5Q==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@react-email/html@0.0.3': resolution: {integrity: sha512-NtHzGq38StO95W/rASYy+qdzFEpqUDmEe5RMygxeaV/TTZbtLLRqycwk5B/dxq4DfreZE/GL0vAg6u6sMVLTPA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@react-email/img@0.0.3': resolution: {integrity: sha512-RHO7SpRwNo7+Z2h+RtNP0r4ofKYEwvPnKtCHYL4oIRPu3mEto3JAb4g3tVH3pjiHzY1aqSZUXmbCtZtoqdlCwQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@react-email/link@0.0.3': resolution: {integrity: sha512-ybkITKwNmFAdBYYtxsD5KxQKUhW8ICQmLW29DYi6yS1GUECA2v78noSnZMaTwNP01rOx0I1dqSgn20M/justdg==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@react-email/preview@0.0.3': resolution: {integrity: sha512-vEayk64Sa1m1PhaBte8ovIgbTOG5BHoYMmXsltcFoerPz1ZMY7MJwEGEbhiSh/PGt5CXFOsZEIKZuKIu69lUzQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@react-email/render@0.0.5': resolution: {integrity: sha512-EE9mCvR3lXeZEJaldCEaEc4msCwPQwZfXbhuPVl3SpRsiHiMK0wNm2X39vVpqzCzxrm0wljCoLruT7Klp9DZAw==} @@ -5249,10 +5318,12 @@ packages: '@react-email/section@0.0.4': resolution: {integrity: sha512-AaDejYo9nozfiGMxjGnR+CaC8jnILIowiqPFRVeEQOywS2LrL8iIB4YhrxvFjeaWgZmWqKdYDhq16uXWpi/eEA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@react-email/text@0.0.3': resolution: {integrity: sha512-gDPeps1TKqTuvTGVCmjDziAhzdctrj8NvmUCwIsKRBRzGqKcjXJaSx/wVs4aAHO6erVpuswmkUsdo2d238YN/Q==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@remirror/core-constants@2.0.2': resolution: {integrity: sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==} @@ -6641,6 +6712,7 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@vitejs/plugin-basic-ssl@1.1.0': resolution: {integrity: sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==} @@ -6690,9 +6762,21 @@ packages: '@vue/compiler-core@3.4.24': resolution: {integrity: sha512-vbW/tgbwJYj62N/Ww99x0zhFTkZDTcGh3uwJEuadZ/nF9/xuFMC4693P9r+3sxGXISABpDKvffY5ApH9pmdd1A==} + '@vue/compiler-core@3.5.39': + resolution: {integrity: sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==} + '@vue/compiler-dom@3.4.24': resolution: {integrity: sha512-4XgABML/4cNndVsQndG6BbGN7+EoisDwi3oXNovqL/4jdNhwvP8/rfRMTb6FxkxIxUUtg6AI1/qZvwfSjxJiWA==} + '@vue/compiler-dom@3.5.39': + resolution: {integrity: sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==} + + '@vue/compiler-sfc@3.5.39': + resolution: {integrity: sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==} + + '@vue/compiler-ssr@3.5.39': + resolution: {integrity: sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==} + '@vue/language-core@1.8.27': resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} peerDependencies: @@ -6701,9 +6785,36 @@ packages: typescript: optional: true + '@vue/reactivity@3.5.39': + resolution: {integrity: sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==} + + '@vue/runtime-core@3.5.39': + resolution: {integrity: sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==} + + '@vue/runtime-dom@3.5.39': + resolution: {integrity: sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==} + + '@vue/server-renderer@3.5.39': + resolution: {integrity: sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==} + peerDependencies: + vue: 3.5.39 + '@vue/shared@3.4.24': resolution: {integrity: sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw==} + '@vue/shared@3.5.39': + resolution: {integrity: sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==} + + '@vue/test-utils@2.4.11': + resolution: {integrity: sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==} + 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==} @@ -6799,6 +6910,7 @@ packages: acorn-import-assertions@1.9.0: resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + deprecated: package has been renamed to acorn-import-attributes peerDependencies: acorn: ^8 @@ -7740,6 +7852,7 @@ packages: critters@0.0.24: resolution: {integrity: sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==} + deprecated: Ownership of Critters has moved to the Nuxt team, who will be maintaining the project going forward. If you'd like to keep using Critters, please switch to the actively-maintained fork at https://github.com/danielroe/beasties cron-parser@4.9.0: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} @@ -7796,6 +7909,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 +8392,11 @@ packages: resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==} hasBin: true + editorconfig@1.0.7: + resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==} + engines: {node: '>=14'} + hasBin: true + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -8345,6 +8466,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'} @@ -8812,6 +8937,7 @@ packages: formidable@2.1.2: resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} + deprecated: 'ACTION REQUIRED: SWITCH TO v3 - v1 and v2 are VULNERABLE! v1 is DEPRECATED FOR OVER 2 YEARS! Use formidable@latest or try formidable-mini for fresh projects' forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} @@ -8988,28 +9114,31 @@ packages: glob@10.2.7: resolution: {integrity: sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==} engines: {node: '>=16 || 14 >=14.17'} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} engines: {node: '>=16 || 14 >=14.17'} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} @@ -9429,6 +9558,7 @@ packages: intersection-observer@0.12.2: resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} + deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019. invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} @@ -9882,6 +10012,11 @@ packages: engines: {node: '>=12'} hasBin: true + js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + js-cookie@3.0.5: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} @@ -10162,6 +10297,7 @@ packages: lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. lodash.isarguments@3.1.0: resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} @@ -10261,6 +10397,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'} @@ -10884,6 +11023,11 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.15: + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} + 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} @@ -10978,6 +11122,7 @@ packages: next@13.4.5: resolution: {integrity: sha512-pfNsRLVM9e5Y1/z02VakJRfD6hMQkr24FaN2xc9GbcZDBxoOgiNAViSg5cXwlWCoMhtm4U315D7XYhgOr96Q3Q==} engines: {node: '>=16.8.0'} + deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details. hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -10996,6 +11141,7 @@ packages: next@14.1.1: resolution: {integrity: sha512-McrGJqlGSHeaz2yTRPkEucxQKe5Zq7uPwyeHNmJaZNY4wx9E9QdxmTp310agFRoMuIYgQrCrT3petg13fSVOww==} engines: {node: '>=18.17.0'} + deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details. hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -11065,6 +11211,7 @@ packages: node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead node-fetch-native@1.2.0: resolution: {integrity: sha512-5IAMBTl9p6PaAjYCnMv5FmqIF6GcZnawAVnzaCG0rX2aYZJ4CxEkZNtVPuTRug7fL7wyM5BQYTlAzcyMPi6oTQ==} @@ -11569,6 +11716,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 +11885,10 @@ packages: resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.16: + resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} + engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -11793,8 +11947,8 @@ packages: engines: {node: '>=14'} hasBin: true - prettier@3.5.2: - resolution: {integrity: sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==} + prettier@3.9.4: + resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==} engines: {node: '>=14'} hasBin: true @@ -12198,6 +12352,7 @@ packages: recharts@2.12.7: resolution: {integrity: sha512-hlLJMhPQfv4/3NBSAyq3gzGg4h2v69RJh6KU7b3pXYNNAELs9kEoXOjbkxdXpALqKBoVmVptGfLpxdaVYqjmXQ==} engines: {node: '>=14'} + deprecated: 1.x and 2.x branches are no longer active. Bump to Recharts v3 to receive latest features and bugfixes. See https://github.com/recharts/recharts/wiki/3.0-migration-guide peerDependencies: react: ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 @@ -12771,6 +12926,10 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + source-map-loader@5.0.0: resolution: {integrity: sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==} engines: {node: '>= 18.12.0'} @@ -12791,6 +12950,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==} @@ -13015,7 +13175,7 @@ packages: superagent@8.1.2: resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net + deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net superjson@1.9.1: resolution: {integrity: sha512-oT3HA2nPKlU1+5taFgz/HDy+GEaY+CWEbLzaRJVD4gZ7zMVVC4GDNFdgvAZt6/VuIk6D2R7RtPAiCHwmdzlMmg==} @@ -13024,6 +13184,7 @@ packages: supertest@6.3.3: resolution: {integrity: sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==} engines: {node: '>=6.4.0'} + deprecated: Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net supports-color@4.5.0: resolution: {integrity: sha512-ycQR/UbvI9xIlEdQT1TQqwoXtEldExbCEAJgRo5YXlmSKjv6ThHnP9/vwGa1gr19Gfw+LkFd7KqYMhzrRC5JYw==} @@ -13151,10 +13312,12 @@ packages: tar@6.1.15: resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me telejson@7.1.0: resolution: {integrity: sha512-jFJO4P5gPebZAERPkJsqMAQ0IMA1Hi0AoSfxpnUaV6j6R2SZqlpkbS20U6dEUtA3RUYt2Ak/mTlkQzHH9Rv/hA==} @@ -13369,6 +13532,7 @@ packages: tsconfck@3.1.0: resolution: {integrity: sha512-CMjc5zMnyAjcS9sPLytrbFmj89st2g+JYtY/c02ug4Q+CZaAtCgbyviI0n1YvjZE/pzoc6FbNsINS13DOL1B9w==} engines: {node: ^18 || >=20} + deprecated: unmaintained hasBin: true peerDependencies: typescript: ^5.0.0 @@ -13775,14 +13939,17 @@ packages: uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true uuid@9.0.0: resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true uvu@0.5.6: @@ -13964,6 +14131,9 @@ packages: vscode-textmate@8.0.0: resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} + vue-component-type-helpers@3.3.6: + resolution: {integrity: sha512-FkljacAwJ9BUoSUdpFe3VDy0sGigNlTH9+2zcXUWmZOjN8swiCkl3t48wOJun0OsUd2cEIda1l04tsxMiKIIrQ==} + vue-template-compiler@2.7.16: resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} @@ -13973,6 +14143,14 @@ packages: peerDependencies: typescript: '*' + vue@3.5.39: + resolution: {integrity: sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} @@ -14046,7 +14224,7 @@ packages: engines: {node: '>= 18.12.0'} hasBin: true peerDependencies: - webpack: 5.93.0 + webpack: ^5.0.0 webpack-cli: '*' peerDependenciesMeta: webpack: @@ -14099,6 +14277,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 +14887,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 +14927,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 +14995,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 +15024,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 +15187,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 +15196,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 +15204,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: @@ -15036,7 +15215,7 @@ snapshots: '@babel/helper-module-imports@7.22.15': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-module-imports@7.22.5': dependencies: @@ -15142,11 +15321,11 @@ 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: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-plugin-utils@7.22.5': {} @@ -15160,7 +15339,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 +15349,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 +15369,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 @@ -15205,7 +15384,7 @@ snapshots: '@babel/helper-simple-access@7.22.5': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-simple-access@7.24.7': dependencies: @@ -15216,7 +15395,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: @@ -15227,7 +15406,7 @@ snapshots: '@babel/helper-split-export-declaration@7.22.6': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-split-export-declaration@7.24.7': dependencies: @@ -15239,10 +15418,14 @@ snapshots: '@babel/helper-string-parser@7.24.8': {} + '@babel/helper-string-parser@7.29.7': {} + '@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 +15439,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 +15463,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 +15511,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 +15519,10 @@ snapshots: dependencies: '@babel/types': 7.25.2 + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + '@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 +17240,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 +17269,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 +17284,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 +17299,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 +17363,11 @@ snapshots: '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@bcoe/v8-coverage@0.2.3': {} '@biomejs/biome@1.9.4': @@ -18088,6 +18276,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.18': dependencies: '@jridgewell/resolve-uri': 3.1.0 @@ -18383,10 +18573,10 @@ snapshots: pump: 3.0.0 tar-fs: 2.1.1 - '@next-auth/prisma-adapter@1.0.5(@prisma/client@5.19.0(prisma@5.19.0))(next-auth@4.22.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))(nodemailer@6.9.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))': + '@next-auth/prisma-adapter@1.0.5(@prisma/client@5.19.0(prisma@5.19.0))(next-auth@4.22.1(next@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(nodemailer@6.9.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))': dependencies: '@prisma/client': 5.19.0(prisma@5.19.0) - next-auth: 4.22.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))(nodemailer@6.9.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + next-auth: 4.22.1(next@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(nodemailer@6.9.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@next/bundle-analyzer@13.3.4': dependencies: @@ -18691,12 +18881,14 @@ snapshots: '@octokit/request-error': 6.1.4 '@octokit/webhooks-methods': 5.1.0 + '@one-ini/wasm@0.1.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)': + '@openpanel/nextjs@1.0.3(next@14.1.1(@babel/core@7.24.9)(@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: '@openpanel/web': 1.0.0 - 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: 14.1.1(@babel/core@7.24.9)(@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 react-dom: 18.2.0(react@18.2.0) @@ -20100,7 +20292,7 @@ snapshots: '@sentry/types': 8.29.0 '@sentry/utils': 8.29.0 - '@sentry/nextjs@8.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(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)(webpack@5.92.1)': + '@sentry/nextjs@8.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@14.1.1(@babel/core@7.24.9)(@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)(webpack@5.92.1)': dependencies: '@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 @@ -20114,7 +20306,7 @@ snapshots: '@sentry/vercel-edge': 8.29.0 '@sentry/webpack-plugin': 2.22.3(encoding@0.1.13)(webpack@5.92.1) chalk: 3.0.0 - 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: 14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) resolve: 1.22.8 rollup: 3.29.4 stacktrace-parser: 0.1.10 @@ -21237,13 +21429,13 @@ snapshots: dependencies: '@trpc/server': 10.30.0 - '@trpc/next@10.30.0(@tanstack/react-query@4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.30.0(@trpc/server@10.30.0))(@trpc/react-query@10.30.0(@tanstack/react-query@4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.30.0(@trpc/server@10.30.0))(@trpc/server@10.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/server@10.30.0)(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)': + '@trpc/next@10.30.0(@tanstack/react-query@4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.30.0(@trpc/server@10.30.0))(@trpc/react-query@10.30.0(@tanstack/react-query@4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.30.0(@trpc/server@10.30.0))(@trpc/server@10.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/server@10.30.0)(next@14.1.1(@babel/core@7.24.9)(@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: '@tanstack/react-query': 4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@trpc/client': 10.30.0(@trpc/server@10.30.0) '@trpc/react-query': 10.30.0(@tanstack/react-query@4.29.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.30.0(@trpc/server@10.30.0))(@trpc/server@10.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@trpc/server': 10.30.0 - 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: 14.1.1(@babel/core@7.24.9)(@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 react-dom: 18.2.0(react@18.2.0) react-ssr-prepass: 1.5.0(react@18.2.0) @@ -21315,7 +21507,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 +22033,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.39': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.39 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + '@vue/compiler-dom@3.4.24': dependencies: '@vue/compiler-core': 3.4.24 '@vue/shared': 3.4.24 + '@vue/compiler-dom@3.5.39': + dependencies: + '@vue/compiler-core': 3.5.39 + '@vue/shared': 3.5.39 + + '@vue/compiler-sfc@3.5.39': + dependencies: + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.39 + '@vue/compiler-dom': 3.5.39 + '@vue/compiler-ssr': 3.5.39 + '@vue/shared': 3.5.39 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.16 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.39': + dependencies: + '@vue/compiler-dom': 3.5.39 + '@vue/shared': 3.5.39 + '@vue/language-core@1.8.27(typescript@5.5.4)': dependencies: '@volar/language-core': 1.11.1 @@ -21866,8 +22088,41 @@ snapshots: optionalDependencies: typescript: 5.5.4 + '@vue/reactivity@3.5.39': + dependencies: + '@vue/shared': 3.5.39 + + '@vue/runtime-core@3.5.39': + dependencies: + '@vue/reactivity': 3.5.39 + '@vue/shared': 3.5.39 + + '@vue/runtime-dom@3.5.39': + dependencies: + '@vue/reactivity': 3.5.39 + '@vue/runtime-core': 3.5.39 + '@vue/shared': 3.5.39 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.39(vue@3.5.39(typescript@5.5.4))': + dependencies: + '@vue/compiler-ssr': 3.5.39 + '@vue/shared': 3.5.39 + vue: 3.5.39(typescript@5.5.4) + '@vue/shared@3.4.24': {} + '@vue/shared@3.5.39': {} + + '@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.39)(@vue/server-renderer@3.5.39(vue@3.5.39(typescript@5.5.4)))(vue@3.5.39(typescript@5.5.4))': + dependencies: + '@vue/compiler-dom': 3.5.39 + js-beautify: 1.15.4 + vue: 3.5.39(typescript@5.5.4) + vue-component-type-helpers: 3.3.6 + optionalDependencies: + '@vue/server-renderer': 3.5.39(vue@3.5.39(typescript@5.5.4)) + '@web3-storage/multipart-parser@1.0.0': {} '@webassemblyjs/ast@1.12.1': @@ -23018,6 +23273,8 @@ snapshots: csstype@3.1.2: {} + csstype@3.2.3: {} + csv-generate@3.4.3: {} csv-parse@4.16.3: {} @@ -23508,6 +23765,13 @@ snapshots: semver: 5.7.1 sigmund: 1.0.1 + editorconfig@1.0.7: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.4 + semver: 7.6.2 + ee-first@1.1.1: {} ejs@3.1.9: @@ -23575,6 +23839,8 @@ snapshots: entities@4.5.0: {} + entities@7.0.1: {} + env-paths@2.2.1: {} envinfo@7.8.1: {} @@ -25507,6 +25773,14 @@ snapshots: glob: 8.1.0 nopt: 6.0.0 + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.7 + glob: 10.4.5 + js-cookie: 3.0.5 + nopt: 7.2.1 + js-cookie@3.0.5: {} js-levenshtein@1.1.6: {} @@ -26000,6 +26274,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@0.30.8: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -27150,6 +27428,8 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + nanoid@3.3.15: {} + nanoid@3.3.6: {} nanoid@3.3.7: {} @@ -27168,13 +27448,13 @@ snapshots: neo-async@2.6.2: {} - next-auth@4.22.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))(nodemailer@6.9.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + next-auth@4.22.1(next@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8))(nodemailer@6.9.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@babel/runtime': 7.22.5 '@panva/hkdf': 1.1.1 cookie: 0.5.0 jose: 4.14.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: 14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) oauth: 0.9.15 openid-client: 5.4.2 preact: 10.15.1 @@ -27198,42 +27478,42 @@ snapshots: next-plausible@3.11.3(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): 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) + next: 14.1.1(@babel/core@7.24.9)(@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 react-dom: 18.2.0(react@18.2.0) - next-plausible@3.12.0(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): + next-plausible@3.12.0(next@14.1.1(@babel/core@7.24.9)(@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) + next: 14.1.1(@babel/core@7.24.9)(@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 react-dom: 18.2.0(react@18.2.0) - next-seo@5.15.0(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): + next-seo@5.15.0(next@14.1.1(@babel/core@7.24.9)(@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) + next: 14.1.1(@babel/core@7.24.9)(@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 react-dom: 18.2.0(react@18.2.0) 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): 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) + next: 14.1.1(@babel/core@7.24.9)(@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 react-dom: 18.2.0(react@18.2.0) - next-sitemap@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-sitemap@3.1.55(@next/env@14.2.4)(next@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8)): dependencies: '@corex/deepmerge': 4.0.43 '@next/env': 14.2.4 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: 14.1.1(@babel/core@7.24.9)(@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.9)(@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) + next: 14.1.1(@babel/core@7.24.9)(@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 react-dom: 18.2.0(react@18.2.0) - next@13.4.5(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8): + next@13.4.5(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8): dependencies: '@next/env': 13.4.5 '@swc/helpers': 0.5.1 @@ -27242,7 +27522,7 @@ snapshots: postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(@babel/core@7.24.7)(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.24.9)(react@18.2.0) watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: @@ -27261,7 +27541,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - 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@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8): dependencies: '@next/env': 14.1.1 '@swc/helpers': 0.5.2 @@ -27271,7 +27551,7 @@ snapshots: postcss: 8.4.31 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(@babel/core@7.24.7)(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.24.9)(react@18.2.0) optionalDependencies: '@next/swc-darwin-arm64': 14.1.1 '@next/swc-darwin-x64': 14.1.1 @@ -27288,10 +27568,10 @@ snapshots: - '@babel/core' - babel-plugin-macros - nextjs-cors@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)): + nextjs-cors@2.1.2(next@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8)): dependencies: cors: 2.8.5 - 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: 14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) nextra-theme-docs@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))(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-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: @@ -27304,9 +27584,9 @@ snapshots: git-url-parse: 13.1.1 intersection-observer: 0.12.2 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: 14.1.1(@babel/core@7.24.9)(@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.9)(@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) @@ -27327,7 +27607,7 @@ snapshots: gray-matter: 4.0.3 katex: 0.16.9 lodash.get: 4.4.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) + next: 14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) next-mdx-remote: 4.4.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) p-limit: 3.1.0 react: 18.2.0 @@ -27579,10 +27859,10 @@ snapshots: dependencies: boolbase: 1.0.0 - nuqs@1.17.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)): + nuqs@1.17.8(next@14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8)): dependencies: mitt: 3.0.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: 14.1.1(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.8) nwsapi@2.2.5: {} @@ -27968,6 +28248,8 @@ snapshots: picocolors@1.0.1: {} + picocolors@1.1.1: {} + picomatch@2.3.1: {} picomatch@4.0.2: {} @@ -28033,20 +28315,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.16)(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.16 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.16)(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.16 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 +28348,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.16)(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.16 ts-node: 10.9.1(@types/node@22.1.0)(typescript@5.5.4) optional: true @@ -28156,6 +28438,12 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + postcss@8.5.16: + dependencies: + nanoid: 3.3.15 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postgres-array@2.0.0: {} postgres-bytea@1.0.0: {} @@ -28197,7 +28485,7 @@ snapshots: prettier@3.0.0: {} - prettier@3.5.2: {} + prettier@3.9.4: {} pretty-format@27.5.1: dependencies: @@ -29393,6 +29681,8 @@ snapshots: source-map-js@1.2.0: {} + source-map-js@1.2.1: {} + source-map-loader@5.0.0(webpack@5.92.1(esbuild@0.21.5)): dependencies: iconv-lite: 0.6.3 @@ -29626,12 +29916,12 @@ snapshots: dependencies: inline-style-parser: 0.2.2 - styled-jsx@5.1.1(@babel/core@7.24.7)(react@18.2.0): + styled-jsx@5.1.1(@babel/core@7.24.9)(react@18.2.0): dependencies: client-only: 0.0.1 react: 18.2.0 optionalDependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.24.9 stylis@4.3.0: {} @@ -29689,7 +29979,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.16)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.16)(sass@1.77.8)(svelte@3.59.1): dependencies: '@jridgewell/trace-mapping': 0.3.18 chokidar: 3.5.3 @@ -29698,7 +29988,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.16)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.16)(sass@1.77.8)(svelte@3.59.1)(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - '@babel/core' @@ -29712,7 +30002,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.16)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.16)(sass@1.77.8)(svelte@3.59.1): dependencies: '@jridgewell/trace-mapping': 0.3.18 chokidar: 3.5.3 @@ -29721,7 +30011,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.16)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.16)(sass@1.77.8)(svelte@3.59.1)(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - '@babel/core' @@ -29739,7 +30029,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.16)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.16)(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 +30041,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.16 + postcss-load-config: 4.0.1(postcss@8.5.16)(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.16)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)))(postcss@8.5.16)(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 +30058,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.16 + postcss-load-config: 4.0.1(postcss@8.5.16)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)) sass: 1.77.8 typescript: 5.5.4 @@ -30151,7 +30441,7 @@ 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.16)(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 @@ -30161,20 +30451,20 @@ snapshots: 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.16)(ts-node@10.9.1(@types/node@20.3.1)(typescript@5.5.4)) resolve-from: 5.0.0 rollup: 3.25.0 source-map: 0.8.0-beta.0 sucrase: 3.32.0 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.4.41 + postcss: 8.5.16 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.16)(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 @@ -30184,14 +30474,14 @@ snapshots: 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.16)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.5.4)) resolve-from: 5.0.0 rollup: 3.25.0 source-map: 0.8.0-beta.0 sucrase: 3.32.0 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.4.41 + postcss: 8.5.16 typescript: 5.5.4 transitivePeerDependencies: - supports-color @@ -30905,6 +31195,8 @@ snapshots: vscode-textmate@8.0.0: {} + vue-component-type-helpers@3.3.6: {} + vue-template-compiler@2.7.16: dependencies: de-indent: 1.0.2 @@ -30917,6 +31209,16 @@ snapshots: semver: 7.6.0 typescript: 5.5.4 + vue@3.5.39(typescript@5.5.4): + dependencies: + '@vue/compiler-dom': 3.5.39 + '@vue/compiler-sfc': 3.5.39 + '@vue/runtime-dom': 3.5.39 + '@vue/server-renderer': 3.5.39(vue@3.5.39(typescript@5.5.4)) + '@vue/shared': 3.5.39 + optionalDependencies: + typescript: 5.5.4 + w3c-keyname@2.2.8: {} w3c-xmlserializer@4.0.0: From 53fabc47092ca423297db67b9b0b8c4f46ff52ca Mon Sep 17 00:00:00 2001 From: Carlos Fadhel Date: Tue, 7 Jul 2026 03:40:26 -0400 Subject: [PATCH 2/2] =?UTF-8?q?fix(vue):=20address=20review=20=E2=80=94=20?= =?UTF-8?q?gate=20flag/RC=20cookie=20writes=20on=20consent,=20SSR-guard=20?= =?UTF-8?q?reset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Apply `disableByDefault` to the flag and remote-config cookie-write adapters (previously only the AB-test adapter was gated), so no cookies are written when cookie consent is disabled - Guard `getABResetFunction` against SSR (js-cookie needs `document`) - Add a test asserting the composable's subscription is torn down when the effect scope is disposed (onScopeDispose) Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/vue/src/createAbby.ts | 5 +++-- packages/vue/tests/featureFlags.test.ts | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/vue/src/createAbby.ts b/packages/vue/src/createAbby.ts index d0b3ba59..43a0f7c4 100644 --- a/packages/vue/src/createAbby.ts +++ b/packages/vue/src/createAbby.ts @@ -89,7 +89,7 @@ export function createAbby< return FlagStorageService.get(config.projectId, key); }, set: (key: string, value: string) => { - if (!isBrowser) return; + if (!isBrowser || config.cookies?.disableByDefault) return; FlagStorageService.set(config.projectId, key, value); }, }, @@ -99,7 +99,7 @@ export function createAbby< return RemoteConfigStorageService.get(config.projectId, key); }, set: (key: string, value: string) => { - if (!isBrowser) return; + if (!isBrowser || config.cookies?.disableByDefault) return; RemoteConfigStorageService.set(config.projectId, key, value); }, } @@ -257,6 +257,7 @@ export function createAbby< */ const getABResetFunction = (name: K) => { return () => { + if (!isBrowser) return; TestStorageService.remove(config.projectId, name as string); }; }; diff --git a/packages/vue/tests/featureFlags.test.ts b/packages/vue/tests/featureFlags.test.ts index 64a236ee..42f8f95a 100644 --- a/packages/vue/tests/featureFlags.test.ts +++ b/packages/vue/tests/featureFlags.test.ts @@ -29,6 +29,22 @@ describe("useFeatureFlag", () => { expect(result.value).toBe(true); }); + + it("tears down its subscription when the scope is disposed", async () => { + const instance = createInstance(); + const { result, wrapper } = withSetup(() => + instance.useFeatureFlag("flag1") + ); + + // unmounting disposes the effect scope -> onScopeDispose unsubscribes + wrapper.unmount(); + + await instance.__abby__.loadProjectData(); + await flushPromises(); + + // the ref no longer tracks changes after the scope was cleaned up + expect(result.value).toBe(false); + }); }); describe("getFeatureFlagValue", () => {