From c8af175adbe4b1cae1bfd059d314df9a07573136 Mon Sep 17 00:00:00 2001 From: Neil Agarwal Date: Wed, 5 Feb 2025 09:12:32 +0530 Subject: [PATCH 1/7] feat: self hosted api plugin --- plugins/api/package.json | 52 +++++++++++++++++ plugins/api/rslib.config.ts | 14 +++++ plugins/api/src/api.ts | 95 ++++++++++++++++++++++++++++++++ plugins/api/src/index.ts | 1 + plugins/api/tsconfig.json | 8 +++ plugins/plugin-core/src/types.ts | 8 ++- 6 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 plugins/api/package.json create mode 100644 plugins/api/rslib.config.ts create mode 100644 plugins/api/src/api.ts create mode 100644 plugins/api/src/index.ts create mode 100644 plugins/api/tsconfig.json diff --git a/plugins/api/package.json b/plugins/api/package.json new file mode 100644 index 00000000..210c3f19 --- /dev/null +++ b/plugins/api/package.json @@ -0,0 +1,52 @@ +{ + "name": "@hot-updater/api", + "version": "0.6.5", + "type": "module", + "description": "React Native OTA solution for self-hosted", + "sideEffects": false, + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "files": [ + "dist", + "package.json" + ], + "keywords": [ + "react-native", + "react-native-code-push", + "code-push", + "eas", + "eas-update", + "expo", + "expo-update", + "self-hosted" + ], + "license": "MIT", + "scripts": { + "build": "rslib build", + "test:type": "tsc --noEmit" + }, + "repository": "https://github.com/gronxb/hot-updater", + "author": "gronxb (https://github.com/gronxb)", + "bugs": { + "url": "https://github.com/gronxb/hot-updater/issues" + }, + "homepage": "https://github.com/gronxb/hot-updater#readme", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@hot-updater/core": "0.6.5" + }, + "devDependencies": { + "semver": "^7.6.3", + "@types/node": "^20.9.4", + "@types/semver": "^7.5.8" + } +} diff --git a/plugins/api/rslib.config.ts b/plugins/api/rslib.config.ts new file mode 100644 index 00000000..cd505720 --- /dev/null +++ b/plugins/api/rslib.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "@rslib/core"; + +export default defineConfig({ + lib: [ + { + format: "esm", + dts: true, + }, + { + format: "cjs", + dts: true, + }, + ], +}); diff --git a/plugins/api/src/api.ts b/plugins/api/src/api.ts new file mode 100644 index 00000000..93f72ac0 --- /dev/null +++ b/plugins/api/src/api.ts @@ -0,0 +1,95 @@ +import type { + BasePluginArgs, + Bundle, + DatabasePlugin, + DatabasePluginHooks, + ApiPluginResponse, +} from "@hot-updater/plugin-core"; + +export interface IApiConfig { + endpoint: string; + headers: Record; +} + +export const api = + (config: IApiConfig, hooks?: DatabasePluginHooks) => + (_: BasePluginArgs): DatabasePlugin => { + const headers = { + "Content-Type": "application/json", + ...config.headers, + }; + + let bundles: Bundle[] = []; + + return { + name: "api", + async commitBundle() { + const response = await fetch(config.endpoint, { + method: "POST", + headers, + body: JSON.stringify(bundles), + }); + + if (!response.ok) { + throw new Error(`API Error: ${response.statusText}`); + } + + const result = (await response.json()) as ApiPluginResponse; + if (!result.success) { + throw new Error("Failed to commit bundles"); + } + + hooks?.onDatabaseUpdated?.(); + }, + async updateBundle(targetBundleId: string, newBundle: Partial) { + bundles = await this.getBundles(); + + const targetIndex = bundles.findIndex((u) => u.id === targetBundleId); + if (targetIndex === -1) { + throw new Error("target bundle version not found"); + } + + Object.assign(bundles[targetIndex], newBundle); + }, + async appendBundle(inputBundle: Bundle) { + bundles = await this.getBundles(); + bundles.unshift(inputBundle); + }, + async setBundles(inputBundles: Bundle[]) { + bundles = inputBundles; + }, + async getBundleById(bundleId: string): Bundle | null { + try { + const response = await fetch(`${config.endpoint}/${bundleId}`, { + method: "GET", + headers, + }); + + if (!response.ok) { + return null; + } + + return (await response.json()) as Bundle; + } catch (error) { + return null; + } + }, + async getBundles(refresh = false): Promise { + if (bundles.length > 0 && !refresh) { + return bundles; + } + + const response = await fetch(config.endpoint, { + method: "GET", + headers, + }); + + if (!response.ok) { + throw new Error(`API Error: ${response.statusText}`); + } + + bundles = (await response.json()) as Bundle[]; + return bundles; + }, + }; + }; diff --git a/plugins/api/src/index.ts b/plugins/api/src/index.ts new file mode 100644 index 00000000..d158c576 --- /dev/null +++ b/plugins/api/src/index.ts @@ -0,0 +1 @@ +export * from "./api"; diff --git a/plugins/api/tsconfig.json b/plugins/api/tsconfig.json new file mode 100644 index 00000000..5f89210d --- /dev/null +++ b/plugins/api/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/*", "vitest.config.ts"], + "exclude": ["node_modules"] +} diff --git a/plugins/plugin-core/src/types.ts b/plugins/plugin-core/src/types.ts index 3203894c..e68e5ef0 100644 --- a/plugins/plugin-core/src/types.ts +++ b/plugins/plugin-core/src/types.ts @@ -15,7 +15,7 @@ export interface DatabasePlugin { getBundles: (refresh?: boolean) => Promise; updateBundle: ( targetBundleId: string, - newBundle: Partial, + newBundle: Partial ) => Promise; setBundles: (bundles: Bundle[]) => Promise; appendBundle: (bundles: Bundle) => Promise; @@ -29,6 +29,10 @@ export interface DatabasePluginHooks { onDatabaseUpdated?: () => Promise; } +export interface ApiPluginResponse { + success: boolean; +} + export interface BuildPlugin { build: (args: { platform: Platform }) => Promise<{ buildPath: string; @@ -40,7 +44,7 @@ export interface BuildPlugin { export interface StoragePlugin { uploadBundle: ( bundleId: string, - bundlePath: string, + bundlePath: string ) => Promise<{ fileUrl: string; }>; From 7554835f8400a0759ae0f825353c1d004f4ba43b Mon Sep 17 00:00:00 2001 From: Neil Agarwal Date: Wed, 5 Feb 2025 09:37:41 +0530 Subject: [PATCH 2/7] fix: return type of bundle --- plugins/api/src/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/api/src/api.ts b/plugins/api/src/api.ts index 93f72ac0..75032176 100644 --- a/plugins/api/src/api.ts +++ b/plugins/api/src/api.ts @@ -58,7 +58,7 @@ export const api = async setBundles(inputBundles: Bundle[]) { bundles = inputBundles; }, - async getBundleById(bundleId: string): Bundle | null { + async getBundleById(bundleId: string): Promise { try { const response = await fetch(`${config.endpoint}/${bundleId}`, { method: "GET", From 889801a87e3ff6e9a46a090a4f4411a36a5b4456 Mon Sep 17 00:00:00 2001 From: Neil Agarwal Date: Thu, 20 Feb 2025 00:15:49 +0530 Subject: [PATCH 3/7] feat: modular endpoints --- plugins/api/src/api.ts | 95 ----------- plugins/api/src/index.ts | 1 - plugins/{api => standalone}/package.json | 5 +- plugins/{api => standalone}/rslib.config.ts | 0 plugins/standalone/src/index.ts | 2 + .../standalone/src/standaloneRepository.ts | 152 ++++++++++++++++++ plugins/standalone/src/types.ts | 10 ++ plugins/{api => standalone}/tsconfig.json | 0 pnpm-lock.yaml | 19 +++ 9 files changed, 186 insertions(+), 98 deletions(-) delete mode 100644 plugins/api/src/api.ts delete mode 100644 plugins/api/src/index.ts rename plugins/{api => standalone}/package.json (90%) rename plugins/{api => standalone}/rslib.config.ts (100%) create mode 100644 plugins/standalone/src/index.ts create mode 100644 plugins/standalone/src/standaloneRepository.ts create mode 100644 plugins/standalone/src/types.ts rename plugins/{api => standalone}/tsconfig.json (100%) diff --git a/plugins/api/src/api.ts b/plugins/api/src/api.ts deleted file mode 100644 index 75032176..00000000 --- a/plugins/api/src/api.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { - BasePluginArgs, - Bundle, - DatabasePlugin, - DatabasePluginHooks, - ApiPluginResponse, -} from "@hot-updater/plugin-core"; - -export interface IApiConfig { - endpoint: string; - headers: Record; -} - -export const api = - (config: IApiConfig, hooks?: DatabasePluginHooks) => - (_: BasePluginArgs): DatabasePlugin => { - const headers = { - "Content-Type": "application/json", - ...config.headers, - }; - - let bundles: Bundle[] = []; - - return { - name: "api", - async commitBundle() { - const response = await fetch(config.endpoint, { - method: "POST", - headers, - body: JSON.stringify(bundles), - }); - - if (!response.ok) { - throw new Error(`API Error: ${response.statusText}`); - } - - const result = (await response.json()) as ApiPluginResponse; - if (!result.success) { - throw new Error("Failed to commit bundles"); - } - - hooks?.onDatabaseUpdated?.(); - }, - async updateBundle(targetBundleId: string, newBundle: Partial) { - bundles = await this.getBundles(); - - const targetIndex = bundles.findIndex((u) => u.id === targetBundleId); - if (targetIndex === -1) { - throw new Error("target bundle version not found"); - } - - Object.assign(bundles[targetIndex], newBundle); - }, - async appendBundle(inputBundle: Bundle) { - bundles = await this.getBundles(); - bundles.unshift(inputBundle); - }, - async setBundles(inputBundles: Bundle[]) { - bundles = inputBundles; - }, - async getBundleById(bundleId: string): Promise { - try { - const response = await fetch(`${config.endpoint}/${bundleId}`, { - method: "GET", - headers, - }); - - if (!response.ok) { - return null; - } - - return (await response.json()) as Bundle; - } catch (error) { - return null; - } - }, - async getBundles(refresh = false): Promise { - if (bundles.length > 0 && !refresh) { - return bundles; - } - - const response = await fetch(config.endpoint, { - method: "GET", - headers, - }); - - if (!response.ok) { - throw new Error(`API Error: ${response.statusText}`); - } - - bundles = (await response.json()) as Bundle[]; - return bundles; - }, - }; - }; diff --git a/plugins/api/src/index.ts b/plugins/api/src/index.ts deleted file mode 100644 index d158c576..00000000 --- a/plugins/api/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./api"; diff --git a/plugins/api/package.json b/plugins/standalone/package.json similarity index 90% rename from plugins/api/package.json rename to plugins/standalone/package.json index 210c3f19..3362b7fb 100644 --- a/plugins/api/package.json +++ b/plugins/standalone/package.json @@ -1,5 +1,5 @@ { - "name": "@hot-updater/api", + "name": "@hot-updater/standalone", "version": "0.6.5", "type": "module", "description": "React Native OTA solution for self-hosted", @@ -42,7 +42,8 @@ "access": "public" }, "dependencies": { - "@hot-updater/core": "0.6.5" + "@hot-updater/core": "0.6.5", + "@hot-updater/plugin-core": "0.6.5" }, "devDependencies": { "semver": "^7.6.3", diff --git a/plugins/api/rslib.config.ts b/plugins/standalone/rslib.config.ts similarity index 100% rename from plugins/api/rslib.config.ts rename to plugins/standalone/rslib.config.ts diff --git a/plugins/standalone/src/index.ts b/plugins/standalone/src/index.ts new file mode 100644 index 00000000..14381602 --- /dev/null +++ b/plugins/standalone/src/index.ts @@ -0,0 +1,2 @@ +export { standaloneRepository } from './standaloneRepository'; +export type { StandaloneRepositoryConfig } from './standaloneRepository'; diff --git a/plugins/standalone/src/standaloneRepository.ts b/plugins/standalone/src/standaloneRepository.ts new file mode 100644 index 00000000..d8e1a482 --- /dev/null +++ b/plugins/standalone/src/standaloneRepository.ts @@ -0,0 +1,152 @@ +import type { + BasePluginArgs, + Bundle, + DatabasePlugin, + DatabasePluginHooks, +} from "@hot-updater/plugin-core"; +import type { Routes, RouteConfig } from "./types"; + +const defaultRoutes: Routes = { + upsert: () => ({ + path: "/bundles", + }), + list: () => ({ + path: "/bundles", + headers: { "Cache-Control": "no-cache" }, + }), + retrieve: (bundleId: string) => ({ + path: `/bundles/${bundleId}`, + headers: { Accept: "application/json" }, + }), +}; + +const createRoute = ( + defaultRoute: RouteConfig, + customRoute?: Partial +): RouteConfig => ({ + path: customRoute?.path ?? defaultRoute.path, + headers: { + ...defaultRoute.headers, + ...customRoute?.headers, + }, +}); + +export interface StandaloneRepositoryConfig { + baseUrl: string; + commonHeaders?: Record; + routes?: Routes; +} + +export const standaloneRepository = + (config: StandaloneRepositoryConfig, hooks?: DatabasePluginHooks) => + (_: BasePluginArgs): DatabasePlugin => { + const routes: Routes = { + upsert: () => + createRoute(defaultRoutes.upsert(), config.routes?.upsert?.()), + list: () => createRoute(defaultRoutes.list(), config.routes?.list?.()), + retrieve: (bundleId) => + createRoute( + defaultRoutes.retrieve(bundleId), + config.routes?.retrieve?.(bundleId) + ), + }; + + const getHeaders = (routeHeaders?: Record) => ({ + "Content-Type": "application/json", + ...config.commonHeaders, + ...routeHeaders, + }); + + let bundles: Bundle[] = []; + let lastCommittedBundles: Bundle[] = []; + + const getChangedBundles = () => { + return bundles.filter((bundle, index) => { + const lastCommitted = lastCommittedBundles[index]; + return ( + !lastCommitted || + JSON.stringify(bundle) !== JSON.stringify(lastCommitted) + ); + }); + }; + + return { + name: "standalone-repository", + async commitBundle() { + const changedBundles = getChangedBundles(); + if (changedBundles.length === 0) return; + + const { path, headers: routeHeaders } = routes.upsert(); + const response = await fetch(`${config.baseUrl}${path}`, { + method: "POST", + headers: getHeaders(routeHeaders), + body: JSON.stringify(changedBundles), + }); + + if (!response.ok) { + throw new Error(`API Error: ${response.statusText}`); + } + + const result = (await response.json()) as { success: boolean }; + if (!result.success) { + throw new Error("Failed to commit bundles"); + } + + lastCommittedBundles = [...bundles]; + hooks?.onDatabaseUpdated?.(); + }, + async updateBundle(targetBundleId: string, newBundle: Partial) { + bundles = await this.getBundles(); + + const targetIndex = bundles.findIndex((u) => u.id === targetBundleId); + if (targetIndex === -1) { + throw new Error("target bundle version not found"); + } + + Object.assign(bundles[targetIndex], newBundle); + }, + async appendBundle(inputBundle: Bundle) { + bundles = await this.getBundles(); + bundles.unshift(inputBundle); + }, + async setBundles(inputBundles: Bundle[]) { + bundles = inputBundles; + }, + async getBundleById(bundleId: string): Promise { + try { + const { path, headers: routeHeaders } = routes.retrieve(bundleId); + const response = await fetch(`${config.baseUrl}${path}`, { + method: "GET", + headers: getHeaders(routeHeaders), + }); + + if (!response.ok) { + return null; + } + + return (await response.json()) as Bundle; + } catch (error) { + return null; + } + }, + async getBundles(refresh = false): Promise { + if (bundles.length > 0 && !refresh) { + return bundles; + } + + const { path, headers: routeHeaders } = routes.list(); + const response = await fetch(`${config.baseUrl}${path}`, { + method: "GET", + headers: getHeaders(routeHeaders), + }); + + if (!response.ok) { + throw new Error(`API Error: ${response.statusText}`); + } + + bundles = (await response.json()) as Bundle[]; + lastCommittedBundles = [...bundles]; + return bundles; + }, + }; + }; diff --git a/plugins/standalone/src/types.ts b/plugins/standalone/src/types.ts new file mode 100644 index 00000000..a7e1113d --- /dev/null +++ b/plugins/standalone/src/types.ts @@ -0,0 +1,10 @@ +export interface RouteConfig { + path: string; + headers?: Record; +} + +export interface Routes { + upsert: () => RouteConfig; + list: () => RouteConfig; + retrieve: (bundleId: string) => RouteConfig; +} diff --git a/plugins/api/tsconfig.json b/plugins/standalone/tsconfig.json similarity index 100% rename from plugins/api/tsconfig.json rename to plugins/standalone/tsconfig.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78d686e6..5b06d7c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -836,6 +836,25 @@ importers: specifier: ^9.1.3 version: 9.1.3 + plugins/standalone: + dependencies: + '@hot-updater/core': + specifier: workspace:* + version: link:../../packages/core + '@hot-updater/plugin-core': + specifier: workspace:* + version: link:../plugin-core + devDependencies: + '@types/node': + specifier: ^20.9.4 + version: 20.16.10 + '@types/semver': + specifier: ^7.5.8 + version: 7.5.8 + semver: + specifier: ^7.6.3 + version: 7.6.3 + plugins/supabase: dependencies: '@hot-updater/core': From 1821a55cee9387c600c7698c4092d35577e0d2ae Mon Sep 17 00:00:00 2001 From: Neil Agarwal Date: Thu, 20 Feb 2025 00:46:01 +0530 Subject: [PATCH 4/7] fix: use the lastChangedIds approach to upsert --- plugins/plugin-core/src/types.ts | 8 ++---- plugins/standalone/src/index.ts | 3 +- .../standalone/src/standaloneRepository.ts | 28 +++++++++---------- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/plugins/plugin-core/src/types.ts b/plugins/plugin-core/src/types.ts index e68e5ef0..3203894c 100644 --- a/plugins/plugin-core/src/types.ts +++ b/plugins/plugin-core/src/types.ts @@ -15,7 +15,7 @@ export interface DatabasePlugin { getBundles: (refresh?: boolean) => Promise; updateBundle: ( targetBundleId: string, - newBundle: Partial + newBundle: Partial, ) => Promise; setBundles: (bundles: Bundle[]) => Promise; appendBundle: (bundles: Bundle) => Promise; @@ -29,10 +29,6 @@ export interface DatabasePluginHooks { onDatabaseUpdated?: () => Promise; } -export interface ApiPluginResponse { - success: boolean; -} - export interface BuildPlugin { build: (args: { platform: Platform }) => Promise<{ buildPath: string; @@ -44,7 +40,7 @@ export interface BuildPlugin { export interface StoragePlugin { uploadBundle: ( bundleId: string, - bundlePath: string + bundlePath: string, ) => Promise<{ fileUrl: string; }>; diff --git a/plugins/standalone/src/index.ts b/plugins/standalone/src/index.ts index 14381602..303a28bd 100644 --- a/plugins/standalone/src/index.ts +++ b/plugins/standalone/src/index.ts @@ -1,2 +1 @@ -export { standaloneRepository } from './standaloneRepository'; -export type { StandaloneRepositoryConfig } from './standaloneRepository'; +export * from "./standaloneRepository"; diff --git a/plugins/standalone/src/standaloneRepository.ts b/plugins/standalone/src/standaloneRepository.ts index d8e1a482..17c9094c 100644 --- a/plugins/standalone/src/standaloneRepository.ts +++ b/plugins/standalone/src/standaloneRepository.ts @@ -58,22 +58,18 @@ export const standaloneRepository = }); let bundles: Bundle[] = []; - let lastCommittedBundles: Bundle[] = []; - - const getChangedBundles = () => { - return bundles.filter((bundle, index) => { - const lastCommitted = lastCommittedBundles[index]; - return ( - !lastCommitted || - JSON.stringify(bundle) !== JSON.stringify(lastCommitted) - ); - }); - }; + const changedIds = new Set(); + + function markChanged(id: string) { + changedIds.add(id); + } return { name: "standalone-repository", async commitBundle() { - const changedBundles = getChangedBundles(); + if (changedIds.size === 0) return; + + const changedBundles = bundles.filter((b) => changedIds.has(b.id)); if (changedBundles.length === 0) return; const { path, headers: routeHeaders } = routes.upsert(); @@ -92,7 +88,7 @@ export const standaloneRepository = throw new Error("Failed to commit bundles"); } - lastCommittedBundles = [...bundles]; + changedIds.clear(); hooks?.onDatabaseUpdated?.(); }, async updateBundle(targetBundleId: string, newBundle: Partial) { @@ -104,13 +100,18 @@ export const standaloneRepository = } Object.assign(bundles[targetIndex], newBundle); + markChanged(targetBundleId); }, async appendBundle(inputBundle: Bundle) { bundles = await this.getBundles(); bundles.unshift(inputBundle); + markChanged(inputBundle.id); }, async setBundles(inputBundles: Bundle[]) { bundles = inputBundles; + for (const bundle of inputBundles) { + markChanged(bundle.id); + } }, async getBundleById(bundleId: string): Promise { try { @@ -145,7 +146,6 @@ export const standaloneRepository = } bundles = (await response.json()) as Bundle[]; - lastCommittedBundles = [...bundles]; return bundles; }, }; From 86d6d1712580311bd4415efb8119ed10bd935e62 Mon Sep 17 00:00:00 2001 From: gronxb Date: Thu, 20 Feb 2025 09:48:11 +0900 Subject: [PATCH 5/7] fix: lint --- plugins/standalone/package.json | 6 +++--- plugins/standalone/src/standaloneRepository.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/standalone/package.json b/plugins/standalone/package.json index 3362b7fb..6fd354a1 100644 --- a/plugins/standalone/package.json +++ b/plugins/standalone/package.json @@ -1,6 +1,6 @@ { "name": "@hot-updater/standalone", - "version": "0.6.5", + "version": "0.10.1", "type": "module", "description": "React Native OTA solution for self-hosted", "sideEffects": false, @@ -42,8 +42,8 @@ "access": "public" }, "dependencies": { - "@hot-updater/core": "0.6.5", - "@hot-updater/plugin-core": "0.6.5" + "@hot-updater/core": "0.10.1", + "@hot-updater/plugin-core": "0.10.1" }, "devDependencies": { "semver": "^7.6.3", diff --git a/plugins/standalone/src/standaloneRepository.ts b/plugins/standalone/src/standaloneRepository.ts index 17c9094c..5601d108 100644 --- a/plugins/standalone/src/standaloneRepository.ts +++ b/plugins/standalone/src/standaloneRepository.ts @@ -22,7 +22,7 @@ const defaultRoutes: Routes = { const createRoute = ( defaultRoute: RouteConfig, - customRoute?: Partial + customRoute?: Partial, ): RouteConfig => ({ path: customRoute?.path ?? defaultRoute.path, headers: { @@ -47,7 +47,7 @@ export const standaloneRepository = retrieve: (bundleId) => createRoute( defaultRoutes.retrieve(bundleId), - config.routes?.retrieve?.(bundleId) + config.routes?.retrieve?.(bundleId), ), }; From 1cdf1d1cb121513f1bd978792666e8453b84bb94 Mon Sep 17 00:00:00 2001 From: gronxb Date: Thu, 20 Feb 2025 10:12:59 +0900 Subject: [PATCH 6/7] feat: add test code --- plugins/standalone/package.json | 5 +- .../src/standaloneRepository.spec.ts | 339 ++++++++++++++++++ .../standalone/src/standaloneRepository.ts | 2 +- pnpm-lock.yaml | 333 ++++++++++++++++- 4 files changed, 661 insertions(+), 18 deletions(-) create mode 100644 plugins/standalone/src/standaloneRepository.spec.ts diff --git a/plugins/standalone/package.json b/plugins/standalone/package.json index 6fd354a1..aac78f87 100644 --- a/plugins/standalone/package.json +++ b/plugins/standalone/package.json @@ -46,8 +46,9 @@ "@hot-updater/plugin-core": "0.10.1" }, "devDependencies": { - "semver": "^7.6.3", "@types/node": "^20.9.4", - "@types/semver": "^7.5.8" + "@types/semver": "^7.5.8", + "msw": "^2.7.0", + "semver": "^7.6.3" } } diff --git a/plugins/standalone/src/standaloneRepository.spec.ts b/plugins/standalone/src/standaloneRepository.spec.ts new file mode 100644 index 00000000..1ca5d64d --- /dev/null +++ b/plugins/standalone/src/standaloneRepository.spec.ts @@ -0,0 +1,339 @@ +import type { BasePluginArgs, Bundle } from "@hot-updater/plugin-core"; +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { + type StandaloneRepositoryConfig, + standaloneRepository, +} from "./standaloneRepository"; + +// ─── Default test bundle data ──────────────────────────────── +const DEFAULT_BUNDLE = { + fileUrl: "http://example.com/bundle.zip", + fileHash: "hash", + platform: "ios", + gitCommitHash: null, + message: null, +} as const; + +const testBundles: Bundle[] = [ + { + ...DEFAULT_BUNDLE, + targetAppVersion: "*", + shouldForceUpdate: false, + enabled: true, + id: "00000000-0000-0000-0000-000000000001", + }, +]; + +// ─── MSW Server Setup ──────────────────────────────── +const server = setupServer(); + +beforeAll(() => server.listen()); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +// ─── Default Routes Tests ──────────────────────────────── +describe("Standalone Repository Plugin (Default Routes)", () => { + let repo: ReturnType>; + let onDatabaseUpdated: () => Promise; + const config: StandaloneRepositoryConfig = { + baseUrl: "http://localhost", + }; + + beforeEach(() => { + onDatabaseUpdated = vi.fn(); + repo = standaloneRepository(config, { onDatabaseUpdated })( + {} as BasePluginArgs, + ); + }); + + it("getBundles: GET /bundles fetches bundle list", async () => { + let callCount = 0; + server.use( + http.get("http://localhost/bundles", ({ request }) => { + callCount++; + expect(request.headers.get("Content-Type")).toEqual("application/json"); + expect(request.headers.get("Cache-Control")).toEqual("no-cache"); + return HttpResponse.json(testBundles); + }), + ); + + const bundles = await repo.getBundles(); + expect(bundles).toEqual(testBundles); + expect(callCount).toBe(1); + }); + + it("getBundles: returns cached result when refresh is false", async () => { + let callCount = 0; + server.use( + http.get("http://localhost/bundles", () => { + callCount++; + return HttpResponse.json(testBundles); + }), + ); + + const firstCall = await repo.getBundles(); + const secondCall = await repo.getBundles(); + expect(firstCall).toEqual(testBundles); + expect(secondCall).toEqual(testBundles); + expect(callCount).toBe(1); // caching in effect + }); + + it("getBundles: makes new request when refresh is true", async () => { + let callCount = 0; + server.use( + http.get("http://localhost/bundles", () => { + callCount++; + return HttpResponse.json(testBundles); + }), + ); + + await repo.getBundles(); + const refreshed = await repo.getBundles(true); + expect(refreshed).toEqual(testBundles); + expect(callCount).toBe(2); + }); + + it("getBundleById: GET /bundles/:id retrieves a bundle (success case)", async () => { + server.use( + http.get("http://localhost/bundles/:bundleId", ({ params, request }) => { + const { bundleId } = params; + if (bundleId === testBundles[0].id) { + expect(request.headers.get("Accept")).toEqual("application/json"); + return HttpResponse.json(testBundles[0]); + } + return HttpResponse.error(); + }), + ); + + const bundle = await repo.getBundleById( + "00000000-0000-0000-0000-000000000001", + ); + expect(bundle).toEqual(testBundles[0]); + }); + + it("getBundleById: returns null when retrieval fails", async () => { + server.use( + http.get("http://localhost/bundles/:bundleId", () => { + return HttpResponse.error(); + }), + ); + + const bundle = await repo.getBundleById("non-existent"); + expect(bundle).toBeNull(); + }); + + it("getBundleById: returns null on network error", async () => { + server.use( + http.get("http://localhost/bundles/:bundleId", () => { + throw new Error("Network failure"); + }), + ); + + const bundle = await repo.getBundleById( + "00000000-0000-0000-0000-000000000001", + ); + expect(bundle).toBeNull(); + }); + + it("getBundles: throws error when API returns error", async () => { + server.use( + http.get("http://localhost/bundles", () => { + return new HttpResponse(null, { + status: 500, + statusText: "Internal Server Error", + }); + }), + ); + + await expect(repo.getBundles(true)).rejects.toThrow( + "API Error: Internal Server Error", + ); + }); + + it("updateBundle & commitBundle: updates an existing bundle and commits", async () => { + server.use( + http.get("http://localhost/bundles", () => { + return HttpResponse.json(testBundles); + }), + ); + + await repo.updateBundle("00000000-0000-0000-0000-000000000001", { + enabled: false, + }); + + let postCalled = false; + server.use( + http.post("http://localhost/bundles", async ({ request }) => { + postCalled = true; + const body = (await request.json()) as Bundle[]; + expect(Array.isArray(body)).toBe(true); + expect(body[0].id).toBe("00000000-0000-0000-0000-000000000001"); + expect(body[0].enabled).toBe(false); + return HttpResponse.json({ success: true }); + }), + ); + + await repo.commitBundle(); + expect(postCalled).toBe(true); + expect(onDatabaseUpdated).toHaveBeenCalled(); + }); + + it("updateBundle: throws error if target bundle does not exist", async () => { + server.use( + http.get("http://localhost/bundles", () => { + return HttpResponse.json([]); + }), + ); + + await expect( + repo.updateBundle("non-existent-id", { enabled: false }), + ).rejects.toThrow("target bundle version not found"); + }); + + it("appendBundle & commitBundle: appends a new bundle and commits", async () => { + server.use( + http.get("http://localhost/bundles", () => { + return HttpResponse.json([]); + }), + ); + + const newBundle: Bundle = { + ...DEFAULT_BUNDLE, + targetAppVersion: "1.0.0", + shouldForceUpdate: false, + enabled: true, + id: "00000000-0000-0000-0000-000000000002", + }; + + await repo.appendBundle(newBundle); + + let postCalled = false; + server.use( + http.post("http://localhost/bundles", async ({ request }) => { + postCalled = true; + const body = await request.json(); + expect(body).toEqual([newBundle]); + return HttpResponse.json({ success: true }); + }), + ); + + await repo.commitBundle(); + expect(postCalled).toBe(true); + }); + + it("setBundles & commitBundle: sets the bundle list and commits all bundles", async () => { + await repo.setBundles(testBundles); + + let postCalled = false; + server.use( + http.post("http://localhost/bundles", async ({ request }) => { + postCalled = true; + const body = await request.json(); + expect(body).toEqual(testBundles); + return HttpResponse.json({ success: true }); + }), + ); + + await repo.commitBundle(); + expect(postCalled).toBe(true); + }); + + it("commitBundle: does nothing if there are no changes", async () => { + const spy = vi.spyOn(global, "fetch"); + await repo.commitBundle(); + expect(spy).not.toHaveBeenCalled(); + spy.mockRestore(); + }); + + it("commitBundle: throws exception on API error", async () => { + await repo.setBundles(testBundles); + + server.use( + http.post("http://localhost/bundles", () => { + return new HttpResponse(null, { + status: 500, + statusText: "Internal Server Error", + }); + }), + ); + + await expect(repo.commitBundle()).rejects.toThrow( + "API Error: Internal Server Error", + ); + }); +}); + +// ─── Custom Routes Tests ──────────────────────────────── +describe("Standalone Repository Plugin (Custom Routes)", () => { + let customRepo: ReturnType>; + const customConfig: StandaloneRepositoryConfig = { + baseUrl: "http://localhost/api", + commonHeaders: { Authorization: "Bearer token" }, + routes: { + upsert: () => ({ + path: "/custom/bundles", + headers: { "X-Custom": "upsert" }, + }), + list: () => ({ + path: "/custom/bundles", + headers: { "Cache-Control": "max-age=60" }, + }), + retrieve: (bundleId: string) => ({ + path: `/custom/bundles/${bundleId}`, + headers: { Accept: "application/custom+json" }, + }), + }, + }; + + beforeEach(() => { + customRepo = standaloneRepository(customConfig)({} as BasePluginArgs); + }); + + it("getBundles: uses custom list route and headers", async () => { + server.use( + http.get("http://localhost/api/custom/bundles", ({ request }) => { + expect(request.headers.get("Authorization")).toEqual("Bearer token"); + expect(request.headers.get("Cache-Control")).toEqual("max-age=60"); + return HttpResponse.json(testBundles); + }), + ); + + const bundles = await customRepo.getBundles(); + expect(bundles).toEqual(testBundles); + }); + + it("getBundleById: uses custom retrieve route and headers", async () => { + server.use( + http.get( + "http://localhost/api/custom/bundles/:bundleId", + ({ params, request }) => { + expect(request.headers.get("Authorization")).toEqual("Bearer token"); + expect(request.headers.get("Accept")).toEqual( + "application/custom+json", + ); + const { bundleId } = params; + if (bundleId === testBundles[0].id) { + return HttpResponse.json(testBundles[0]); + } + return HttpResponse.error(); + }, + ), + ); + + const bundle = await customRepo.getBundleById( + "00000000-0000-0000-0000-000000000001", + ); + expect(bundle).toEqual(testBundles[0]); + }); +}); diff --git a/plugins/standalone/src/standaloneRepository.ts b/plugins/standalone/src/standaloneRepository.ts index 5601d108..89598bb6 100644 --- a/plugins/standalone/src/standaloneRepository.ts +++ b/plugins/standalone/src/standaloneRepository.ts @@ -4,7 +4,7 @@ import type { DatabasePlugin, DatabasePluginHooks, } from "@hot-updater/plugin-core"; -import type { Routes, RouteConfig } from "./types"; +import type { RouteConfig, Routes } from "./types"; const defaultRoutes: Routes = { upsert: () => ({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b06d7c8..a2c0b84f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,7 +44,7 @@ importers: version: 5.7.2 vitest: specifier: ^2.1.8 - version: 2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(sass-embedded@1.83.0)(terser@5.31.0) + version: 2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0) docs: devDependencies: @@ -172,7 +172,7 @@ importers: version: 7.26.0 '@callstack/repack': specifier: ^4.0.0 - version: 4.0.0(@react-native-community/cli-debugger-ui@15.0.1)(@react-native-community/cli-server-api@15.0.1)(@react-native-community/cli-types@15.0.1)(@react-native-community/cli@15.0.1(typescript@5.7.2))(encoding@0.1.13)(react-native@0.74.1(@babel/core@7.26.0)(@babel/preset-env@7.23.2(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.2.0))(type-fest@4.23.0)(webpack-dev-server@5.0.4(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)))(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)) + version: 4.0.0(@react-native-community/cli-debugger-ui@15.0.1)(@react-native-community/cli-server-api@15.0.1)(@react-native-community/cli-types@15.0.1)(@react-native-community/cli@15.0.1(typescript@5.7.2))(encoding@0.1.13)(react-native@0.74.1(@babel/core@7.26.0)(@babel/preset-env@7.23.2(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.2.0))(type-fest@4.35.0)(webpack-dev-server@5.0.4(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)))(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)) '@hot-updater/aws': specifier: workspace:* version: link:../../plugins/aws @@ -713,7 +713,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: ^0.6.4 - version: 0.6.7(@cloudflare/workers-types@4.20250124.3)(@vitest/runner@2.1.8)(@vitest/snapshot@2.1.8)(vitest@2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(sass-embedded@1.83.0)(terser@5.31.0)) + version: 0.6.7(@cloudflare/workers-types@4.20250124.3)(@vitest/runner@2.1.8)(@vitest/snapshot@2.1.8)(vitest@2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0)) '@cloudflare/workers-types': specifier: ^4.20250124.3 version: 4.20250124.3 @@ -749,7 +749,7 @@ importers: version: 5.7.2 vitest: specifier: 2.1.8 - version: 2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(sass-embedded@1.83.0)(terser@5.31.0) + version: 2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0) wrangler: specifier: ^3.101.0 version: 3.105.1(@cloudflare/workers-types@4.20250124.3) @@ -851,6 +851,9 @@ importers: '@types/semver': specifier: ^7.5.8 version: 7.5.8 + msw: + specifier: ^2.7.0 + version: 2.7.0(@types/node@20.16.10)(typescript@5.7.2) semver: specifier: ^7.6.3 version: 7.6.3 @@ -2314,6 +2317,15 @@ packages: '@bufbuild/protobuf@2.2.3': resolution: {integrity: sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg==} + '@bundled-es-modules/cookie@2.0.1': + resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} + + '@bundled-es-modules/statuses@1.0.1': + resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + + '@bundled-es-modules/tough-cookie@0.1.6': + resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} + '@callstack/repack-dev-server@4.0.0': resolution: {integrity: sha512-zBeIH694dvO+xHu/s/M360bAtYnygxbmOcmHZDzpytpxU9GLg1D1R+l2KS80zqk9a0W0clpCMVQCCTrYXk52SA==} engines: {node: '>=18'} @@ -2920,6 +2932,37 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead + '@inquirer/confirm@5.1.6': + resolution: {integrity: sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.1.7': + resolution: {integrity: sha512-AA9CQhlrt6ZgiSy6qoAigiA1izOa751ugX6ioSjqgJ+/Gd+tEN/TORk5sUYNjXuHWfW0r1n/a6ak4u/NqHHrtA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.10': + resolution: {integrity: sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==} + engines: {node: '>=18'} + + '@inquirer/type@3.0.4': + resolution: {integrity: sha512-2MNFrDY8jkFYc9Il9DgLsHhMzuHnOYM1+CUYVWbzu9oT0hC7V7EcYvdCKeoll/Fcci04A+ERZ9wcc7cQ8lTkIA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@internationalized/date@3.5.6': resolution: {integrity: sha512-jLxQjefH9VI5P9UQuqB6qNKnvFt1Ky1TPIzHGsIlCi7sZZoMR8SdYbBGRvM0y+Jtb+ez4ieBzmiAUcpmPYpyOw==} @@ -3123,6 +3166,10 @@ packages: '@module-federation/webpack-bundler-runtime@0.8.4': resolution: {integrity: sha512-HggROJhvHPUX7uqBD/XlajGygMNM1DG0+4OAkk8MBQe4a18QzrRNzZt6XQbRTSG4OaEoyRWhQHvYD3Yps405tQ==} + '@mswjs/interceptors@0.37.6': + resolution: {integrity: sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==} + engines: {node: '>=18'} + '@napi-rs/wasm-runtime@0.2.4': resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} @@ -3220,6 +3267,15 @@ packages: '@nx/workspace@20.3.0': resolution: {integrity: sha512-z8NSAo5SiLEMPuwasDvLdCCtaTGdINh1cSZMCom8HeLbT8F7risbR0IlHVqVrKj9FPKqrAIsH+4knVb4dHHCnQ==} + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@oxc-resolver/binding-darwin-arm64@1.12.0': resolution: {integrity: sha512-wYe+dlF8npM7cwopOOxbdNjtmJp17e/xF5c0K2WooQXy5VOh74icydM33+Uh/SZDgwyum09/U1FVCX5GdeQk+A==} cpu: [arm64] @@ -4832,6 +4888,9 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/cosmiconfig@6.0.0': resolution: {integrity: sha512-KxKYXK5K1W+SVj1wjBBlwUs43K2D4iteye+r2ObPPQ7+NVASxSPfTb5H8iPW0bLswapMvaA4YMnxdKx7M4k29A==} deprecated: This is a stub types definition. cosmiconfig provides its own type definitions, so you do not need this installed. @@ -4974,6 +5033,12 @@ packages: '@types/stack-utils@2.0.2': resolution: {integrity: sha512-g7CK9nHdwjK2n0ymT2CW698FuWJRIx+RP6embAzZ2Qi8/ilIrA1Imt2LVSeHUzKvpoi7BhmmQcXz95eS0f2JXw==} + '@types/statuses@2.0.5': + resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -5931,6 +5996,10 @@ packages: resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} engines: {node: '>=6'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} @@ -7115,6 +7184,10 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphql@16.10.0: + resolution: {integrity: sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + gray-matter@4.0.3: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} @@ -7185,6 +7258,9 @@ packages: hastscript@9.0.0: resolution: {integrity: sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw==} + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + hermes-eslint@0.23.1: resolution: {integrity: sha512-DaEpbJobK1KwpTSXrPIKkHs2h+B+RTw2F1g9S70tjtJ14a3zM+2gPVUtc8xyffQqRJ6tPfs+/zRKwV17lwDvqA==} @@ -7562,6 +7638,9 @@ packages: resolution: {integrity: sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==} engines: {node: '>=16'} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} @@ -8677,6 +8756,16 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msw@2.7.0: + resolution: {integrity: sha512-BIodwZ19RWfCbYTxWTUfTXc+sg4OwjCAgxU1ZsgmggX/7S3LdUifsbUPJs61j0rWb19CZRGY5if77duhc0uXzw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + multicast-dns@7.2.5: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} hasBin: true @@ -8685,6 +8774,10 @@ packages: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -8913,6 +9006,9 @@ packages: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + oxc-resolver@1.12.0: resolution: {integrity: sha512-YlaCIArvWNKCWZFRrMjhh2l5jK80eXnpYP+bhRc1J/7cW3TiyEY0ngJo73o/5n8hA3+4yLdTmXLNTQ3Ncz50LQ==} @@ -9365,6 +9461,9 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + pump@3.0.2: resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} @@ -9384,6 +9483,9 @@ packages: engines: {node: '>=0.4.x'} deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -10288,6 +10390,9 @@ packages: stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -10552,6 +10657,10 @@ packages: toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + tough-cookie@5.0.0: resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==} engines: {node: '>=16'} @@ -10682,6 +10791,10 @@ packages: resolution: {integrity: sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==} engines: {node: '>=16'} + type-fest@4.35.0: + resolution: {integrity: sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==} + engines: {node: '>=16'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -10824,6 +10937,10 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -10851,6 +10968,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-sync-external-store@1.2.0: resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: @@ -11317,6 +11437,10 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + yoctocolors@2.1.1: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} @@ -13403,6 +13527,19 @@ snapshots: '@bufbuild/protobuf@2.2.3': {} + '@bundled-es-modules/cookie@2.0.1': + dependencies: + cookie: 0.7.2 + + '@bundled-es-modules/statuses@1.0.1': + dependencies: + statuses: 2.0.1 + + '@bundled-es-modules/tough-cookie@0.1.6': + dependencies: + '@types/tough-cookie': 4.0.5 + tough-cookie: 4.1.4 + '@callstack/repack-dev-server@4.0.0(@react-native-community/cli-debugger-ui@15.0.1)(@react-native-community/cli-server-api@15.0.1)(encoding@0.1.13)': dependencies: '@babel/code-frame': 7.26.2 @@ -13425,11 +13562,11 @@ snapshots: - supports-color - utf-8-validate - '@callstack/repack@4.0.0(@react-native-community/cli-debugger-ui@15.0.1)(@react-native-community/cli-server-api@15.0.1)(@react-native-community/cli-types@15.0.1)(@react-native-community/cli@15.0.1(typescript@5.7.2))(encoding@0.1.13)(react-native@0.74.1(@babel/core@7.26.0)(@babel/preset-env@7.23.2(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.2.0))(type-fest@4.23.0)(webpack-dev-server@5.0.4(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)))(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2))': + '@callstack/repack@4.0.0(@react-native-community/cli-debugger-ui@15.0.1)(@react-native-community/cli-server-api@15.0.1)(@react-native-community/cli-types@15.0.1)(@react-native-community/cli@15.0.1(typescript@5.7.2))(encoding@0.1.13)(react-native@0.74.1(@babel/core@7.26.0)(@babel/preset-env@7.23.2(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.2.0))(type-fest@4.35.0)(webpack-dev-server@5.0.4(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)))(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2))': dependencies: '@callstack/repack-dev-server': 4.0.0(@react-native-community/cli-debugger-ui@15.0.1)(@react-native-community/cli-server-api@15.0.1)(encoding@0.1.13) '@discoveryjs/json-ext': 0.5.7 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.13(react-refresh@0.14.2)(type-fest@4.23.0)(webpack-dev-server@5.0.4(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)))(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.13(react-refresh@0.14.2)(type-fest@4.35.0)(webpack-dev-server@5.0.4(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)))(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)) '@react-native-community/cli': 15.0.1(typescript@5.7.2) '@react-native-community/cli-types': 15.0.1 colorette: 1.4.0 @@ -13482,7 +13619,7 @@ snapshots: dependencies: mime: 3.0.0 - '@cloudflare/vitest-pool-workers@0.6.7(@cloudflare/workers-types@4.20250124.3)(@vitest/runner@2.1.8)(@vitest/snapshot@2.1.8)(vitest@2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(sass-embedded@1.83.0)(terser@5.31.0))': + '@cloudflare/vitest-pool-workers@0.6.7(@cloudflare/workers-types@4.20250124.3)(@vitest/runner@2.1.8)(@vitest/snapshot@2.1.8)(vitest@2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0))': dependencies: '@vitest/runner': 2.1.8 '@vitest/snapshot': 2.1.8 @@ -13492,7 +13629,7 @@ snapshots: esbuild: 0.17.19 miniflare: 3.20250124.0 semver: 7.6.3 - vitest: 2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(sass-embedded@1.83.0)(terser@5.31.0) + vitest: 2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0) wrangler: 3.105.1(@cloudflare/workers-types@4.20250124.3) zod: 3.24.1 transitivePeerDependencies: @@ -13860,6 +13997,59 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} + '@inquirer/confirm@5.1.6(@types/node@20.16.10)': + dependencies: + '@inquirer/core': 10.1.7(@types/node@20.16.10) + '@inquirer/type': 3.0.4(@types/node@20.16.10) + optionalDependencies: + '@types/node': 20.16.10 + + '@inquirer/confirm@5.1.6(@types/node@22.13.0)': + dependencies: + '@inquirer/core': 10.1.7(@types/node@22.13.0) + '@inquirer/type': 3.0.4(@types/node@22.13.0) + optionalDependencies: + '@types/node': 22.13.0 + optional: true + + '@inquirer/core@10.1.7(@types/node@20.16.10)': + dependencies: + '@inquirer/figures': 1.0.10 + '@inquirer/type': 3.0.4(@types/node@20.16.10) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 20.16.10 + + '@inquirer/core@10.1.7(@types/node@22.13.0)': + dependencies: + '@inquirer/figures': 1.0.10 + '@inquirer/type': 3.0.4(@types/node@22.13.0) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.13.0 + optional: true + + '@inquirer/figures@1.0.10': {} + + '@inquirer/type@3.0.4(@types/node@20.16.10)': + optionalDependencies: + '@types/node': 20.16.10 + + '@inquirer/type@3.0.4(@types/node@22.13.0)': + optionalDependencies: + '@types/node': 22.13.0 + optional: true + '@internationalized/date@3.5.6': dependencies: '@swc/helpers': 0.5.13 @@ -14289,6 +14479,15 @@ snapshots: '@module-federation/runtime': 0.8.4 '@module-federation/sdk': 0.8.4 + '@mswjs/interceptors@0.37.6': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + '@napi-rs/wasm-runtime@0.2.4': dependencies: '@emnapi/core': 1.3.1 @@ -14416,6 +14615,15 @@ snapshots: - '@swc/core' - debug + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + '@oxc-resolver/binding-darwin-arm64@1.12.0': optional: true @@ -14454,7 +14662,7 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.14.2)(type-fest@4.23.0)(webpack-dev-server@5.0.4(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)))(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.14.2)(type-fest@4.35.0)(webpack-dev-server@5.0.4(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)))(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2))': dependencies: ansi-html-community: 0.0.8 core-js-pure: 3.37.1 @@ -14466,7 +14674,7 @@ snapshots: source-map: 0.7.4 webpack: 5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2) optionalDependencies: - type-fest: 4.23.0 + type-fest: 4.35.0 webpack-dev-server: 5.0.4(webpack@5.91.0(@swc/core@1.7.42(@swc/helpers@0.5.15))(esbuild@0.24.2)) '@react-native-community/cli-clean@10.1.1(encoding@0.1.13)': @@ -17121,6 +17329,8 @@ snapshots: dependencies: '@types/node': 22.13.0 + '@types/cookie@0.6.0': {} + '@types/cosmiconfig@6.0.0(typescript@5.7.2)': dependencies: cosmiconfig: 9.0.0(typescript@5.7.2) @@ -17308,6 +17518,10 @@ snapshots: '@types/stack-utils@2.0.2': {} + '@types/statuses@2.0.5': {} + + '@types/tough-cookie@4.0.5': {} + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -17660,12 +17874,13 @@ snapshots: chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.8(vite@5.4.8(@types/node@22.13.0)(sass-embedded@1.83.0)(terser@5.31.0))': + '@vitest/mocker@2.1.8(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(vite@5.4.8(@types/node@22.13.0)(sass-embedded@1.83.0)(terser@5.31.0))': dependencies: '@vitest/spy': 2.1.8 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: + msw: 2.7.0(@types/node@22.13.0)(typescript@5.7.2) vite: 5.4.8(@types/node@22.13.0)(sass-embedded@1.83.0)(terser@5.31.0) '@vitest/pretty-format@2.1.8': @@ -18556,6 +18771,8 @@ snapshots: cli-spinners@2.9.1: {} + cli-width@4.1.0: {} + cliui@6.0.0: dependencies: string-width: 4.2.3 @@ -20049,6 +20266,8 @@ snapshots: graphemer@1.4.0: {} + graphql@16.10.0: {} + gray-matter@4.0.3: dependencies: js-yaml: 3.14.1 @@ -20161,6 +20380,8 @@ snapshots: property-information: 6.5.0 space-separated-tokens: 2.0.2 + headers-polyfill@4.0.3: {} + hermes-eslint@0.23.1: dependencies: esrecurse: 4.3.0 @@ -20532,6 +20753,8 @@ snapshots: is-network-error@1.1.0: optional: true + is-node-process@1.2.0: {} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -22708,6 +22931,57 @@ snapshots: ms@2.1.3: {} + msw@2.7.0(@types/node@20.16.10)(typescript@5.7.2): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.1.6(@types/node@20.16.10) + '@mswjs/interceptors': 0.37.6 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.5 + graphql: 16.10.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + strict-event-emitter: 0.5.1 + type-fest: 4.35.0 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.7.2 + transitivePeerDependencies: + - '@types/node' + + msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.1.6(@types/node@22.13.0) + '@mswjs/interceptors': 0.37.6 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.5 + graphql: 16.10.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + strict-event-emitter: 0.5.1 + type-fest: 4.35.0 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.7.2 + transitivePeerDependencies: + - '@types/node' + optional: true + multicast-dns@7.2.5: dependencies: dns-packet: 5.6.1 @@ -22716,6 +22990,8 @@ snapshots: mustache@4.2.0: {} + mute-stream@2.0.0: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -22983,6 +23259,8 @@ snapshots: os-tmpdir@1.0.2: {} + outvariant@1.4.3: {} + oxc-resolver@1.12.0: optionalDependencies: '@oxc-resolver/binding-darwin-arm64': 1.12.0 @@ -23421,6 +23699,10 @@ snapshots: proxy-from-env@1.1.0: {} + psl@1.15.0: + dependencies: + punycode: 2.3.1 + pump@3.0.2: dependencies: end-of-stream: 1.4.4 @@ -23437,6 +23719,8 @@ snapshots: querystring@0.2.1: {} + querystringify@2.2.0: {} + queue-microtask@1.2.3: {} queue@6.0.2: @@ -24086,8 +24370,7 @@ snapshots: require-main-filename@2.0.0: {} - requires-port@1.0.0: - optional: true + requires-port@1.0.0: {} reselect@4.1.8: {} @@ -24721,6 +25004,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + strict-event-emitter@0.5.1: {} + string-length@4.0.2: dependencies: char-regex: 1.0.2 @@ -25004,6 +25289,13 @@ snapshots: toml@3.0.0: {} + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + tough-cookie@5.0.0: dependencies: tldts: 6.1.48 @@ -25213,6 +25505,8 @@ snapshots: type-fest@4.23.0: {} + type-fest@4.35.0: {} + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -25376,6 +25670,8 @@ snapshots: universalify@0.1.2: {} + universalify@0.2.0: {} + universalify@2.0.1: {} unpipe@1.0.0: {} @@ -25404,6 +25700,11 @@ snapshots: dependencies: punycode: 2.3.1 + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + use-sync-external-store@1.2.0(react@18.2.0): dependencies: react: 18.2.0 @@ -25507,10 +25808,10 @@ snapshots: sass-embedded: 1.83.0 terser: 5.31.0 - vitest@2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(sass-embedded@1.83.0)(terser@5.31.0): + vitest@2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0): dependencies: '@vitest/expect': 2.1.8 - '@vitest/mocker': 2.1.8(vite@5.4.8(@types/node@22.13.0)(sass-embedded@1.83.0)(terser@5.31.0)) + '@vitest/mocker': 2.1.8(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(vite@5.4.8(@types/node@22.13.0)(sass-embedded@1.83.0)(terser@5.31.0)) '@vitest/pretty-format': 2.1.8 '@vitest/runner': 2.1.8 '@vitest/snapshot': 2.1.8 @@ -25929,6 +26230,8 @@ snapshots: yocto-queue@1.0.0: {} + yoctocolors-cjs@2.1.2: {} + yoctocolors@2.1.1: {} youch@3.3.4: From e58e72dad603e60d54d8a73692343883e66989e3 Mon Sep 17 00:00:00 2001 From: gronxb Date: Thu, 20 Feb 2025 10:26:37 +0900 Subject: [PATCH 7/7] fix: deprecated setBundles and test --- plugins/standalone/package.json | 3 +- .../src/standaloneRepository.spec.ts | 32 +-- .../standalone/src/standaloneRepository.ts | 18 +- plugins/standalone/src/types.ts | 10 - pnpm-lock.yaml | 223 +++++++++++++++++- 5 files changed, 242 insertions(+), 44 deletions(-) delete mode 100644 plugins/standalone/src/types.ts diff --git a/plugins/standalone/package.json b/plugins/standalone/package.json index aac78f87..fba94f3f 100644 --- a/plugins/standalone/package.json +++ b/plugins/standalone/package.json @@ -49,6 +49,7 @@ "@types/node": "^20.9.4", "@types/semver": "^7.5.8", "msw": "^2.7.0", - "semver": "^7.6.3" + "semver": "^7.6.3", + "vitest": "^3.0.6" } } diff --git a/plugins/standalone/src/standaloneRepository.spec.ts b/plugins/standalone/src/standaloneRepository.spec.ts index 1ca5d64d..fb94199b 100644 --- a/plugins/standalone/src/standaloneRepository.spec.ts +++ b/plugins/standalone/src/standaloneRepository.spec.ts @@ -232,32 +232,22 @@ describe("Standalone Repository Plugin (Default Routes)", () => { expect(postCalled).toBe(true); }); - it("setBundles & commitBundle: sets the bundle list and commits all bundles", async () => { - await repo.setBundles(testBundles); - - let postCalled = false; - server.use( - http.post("http://localhost/bundles", async ({ request }) => { - postCalled = true; - const body = await request.json(); - expect(body).toEqual(testBundles); - return HttpResponse.json({ success: true }); - }), - ); - - await repo.commitBundle(); - expect(postCalled).toBe(true); - }); - it("commitBundle: does nothing if there are no changes", async () => { const spy = vi.spyOn(global, "fetch"); await repo.commitBundle(); expect(spy).not.toHaveBeenCalled(); spy.mockRestore(); }); - it("commitBundle: throws exception on API error", async () => { - await repo.setBundles(testBundles); + server.use( + http.get("http://localhost/bundles", () => { + return HttpResponse.json(testBundles); + }), + ); + + await repo.updateBundle("00000000-0000-0000-0000-000000000001", { + enabled: false, + }); server.use( http.post("http://localhost/bundles", () => { @@ -268,8 +258,8 @@ describe("Standalone Repository Plugin (Default Routes)", () => { }), ); - await expect(repo.commitBundle()).rejects.toThrow( - "API Error: Internal Server Error", + await expect(repo.commitBundle()).rejects.toStrictEqual( + new Error("API Error: Internal Server Error"), ); }); }); diff --git a/plugins/standalone/src/standaloneRepository.ts b/plugins/standalone/src/standaloneRepository.ts index 89598bb6..785c17cc 100644 --- a/plugins/standalone/src/standaloneRepository.ts +++ b/plugins/standalone/src/standaloneRepository.ts @@ -4,7 +4,17 @@ import type { DatabasePlugin, DatabasePluginHooks, } from "@hot-updater/plugin-core"; -import type { RouteConfig, Routes } from "./types"; + +export interface RouteConfig { + path: string; + headers?: Record; +} + +export interface Routes { + upsert: () => RouteConfig; + list: () => RouteConfig; + retrieve: (bundleId: string) => RouteConfig; +} const defaultRoutes: Routes = { upsert: () => ({ @@ -107,12 +117,6 @@ export const standaloneRepository = bundles.unshift(inputBundle); markChanged(inputBundle.id); }, - async setBundles(inputBundles: Bundle[]) { - bundles = inputBundles; - for (const bundle of inputBundles) { - markChanged(bundle.id); - } - }, async getBundleById(bundleId: string): Promise { try { const { path, headers: routeHeaders } = routes.retrieve(bundleId); diff --git a/plugins/standalone/src/types.ts b/plugins/standalone/src/types.ts deleted file mode 100644 index a7e1113d..00000000 --- a/plugins/standalone/src/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface RouteConfig { - path: string; - headers?: Record; -} - -export interface Routes { - upsert: () => RouteConfig; - list: () => RouteConfig; - retrieve: (bundleId: string) => RouteConfig; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6862b5f3..21fed874 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -714,7 +714,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: ^0.6.4 - version: 0.6.7(@cloudflare/workers-types@4.20250124.3)(@vitest/runner@2.1.8)(@vitest/snapshot@2.1.8)(vitest@2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0)) + version: 0.6.7(@cloudflare/workers-types@4.20250124.3)(@vitest/runner@3.0.6)(@vitest/snapshot@3.0.6)(vitest@2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0)) '@cloudflare/workers-types': specifier: ^4.20250124.3 version: 4.20250124.3 @@ -801,7 +801,7 @@ importers: devDependencies: vitest: specifier: 2.1.8 - version: 2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(sass-embedded@1.83.0)(terser@5.31.0) + version: 2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0) plugins/plugin-core: dependencies: @@ -871,6 +871,9 @@ importers: semver: specifier: ^7.6.3 version: 7.6.3 + vitest: + specifier: ^3.0.6 + version: 3.0.6(@types/debug@4.1.12)(@types/node@20.16.10)(jsdom@25.0.1)(msw@2.7.0(@types/node@20.16.10)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0) plugins/supabase: dependencies: @@ -5193,6 +5196,9 @@ packages: '@vitest/expect@2.1.8': resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==} + '@vitest/expect@3.0.6': + resolution: {integrity: sha512-zBduHf/ja7/QRX4HdP1DSq5XrPgdN+jzLOwaTq/0qZjYfgETNFCKf9nOAp2j3hmom3oTbczuUzrzg9Hafh7hNg==} + '@vitest/mocker@2.1.8': resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} peerDependencies: @@ -5204,21 +5210,47 @@ packages: vite: optional: true + '@vitest/mocker@3.0.6': + resolution: {integrity: sha512-KPztr4/tn7qDGZfqlSPQoF2VgJcKxnDNhmfR3VgZ6Fy1bO8T9Fc1stUiTXtqz0yG24VpD00pZP5f8EOFknjNuQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@2.1.8': resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==} + '@vitest/pretty-format@3.0.6': + resolution: {integrity: sha512-Zyctv3dbNL+67qtHfRnUE/k8qxduOamRfAL1BurEIQSyOEFffoMvx2pnDSSbKAAVxY0Ej2J/GH2dQKI0W2JyVg==} + '@vitest/runner@2.1.8': resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} + '@vitest/runner@3.0.6': + resolution: {integrity: sha512-JopP4m/jGoaG1+CBqubV/5VMbi7L+NQCJTu1J1Pf6YaUbk7bZtaq5CX7p+8sY64Sjn1UQ1XJparHfcvTTdu9cA==} + '@vitest/snapshot@2.1.8': resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} + '@vitest/snapshot@3.0.6': + resolution: {integrity: sha512-qKSmxNQwT60kNwwJHMVwavvZsMGXWmngD023OHSgn873pV0lylK7dwBTfYP7e4URy5NiBCHHiQGA9DHkYkqRqg==} + '@vitest/spy@2.1.8': resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} + '@vitest/spy@3.0.6': + resolution: {integrity: sha512-HfOGx/bXtjy24fDlTOpgiAEJbRfFxoX3zIGagCqACkFKKZ/TTOE6gYMKXlqecvxEndKFuNHcHqP081ggZ2yM0Q==} + '@vitest/utils@2.1.8': resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} + '@vitest/utils@3.0.6': + resolution: {integrity: sha512-18ktZpf4GQFTbf9jK543uspU03Q2qya7ZGya5yiZ0Gx0nnnalBvd5ZBislbl2EhLjM8A8rt4OilqKG7QwcGkvQ==} + '@webassemblyjs/ast@1.12.1': resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} @@ -5833,6 +5865,10 @@ packages: resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} engines: {node: '>=12'} + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -6195,6 +6231,15 @@ packages: supports-color: optional: true + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decamelize@1.2.0: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} @@ -8168,6 +8213,9 @@ packages: loupe@3.1.2: resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + lowlight@1.20.0: resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} @@ -9094,6 +9142,9 @@ packages: pathe@2.0.2: resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} @@ -10550,6 +10601,10 @@ packages: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + tinyspy@3.0.2: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} @@ -10981,6 +11036,11 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true + vite-node@3.0.6: + resolution: {integrity: sha512-s51RzrTkXKJrhNbUzQRsarjmAae7VmMPAsRT7lppVpIg6mK3zGthP9Hgz0YQQKuNcF+Ii7DfYk3Fxz40jRmePw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + vite@5.4.8: resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -11037,6 +11097,34 @@ packages: jsdom: optional: true + vitest@3.0.6: + resolution: {integrity: sha512-/iL1Sc5VeDZKPDe58oGK4HUFLhw6b5XdY1MYawjuSaDA4sEfYlY9HnS6aCEG26fX+MgUi7MwlduTBHHAI/OvMA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.0.6 + '@vitest/ui': 3.0.6 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vlq@1.0.1: resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} @@ -13548,10 +13636,10 @@ snapshots: dependencies: mime: 3.0.0 - '@cloudflare/vitest-pool-workers@0.6.7(@cloudflare/workers-types@4.20250124.3)(@vitest/runner@2.1.8)(@vitest/snapshot@2.1.8)(vitest@2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0))': + '@cloudflare/vitest-pool-workers@0.6.7(@cloudflare/workers-types@4.20250124.3)(@vitest/runner@3.0.6)(@vitest/snapshot@3.0.6)(vitest@2.1.8(@types/node@22.13.0)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0))': dependencies: - '@vitest/runner': 2.1.8 - '@vitest/snapshot': 2.1.8 + '@vitest/runner': 3.0.6 + '@vitest/snapshot': 3.0.6 birpc: 0.2.14 cjs-module-lexer: 1.3.1 devalue: 4.3.3 @@ -17755,6 +17843,13 @@ snapshots: chai: 5.1.2 tinyrainbow: 1.2.0 + '@vitest/expect@3.0.6': + dependencies: + '@vitest/spy': 3.0.6 + '@vitest/utils': 3.0.6 + chai: 5.2.0 + tinyrainbow: 2.0.0 + '@vitest/mocker@2.1.8(msw@2.7.0(@types/node@22.13.0)(typescript@5.7.2))(vite@5.4.8(@types/node@22.13.0)(sass-embedded@1.83.0)(terser@5.31.0))': dependencies: '@vitest/spy': 2.1.8 @@ -17764,31 +17859,65 @@ snapshots: msw: 2.7.0(@types/node@22.13.0)(typescript@5.7.2) vite: 5.4.8(@types/node@22.13.0)(sass-embedded@1.83.0)(terser@5.31.0) + '@vitest/mocker@3.0.6(msw@2.7.0(@types/node@20.16.10)(typescript@5.7.2))(vite@5.4.8(@types/node@20.16.10)(sass-embedded@1.83.0)(terser@5.31.0))': + dependencies: + '@vitest/spy': 3.0.6 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + msw: 2.7.0(@types/node@20.16.10)(typescript@5.7.2) + vite: 5.4.8(@types/node@20.16.10)(sass-embedded@1.83.0)(terser@5.31.0) + '@vitest/pretty-format@2.1.8': dependencies: tinyrainbow: 1.2.0 + '@vitest/pretty-format@3.0.6': + dependencies: + tinyrainbow: 2.0.0 + '@vitest/runner@2.1.8': dependencies: '@vitest/utils': 2.1.8 pathe: 1.1.2 + '@vitest/runner@3.0.6': + dependencies: + '@vitest/utils': 3.0.6 + pathe: 2.0.3 + '@vitest/snapshot@2.1.8': dependencies: '@vitest/pretty-format': 2.1.8 magic-string: 0.30.17 pathe: 1.1.2 + '@vitest/snapshot@3.0.6': + dependencies: + '@vitest/pretty-format': 3.0.6 + magic-string: 0.30.17 + pathe: 2.0.3 + '@vitest/spy@2.1.8': dependencies: tinyspy: 3.0.2 + '@vitest/spy@3.0.6': + dependencies: + tinyspy: 3.0.2 + '@vitest/utils@2.1.8': dependencies: '@vitest/pretty-format': 2.1.8 loupe: 3.1.2 tinyrainbow: 1.2.0 + '@vitest/utils@3.0.6': + dependencies: + '@vitest/pretty-format': 3.0.6 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + '@webassemblyjs/ast@1.12.1': dependencies: '@webassemblyjs/helper-numbers': 1.11.6 @@ -18554,6 +18683,14 @@ snapshots: loupe: 3.1.2 pathval: 2.0.0 + chai@5.2.0: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.2 + pathval: 2.0.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -18968,6 +19105,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.0: + dependencies: + ms: 2.1.3 + decamelize@1.2.0: {} decimal.js@10.4.3: @@ -21629,6 +21770,8 @@ snapshots: loupe@3.1.2: {} + loupe@3.1.3: {} + lowlight@1.20.0: dependencies: fault: 1.0.4 @@ -23300,6 +23443,8 @@ snapshots: pathe@2.0.2: {} + pathe@2.0.3: {} + pathval@2.0.0: {} peberminta@0.9.0: {} @@ -25122,6 +25267,8 @@ snapshots: tinyrainbow@1.2.0: {} + tinyrainbow@2.0.0: {} + tinyspy@3.0.2: {} tldts-core@6.1.48: @@ -25656,6 +25803,35 @@ snapshots: - supports-color - terser + vite-node@3.0.6(@types/node@20.16.10)(sass-embedded@1.83.0)(terser@5.31.0): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 2.0.3 + vite: 5.4.8(@types/node@20.16.10)(sass-embedded@1.83.0)(terser@5.31.0) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.8(@types/node@20.16.10)(sass-embedded@1.83.0)(terser@5.31.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.49 + rollup: 4.32.1 + optionalDependencies: + '@types/node': 20.16.10 + fsevents: 2.3.3 + sass-embedded: 1.83.0 + terser: 5.31.0 + vite@5.4.8(@types/node@22.13.0)(sass-embedded@1.83.0)(terser@5.31.0): dependencies: esbuild: 0.21.5 @@ -25703,6 +25879,43 @@ snapshots: - supports-color - terser + vitest@3.0.6(@types/debug@4.1.12)(@types/node@20.16.10)(jsdom@25.0.1)(msw@2.7.0(@types/node@20.16.10)(typescript@5.7.2))(sass-embedded@1.83.0)(terser@5.31.0): + dependencies: + '@vitest/expect': 3.0.6 + '@vitest/mocker': 3.0.6(msw@2.7.0(@types/node@20.16.10)(typescript@5.7.2))(vite@5.4.8(@types/node@20.16.10)(sass-embedded@1.83.0)(terser@5.31.0)) + '@vitest/pretty-format': 3.0.6 + '@vitest/runner': 3.0.6 + '@vitest/snapshot': 3.0.6 + '@vitest/spy': 3.0.6 + '@vitest/utils': 3.0.6 + chai: 5.2.0 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 2.0.3 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 5.4.8(@types/node@20.16.10)(sass-embedded@1.83.0)(terser@5.31.0) + vite-node: 3.0.6(@types/node@20.16.10)(sass-embedded@1.83.0)(terser@5.31.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 20.16.10 + jsdom: 25.0.1 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vlq@1.0.1: {} vscode-oniguruma@1.7.0: {}