diff --git a/examples/msw-demo/README.md b/examples/msw-demo/README.md new file mode 100644 index 000000000..f14620d6d --- /dev/null +++ b/examples/msw-demo/README.md @@ -0,0 +1,88 @@ +# MSW Demo Example + +This example demonstrates how to use the `@objectstack/plugin-msw` package to mock ObjectStack APIs using Mock Service Worker (MSW). + +## Features + +- **Browser Mode**: Shows how to use MSW in a browser environment with standalone handlers +- **Server Mode**: Demonstrates MSW plugin integration with ObjectStack Runtime + +## Files + +- `src/browser.ts` - Browser-based MSW setup matching the problem statement +- `src/server.ts` - Server-side MSW plugin integration with Runtime + +## Usage + +### Browser Mode + +```typescript +import { setupWorker } from 'msw/browser'; +import { http, HttpResponse } from 'msw'; +import { ObjectStackServer } from '@objectstack/plugin-msw'; + +// Initialize mock server +ObjectStackServer.init(protocol); + +// Define handlers +const handlers = [ + http.get('/api/user/:id', async ({ params }) => { + const result = await ObjectStackServer.getUser(params.id); + return HttpResponse.json(result.data, { status: result.status }); + }) +]; + +// Start worker +const worker = setupWorker(...handlers); +await worker.start(); +``` + +### Runtime Integration + +```typescript +import { ObjectStackKernel } from '@objectstack/runtime'; +import { MSWPlugin } from '@objectstack/plugin-msw'; + +const kernel = new ObjectStackKernel([ + // Your manifests... + new MSWPlugin({ + baseUrl: '/api/v1', + logRequests: true + }) +]); + +await kernel.start(); +``` + +## Running + +```bash +# Install dependencies +pnpm install + +# Build +pnpm build + +# Run server example +pnpm dev +``` + +## API Endpoints Mocked + +The MSW plugin automatically mocks the following endpoints: + +- `GET /api/v1` - Discovery +- `GET /api/v1/meta` - Metadata types +- `GET /api/v1/meta/:type` - Metadata items +- `GET /api/v1/meta/:type/:name` - Specific metadata +- `GET /api/v1/data/:object` - Find records +- `GET /api/v1/data/:object/:id` - Get record +- `POST /api/v1/data/:object` - Create record +- `PATCH /api/v1/data/:object/:id` - Update record +- `DELETE /api/v1/data/:object/:id` - Delete record +- `GET /api/v1/ui/view/:object` - UI view config + +## See Also + +- [@objectstack/plugin-msw](../../packages/plugin-msw) - MSW Plugin package +- [MSW Documentation](https://mswjs.io/) - Official MSW docs diff --git a/examples/msw-demo/package.json b/examples/msw-demo/package.json new file mode 100644 index 000000000..c41b9a19e --- /dev/null +++ b/examples/msw-demo/package.json @@ -0,0 +1,24 @@ +{ + "name": "@objectstack/example-msw-demo", + "version": "0.1.0", + "private": true, + "description": "Example demonstrating MSW plugin usage with ObjectStack", + "scripts": { + "build": "tsc", + "dev": "ts-node src/server.ts", + "clean": "rm -rf dist node_modules" + }, + "dependencies": { + "@objectstack/driver-memory": "workspace:*", + "@objectstack/example-crm": "workspace:*", + "@objectstack/objectql": "workspace:*", + "@objectstack/plugin-msw": "workspace:*", + "@objectstack/runtime": "workspace:*", + "msw": "^2.0.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.0" + } +} diff --git a/examples/msw-demo/src/browser.ts b/examples/msw-demo/src/browser.ts new file mode 100644 index 000000000..de3a3b95e --- /dev/null +++ b/examples/msw-demo/src/browser.ts @@ -0,0 +1,62 @@ +/** + * MSW Browser Example - Standalone Usage + * + * This example shows how to use MSW with ObjectStack in a browser environment. + * It matches the example from the problem statement. + */ + +import { setupWorker } from 'msw/browser'; +import { http, HttpResponse } from 'msw'; +import { ObjectStackServer } from '@objectstack/plugin-msw'; + +// Mock protocol - in real usage, this would come from runtime +// For this example, we'll simulate it +const mockProtocol = { + getData: async (object: string, id: string) => { + return { id, object, name: `Mock ${object}`, status: 'active' }; + }, + createData: async (object: string, data: any) => { + return { id: 'new-id', ...data }; + }, + // Add other methods as needed +} as any; + +// 1. Initialize the mock server (equivalent to ObjectStackServer.init()) +ObjectStackServer.init(mockProtocol); + +// 2. Define request handlers (similar to Express/Koa routes, but in Service Worker) +const handlers = [ + + // Intercept GET /api/user/:id + http.get('/api/user/:id', async ({ params }) => { + const { id } = params; + + // Call local logic + const result = await ObjectStackServer.getUser(id as string); + + // Return constructed Response + return HttpResponse.json(result.data, { status: result.status }); + }), + + // Intercept POST /api/user + http.post('/api/user', async ({ request }) => { + const body = await request.json(); + + // Call local logic + const result = await ObjectStackServer.createUser(body); + + return HttpResponse.json(result.data, { status: result.status }); + }), +]; + +// 3. Create Worker instance +export const worker = setupWorker(...handlers); + +// Start the worker (typically called in your app entry point) +if (typeof window !== 'undefined') { + worker.start({ + onUnhandledRequest: 'bypass', + }).then(() => { + console.log('[MSW] Mock Service Worker started'); + }); +} diff --git a/examples/msw-demo/src/server.ts b/examples/msw-demo/src/server.ts new file mode 100644 index 000000000..40282e73b --- /dev/null +++ b/examples/msw-demo/src/server.ts @@ -0,0 +1,36 @@ +/** + * MSW Server Example - Runtime Integration + * + * This example shows how to use the MSW plugin with ObjectStack Runtime. + * This is useful for Node.js testing environments or development. + */ + +import { ObjectStackKernel } from '@objectstack/runtime'; +import { InMemoryDriver } from '@objectstack/driver-memory'; +import { MSWPlugin } from '@objectstack/plugin-msw'; + +import CrmApp from '@objectstack/example-crm/objectstack.config'; + +(async () => { + console.log('๐Ÿš€ Starting ObjectStack with MSW Plugin...'); + + const kernel = new ObjectStackKernel([ + CrmApp, + new InMemoryDriver(), + + // Add MSW Plugin for API mocking + new MSWPlugin({ + enableBrowser: false, // Disable browser mode for Node.js + baseUrl: '/api/v1', + logRequests: true, + customHandlers: [ + // You can add custom handlers here + ] + }) + ]); + + await kernel.start(); + + console.log('โœ… MSW Plugin initialized'); + console.log('๐Ÿ“ All API endpoints are now mocked and ready for testing'); +})(); diff --git a/examples/msw-demo/tsconfig.json b/examples/msw-demo/tsconfig.json new file mode 100644 index 000000000..ec67f0b8f --- /dev/null +++ b/examples/msw-demo/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "declaration": true, + "outDir": "./dist", + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/plugin-msw/CHANGELOG.md b/packages/plugin-msw/CHANGELOG.md new file mode 100644 index 000000000..b8f1f9897 --- /dev/null +++ b/packages/plugin-msw/CHANGELOG.md @@ -0,0 +1,24 @@ +# @objectstack/plugin-msw + +## 0.3.1 + +### Added + +- Initial release of MSW plugin for ObjectStack +- `MSWPlugin` class implementing RuntimePlugin interface +- `ObjectStackServer` mock server for handling API calls +- Automatic generation of MSW handlers for ObjectStack API endpoints +- Support for browser and Node.js environments +- Custom handler support +- Comprehensive documentation and examples +- TypeScript type definitions + +### Features + +- Discovery endpoint mocking +- Metadata endpoint mocking +- Data CRUD operation mocking +- UI protocol endpoint mocking +- Request logging support +- Configurable base URL +- Integration with ObjectStack Runtime Protocol diff --git a/packages/plugin-msw/README.md b/packages/plugin-msw/README.md new file mode 100644 index 000000000..e97d2215a --- /dev/null +++ b/packages/plugin-msw/README.md @@ -0,0 +1,211 @@ +# @objectstack/plugin-msw + +MSW (Mock Service Worker) Plugin for ObjectStack Runtime. This plugin enables seamless integration with [Mock Service Worker](https://mswjs.io/) for testing and development environments. + +## Features + +- ๐ŸŽฏ **Automatic API Mocking**: Automatically generates MSW handlers for all ObjectStack API endpoints +- ๐Ÿ”„ **Runtime Integration**: Seamlessly integrates with ObjectStack Runtime Protocol +- ๐Ÿงช **Testing Ready**: Perfect for unit tests, integration tests, and development +- ๐ŸŒ **Browser & Node Support**: Works in both browser and Node.js environments +- ๐ŸŽจ **Custom Handlers**: Easily add custom MSW handlers alongside standard ones +- ๐Ÿ“ **TypeScript First**: Fully typed with TypeScript + +## Installation + +```bash +pnpm add @objectstack/plugin-msw msw +``` + +## Usage + +### With ObjectStack Runtime + +```typescript +import { MSWPlugin } from '@objectstack/plugin-msw'; +import { ObjectStackRuntime } from '@objectstack/runtime'; + +const runtime = new ObjectStackRuntime({ + plugins: [ + new MSWPlugin({ + enableBrowser: true, + baseUrl: '/api/v1', + logRequests: true + }) + ] +}); + +await runtime.start(); +``` + +### Standalone Usage (Browser) + +```typescript +import { setupWorker } from 'msw/browser'; +import { http, HttpResponse } from 'msw'; +import { ObjectStackServer } from '@objectstack/plugin-msw'; + +// 1. Initialize the mock server +ObjectStackServer.init(protocol); + +// 2. Define your handlers +const handlers = [ + // Intercept GET /api/user/:id + http.get('/api/user/:id', async ({ params }) => { + const result = await ObjectStackServer.getData('user', params.id as string); + return HttpResponse.json(result.data, { status: result.status }); + }), + + // Intercept POST /api/user + http.post('/api/user', async ({ request }) => { + const body = await request.json(); + const result = await ObjectStackServer.createData('user', body); + return HttpResponse.json(result.data, { status: result.status }); + }), +]; + +// 3. Create and start the worker +const worker = setupWorker(...handlers); +await worker.start(); +``` + +### With Custom Handlers + +```typescript +import { MSWPlugin } from '@objectstack/plugin-msw'; +import { http, HttpResponse } from 'msw'; + +const customHandlers = [ + http.get('/api/custom/:id', ({ params }) => { + return HttpResponse.json({ id: params.id, custom: true }); + }) +]; + +const plugin = new MSWPlugin({ + customHandlers, + baseUrl: '/api/v1' +}); +``` + +## API Reference + +### MSWPlugin + +The main plugin class that implements the ObjectStack Runtime Plugin interface. + +#### Options + +```typescript +interface MSWPluginOptions { + /** + * Enable MSW in the browser environment + * @default true + */ + enableBrowser?: boolean; + + /** + * Custom handlers to add to MSW + */ + customHandlers?: Array; + + /** + * Base URL for API endpoints + * @default '/api/v1' + */ + baseUrl?: string; + + /** + * Whether to log requests + * @default true + */ + logRequests?: boolean; +} +``` + +### ObjectStackServer + +The mock server that handles ObjectStack API calls. + +#### Static Methods + +- `init(protocol, logger?)` - Initialize the mock server with an ObjectStack protocol instance +- `findData(object, params?)` - Find records for an object +- `getData(object, id)` - Get a single record by ID +- `createData(object, data)` - Create a new record +- `updateData(object, id, data)` - Update an existing record +- `deleteData(object, id)` - Delete a record +- `getUser(id)` - Legacy method for getting user (alias for `getData('user', id)`) +- `createUser(data)` - Legacy method for creating user (alias for `createData('user', data)`) + +## Mocked Endpoints + +The plugin automatically mocks the following ObjectStack API endpoints: + +### Discovery +- `GET /api/v1` - Get API discovery information + +### Metadata +- `GET /api/v1/meta` - Get available metadata types +- `GET /api/v1/meta/:type` - Get metadata items for a type +- `GET /api/v1/meta/:type/:name` - Get specific metadata item + +### Data Operations +- `GET /api/v1/data/:object` - Find records +- `GET /api/v1/data/:object/:id` - Get record by ID +- `POST /api/v1/data/:object` - Create record +- `PATCH /api/v1/data/:object/:id` - Update record +- `DELETE /api/v1/data/:object/:id` - Delete record + +### UI Protocol +- `GET /api/v1/ui/view/:object` - Get UI view configuration + +## Example: Testing with Vitest + +```typescript +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { setupWorker } from 'msw/browser'; +import { ObjectStackServer } from '@objectstack/plugin-msw'; +import { http, HttpResponse } from 'msw'; + +describe('User API', () => { + let worker: any; + + beforeAll(async () => { + // Initialize mock server + ObjectStackServer.init(protocol); + + // Setup handlers + const handlers = [ + http.get('/api/user/:id', async ({ params }) => { + const result = await ObjectStackServer.getData('user', params.id as string); + return HttpResponse.json(result.data, { status: result.status }); + }) + ]; + + worker = setupWorker(...handlers); + await worker.start({ onUnhandledRequest: 'bypass' }); + }); + + afterAll(() => { + worker.stop(); + }); + + it('should get user by id', async () => { + const response = await fetch('/api/user/123'); + const data = await response.json(); + + expect(response.status).toBe(200); + expect(data).toBeDefined(); + }); +}); +``` + +## License + +Apache-2.0 + +## Related Packages + +- [@objectstack/runtime](../runtime) - ObjectStack Runtime +- [@objectstack/spec](../spec) - ObjectStack Specifications +- [msw](https://mswjs.io/) - Mock Service Worker diff --git a/packages/plugin-msw/package.json b/packages/plugin-msw/package.json new file mode 100644 index 000000000..1956ad647 --- /dev/null +++ b/packages/plugin-msw/package.json @@ -0,0 +1,23 @@ +{ + "name": "@objectstack/plugin-msw", + "version": "0.3.1", + "description": "MSW (Mock Service Worker) Plugin for ObjectStack Runtime", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@objectstack/spec": "workspace:*", + "@objectstack/types": "workspace:*", + "msw": "^2.0.0" + }, + "devDependencies": { + "@objectstack/runtime": "workspace:*", + "@types/node": "^20.0.0", + "typescript": "^5.0.0" + }, + "peerDependencies": { + "@objectstack/runtime": "^0.3.1" + } +} diff --git a/packages/plugin-msw/src/index.ts b/packages/plugin-msw/src/index.ts new file mode 100644 index 000000000..e143df24a --- /dev/null +++ b/packages/plugin-msw/src/index.ts @@ -0,0 +1,46 @@ +/** + * @objectstack/plugin-msw + * + * MSW (Mock Service Worker) Plugin for ObjectStack Runtime + * + * This plugin enables seamless integration with Mock Service Worker for + * testing and development environments. It automatically generates MSW + * handlers for all ObjectStack API endpoints. + * + * @example + * ```typescript + * import { MSWPlugin, ObjectStackServer } from '@objectstack/plugin-msw'; + * import { ObjectStackRuntime } from '@objectstack/runtime'; + * + * // Use with runtime + * const runtime = new ObjectStackRuntime({ + * plugins: [ + * new MSWPlugin({ + * enableBrowser: true, + * baseUrl: '/api/v1' + * }) + * ] + * }); + * + * // Or use standalone in browser + * import { setupWorker, http } from 'msw/browser'; + * + * ObjectStackServer.init(protocol); + * + * const handlers = [ + * http.get('/api/user/:id', async ({ params }) => { + * const result = await ObjectStackServer.getData('user', params.id); + * return HttpResponse.json(result.data, { status: result.status }); + * }) + * ]; + * + * const worker = setupWorker(...handlers); + * await worker.start(); + * ``` + */ + +export { MSWPlugin, ObjectStackServer } from './msw-plugin'; +export type { MSWPluginOptions } from './msw-plugin'; + +// Re-export MSW types for convenience +export type { HttpHandler, HttpResponse } from 'msw'; diff --git a/packages/plugin-msw/src/msw-plugin.ts b/packages/plugin-msw/src/msw-plugin.ts new file mode 100644 index 000000000..a12786bda --- /dev/null +++ b/packages/plugin-msw/src/msw-plugin.ts @@ -0,0 +1,348 @@ +import { http, HttpResponse } from 'msw'; +import { setupWorker } from 'msw/browser'; +import { RuntimePlugin, RuntimeContext, ObjectStackRuntimeProtocol } from '@objectstack/runtime'; + +export interface MSWPluginOptions { + /** + * Enable MSW in the browser environment + */ + enableBrowser?: boolean; + + /** + * Custom handlers to add to MSW + */ + customHandlers?: Array; + + /** + * Base URL for API endpoints + */ + baseUrl?: string; + + /** + * Whether to log requests + */ + logRequests?: boolean; +} + +/** + * ObjectStack Server Mock - Provides mock database functionality + */ +export class ObjectStackServer { + private static protocol: ObjectStackRuntimeProtocol | null = null; + private static logger: ((message: string, ...meta: any[]) => void) | null = null; + + static init(protocol: ObjectStackRuntimeProtocol, logger?: (message: string, ...meta: any[]) => void) { + this.protocol = protocol; + this.logger = logger || console.log; + } + + static async findData(object: string, params?: any) { + if (!this.protocol) { + throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.'); + } + + this.logger?.(`[MSW] Finding ${object} records`, params); + const result = await this.protocol.findData(object, params || {}); + return { + status: 200, + data: result + }; + } + + static async getData(object: string, id: string) { + if (!this.protocol) { + throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.'); + } + + this.logger?.(`[MSW] Getting ${object} record:`, id); + try { + const result = await this.protocol.getData(object, id); + return { + status: 200, + data: result + }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return { + status: 404, + data: { error: message } + }; + } + } + + static async createData(object: string, data: any) { + if (!this.protocol) { + throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.'); + } + + this.logger?.(`[MSW] Creating ${object} record:`, data); + try { + const result = await this.protocol.createData(object, data); + return { + status: 201, + data: result + }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return { + status: 400, + data: { error: message } + }; + } + } + + static async updateData(object: string, id: string, data: any) { + if (!this.protocol) { + throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.'); + } + + this.logger?.(`[MSW] Updating ${object} record ${id}:`, data); + try { + const result = await this.protocol.updateData(object, id, data); + return { + status: 200, + data: result + }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return { + status: 400, + data: { error: message } + }; + } + } + + static async deleteData(object: string, id: string) { + if (!this.protocol) { + throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.'); + } + + this.logger?.(`[MSW] Deleting ${object} record:`, id); + try { + const result = await this.protocol.deleteData(object, id); + return { + status: 200, + data: result + }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return { + status: 400, + data: { error: message } + }; + } + } + + // Legacy method names for compatibility + static async getUser(id: string) { + return this.getData('user', id); + } + + static async createUser(data: any) { + return this.createData('user', data); + } +} + +/** + * MSW Plugin for ObjectStack + * + * This plugin enables Mock Service Worker integration for testing and development. + * It automatically mocks API endpoints using the ObjectStack runtime protocol. + * + * @example + * ```typescript + * import { MSWPlugin } from '@objectstack/plugin-msw'; + * + * const runtime = new ObjectStackRuntime({ + * plugins: [ + * new MSWPlugin({ + * enableBrowser: true, + * baseUrl: '/api/v1' + * }) + * ] + * }); + * ``` + */ +export class MSWPlugin implements RuntimePlugin { + name = 'msw'; + private options: MSWPluginOptions; + private worker: any; + private handlers: Array = []; + + constructor(options: MSWPluginOptions = {}) { + this.options = { + enableBrowser: true, + baseUrl: '/api/v1', + logRequests: true, + ...options + }; + } + + install(ctx: RuntimeContext) { + const { engine } = ctx; + const protocol = new ObjectStackRuntimeProtocol(engine); + + // Initialize ObjectStackServer + ObjectStackServer.init( + protocol, + this.options.logRequests ? console.log : undefined + ); + + const baseUrl = this.options.baseUrl || '/api/v1'; + + // Define standard ObjectStack API handlers + this.handlers = [ + // Discovery endpoint + http.get(`${baseUrl}`, () => { + return HttpResponse.json(protocol.getDiscovery()); + }), + + // Meta endpoints + http.get(`${baseUrl}/meta`, () => { + return HttpResponse.json(protocol.getMetaTypes()); + }), + + http.get(`${baseUrl}/meta/:type`, ({ params }) => { + return HttpResponse.json(protocol.getMetaItems(params.type as string)); + }), + + http.get(`${baseUrl}/meta/:type/:name`, ({ params }) => { + try { + return HttpResponse.json( + protocol.getMetaItem(params.type as string, params.name as string) + ); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return HttpResponse.json({ error: message }, { status: 404 }); + } + }), + + // Data endpoints + http.get(`${baseUrl}/data/:object`, async ({ params, request }) => { + try { + const url = new URL(request.url); + const queryParams: Record = {}; + url.searchParams.forEach((value, key) => { + queryParams[key] = value; + }); + + const result = await ObjectStackServer.findData( + params.object as string, + queryParams + ); + return HttpResponse.json(result.data, { status: result.status }); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return HttpResponse.json({ error: message }, { status: 404 }); + } + }), + + http.get(`${baseUrl}/data/:object/:id`, async ({ params }) => { + try { + const result = await ObjectStackServer.getData( + params.object as string, + params.id as string + ); + return HttpResponse.json(result.data, { status: result.status }); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return HttpResponse.json({ error: message }, { status: 404 }); + } + }), + + http.post(`${baseUrl}/data/:object`, async ({ params, request }) => { + try { + const body = await request.json(); + const result = await ObjectStackServer.createData( + params.object as string, + body + ); + return HttpResponse.json(result.data, { status: result.status }); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return HttpResponse.json({ error: message }, { status: 400 }); + } + }), + + http.patch(`${baseUrl}/data/:object/:id`, async ({ params, request }) => { + try { + const body = await request.json(); + const result = await ObjectStackServer.updateData( + params.object as string, + params.id as string, + body + ); + return HttpResponse.json(result.data, { status: result.status }); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return HttpResponse.json({ error: message }, { status: 400 }); + } + }), + + http.delete(`${baseUrl}/data/:object/:id`, async ({ params }) => { + try { + const result = await ObjectStackServer.deleteData( + params.object as string, + params.id as string + ); + return HttpResponse.json(result.data, { status: result.status }); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return HttpResponse.json({ error: message }, { status: 400 }); + } + }), + + // UI Protocol endpoint + http.get(`${baseUrl}/ui/view/:object`, ({ params, request }) => { + try { + const url = new URL(request.url); + const viewType = url.searchParams.get('type') || 'list'; + const view = protocol.getUiView(params.object as string, viewType as 'list' | 'form'); + return HttpResponse.json(view); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return HttpResponse.json({ error: message }, { status: 404 }); + } + }), + + // Add custom handlers + ...(this.options.customHandlers || []) + ]; + + console.log(`[MSWPlugin] Installed ${this.handlers.length} request handlers.`); + } + + async onStart(ctx: RuntimeContext) { + if (this.options.enableBrowser && typeof window !== 'undefined') { + // Browser environment + this.worker = setupWorker(...this.handlers); + await this.worker.start({ + onUnhandledRequest: 'bypass', + }); + console.log(`[MSWPlugin] Started MSW in browser mode.`); + } else { + console.log(`[MSWPlugin] Browser mode disabled or not in browser environment.`); + } + } + + async onStop() { + if (this.worker) { + this.worker.stop(); + console.log(`[MSWPlugin] Stopped MSW worker.`); + } + } + + /** + * Get the MSW worker instance for advanced use cases + */ + getWorker() { + return this.worker; + } + + /** + * Get registered handlers + */ + getHandlers() { + return this.handlers; + } +} diff --git a/packages/plugin-msw/tsconfig.json b/packages/plugin-msw/tsconfig.json new file mode 100644 index 000000000..a58d9ee89 --- /dev/null +++ b/packages/plugin-msw/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "bundler", + "declaration": true, + "outDir": "./dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f5dd6876..4b4024875 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -173,6 +173,37 @@ importers: specifier: ^5.0.0 version: 5.9.3 + examples/msw-demo: + dependencies: + '@objectstack/driver-memory': + specifier: workspace:* + version: link:../../packages/driver-memory + '@objectstack/example-crm': + specifier: workspace:* + version: link:../crm + '@objectstack/objectql': + specifier: workspace:* + version: link:../../packages/objectql + '@objectstack/plugin-msw': + specifier: workspace:* + version: link:../../packages/plugin-msw + '@objectstack/runtime': + specifier: workspace:* + version: link:../../packages/runtime + msw: + specifier: ^2.0.0 + version: 2.12.7(@types/node@20.19.30)(typescript@5.9.3) + devDependencies: + '@types/node': + specifier: ^20.0.0 + version: 20.19.30 + ts-node: + specifier: ^10.9.1 + version: 10.9.2(@types/node@20.19.30)(typescript@5.9.3) + typescript: + specifier: ^5.0.0 + version: 5.9.3 + examples/plugin-bi: dependencies: '@objectstack/spec': @@ -254,6 +285,28 @@ importers: specifier: ^5.0.0 version: 5.9.3 + packages/plugin-msw: + dependencies: + '@objectstack/spec': + specifier: workspace:* + version: link:../spec + '@objectstack/types': + specifier: workspace:* + version: link:../types + msw: + specifier: ^2.0.0 + version: 2.12.7(@types/node@20.19.30)(typescript@5.9.3) + devDependencies: + '@objectstack/runtime': + specifier: workspace:* + version: link:../runtime + '@types/node': + specifier: ^20.0.0 + version: 20.19.30 + typescript: + specifier: ^5.0.0 + version: 5.9.3 + packages/runtime: dependencies: '@objectstack/objectql': @@ -281,7 +334,7 @@ importers: version: 20.19.30 '@vitest/coverage-v8': specifier: ^2.1.8 - version: 2.1.9(vitest@2.1.9(@types/node@20.19.30)(lightningcss@1.30.2)) + version: 2.1.9(vitest@2.1.9(@types/node@20.19.30)(lightningcss@1.30.2)(msw@2.12.7(@types/node@20.19.30)(typescript@5.9.3))) tsx: specifier: ^4.21.0 version: 4.21.0 @@ -290,7 +343,7 @@ importers: version: 5.9.3 vitest: specifier: ^2.1.8 - version: 2.1.9(@types/node@20.19.30)(lightningcss@1.30.2) + version: 2.1.9(@types/node@20.19.30)(lightningcss@1.30.2)(msw@2.12.7(@types/node@20.19.30)(typescript@5.9.3)) zod-to-json-schema: specifier: ^3.25.1 version: 3.25.1(zod@3.25.76) @@ -876,6 +929,28 @@ packages: cpu: [x64] os: [win32] + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} + engines: {node: '>=18'} + + '@inquirer/confirm@5.1.21': + resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.3.2': + resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} @@ -885,6 +960,19 @@ packages: '@types/node': optional: true + '@inquirer/figures@1.0.15': + resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} + engines: {node: '>=18'} + + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -925,6 +1013,10 @@ packages: '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + '@mswjs/interceptors@0.40.0': + resolution: {integrity: sha512-EFd6cVbHsgLa6wa4RljGj6Wk75qoHxUSyc5asLyyPSyuhIcdS2Q3Phw6ImS1q+CkALthJRShiYfKANcQMuMqsQ==} + engines: {node: '>=18'} + '@next/env@16.1.3': resolution: {integrity: sha512-BLP14oBOvZWXgfdJf9ao+VD8O30uE+x7PaV++QtACLX329WcRSJRO5YJ+Bcvu0Q+c/lei41TjSiFf6pXqnpbQA==} @@ -988,6 +1080,15 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@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==} + '@orama/orama@3.1.18': resolution: {integrity: sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA==} engines: {node: '>= 20.0.0'} @@ -1665,6 +1766,9 @@ packages: '@types/react@19.2.8': resolution: {integrity: sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==} + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -1877,9 +1981,17 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1903,6 +2015,10 @@ packages: confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -2185,6 +2301,10 @@ packages: tailwindcss: optional: true + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} @@ -2217,6 +2337,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphql@16.12.0: + resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -2236,6 +2360,9 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + hono@4.11.4: resolution: {integrity: sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==} engines: {node: '>=16.9.0'} @@ -2294,6 +2421,9 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -2658,6 +2788,20 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msw@2.12.7: + resolution: {integrity: sha512-retd5i3xCZDVWMYjHEVuKTmhqY8lSsxujjVrZiGbbdoxxIBg5S7rCuYy/YQpfrTYIxpd/o0Kyb/3H+1udBMoYg==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2718,6 +2862,9 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -2767,6 +2914,9 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} @@ -2942,6 +3092,10 @@ packages: remark@15.0.1: resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -2949,6 +3103,9 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + rettime@0.7.0: + resolution: {integrity: sha512-LPRKoHnLKd/r3dVxcwO7vhCW+orkOGj9ViueosEBK6ie89CijnfRlhaDhHq/3Hxu4CkWQtxwlBG0mzTQY6uQjw==} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -3024,9 +3181,16 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3080,6 +3244,10 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + tailwind-merge@3.4.0: resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} @@ -3132,10 +3300,21 @@ packages: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} + tldts-core@7.0.19: + resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} + + tldts@7.0.19: + resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} + hasBin: true + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + engines: {node: '>=16'} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -3168,6 +3347,10 @@ packages: resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} engines: {node: '>=4'} + type-fest@5.4.1: + resolution: {integrity: sha512-xygQcmneDyzsEuKZrFbRMne5HDqMs++aFzefrJTgEIKjQ3rekM+RPfFCVq2Gp1VIDqddoYeppCj4Pcb+RZW0GQ==} + engines: {node: '>=20'} + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -3207,6 +3390,9 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + until-async@3.0.2: + resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -3346,6 +3532,10 @@ packages: engines: {node: '>=8'} hasBin: true + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3354,6 +3544,18 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -3362,6 +3564,10 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} + engines: {node: '>=18'} + zod-to-json-schema@3.25.1: resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} peerDependencies: @@ -3842,6 +4048,28 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true + '@inquirer/ansi@1.0.2': {} + + '@inquirer/confirm@5.1.21(@types/node@20.19.30)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@20.19.30) + '@inquirer/type': 3.0.10(@types/node@20.19.30) + optionalDependencies: + '@types/node': 20.19.30 + + '@inquirer/core@10.3.2(@types/node@20.19.30)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@20.19.30) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 20.19.30 + '@inquirer/external-editor@1.0.3(@types/node@20.19.30)': dependencies: chardet: 2.1.1 @@ -3849,6 +4077,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.30 + '@inquirer/figures@1.0.15': {} + + '@inquirer/type@3.0.10(@types/node@20.19.30)': + optionalDependencies: + '@types/node': 20.19.30 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -3934,6 +4168,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@mswjs/interceptors@0.40.0': + 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 + '@next/env@16.1.3': {} '@next/swc-darwin-arm64@16.1.3': @@ -3972,6 +4215,15 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@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': {} + '@orama/orama@3.1.18': {} '@pkgjs/parseargs@0.11.0': @@ -4583,13 +4835,15 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/statuses@2.0.6': {} + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} '@ungap/structured-clone@1.3.0': {} - '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@types/node@20.19.30)(lightningcss@1.30.2))': + '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@types/node@20.19.30)(lightningcss@1.30.2)(msw@2.12.7(@types/node@20.19.30)(typescript@5.9.3)))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -4603,7 +4857,7 @@ snapshots: std-env: 3.10.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.1.9(@types/node@20.19.30)(lightningcss@1.30.2) + vitest: 2.1.9(@types/node@20.19.30)(lightningcss@1.30.2)(msw@2.12.7(@types/node@20.19.30)(typescript@5.9.3)) transitivePeerDependencies: - supports-color @@ -4620,12 +4874,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@20.19.30)(lightningcss@1.30.2))': + '@vitest/mocker@2.1.9(msw@2.12.7(@types/node@20.19.30)(typescript@5.9.3))(vite@5.4.21(@types/node@20.19.30)(lightningcss@1.30.2))': dependencies: '@vitest/spy': 2.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: + msw: 2.12.7(@types/node@20.19.30)(typescript@5.9.3) vite: 5.4.21(@types/node@20.19.30)(lightningcss@1.30.2) '@vitest/pretty-format@2.1.9': @@ -4805,8 +5060,16 @@ snapshots: dependencies: clsx: 2.1.1 + cli-width@4.1.0: {} + client-only@0.0.1: {} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clsx@2.1.1: {} collapse-white-space@2.1.0: {} @@ -4823,6 +5086,8 @@ snapshots: confbox@0.1.8: {} + cookie@1.1.1: {} + create-require@1.1.1: {} cross-spawn@7.0.6: @@ -5152,6 +5417,8 @@ snapshots: transitivePeerDependencies: - '@types/react-dom' + get-caller-file@2.0.5: {} + get-func-name@2.0.2: {} get-nonce@1.0.1: {} @@ -5188,6 +5455,8 @@ snapshots: graceful-fs@4.2.11: {} + graphql@16.12.0: {} + has-flag@4.0.0: {} hast-util-to-estree@3.1.3: @@ -5253,6 +5522,8 @@ snapshots: dependencies: '@types/hast': 3.0.4 + headers-polyfill@4.0.3: {} + hono@4.11.4: {} html-escaper@2.0.2: {} @@ -5292,6 +5563,8 @@ snapshots: is-hexadecimal@2.0.1: {} + is-node-process@1.2.0: {} + is-number@7.0.0: {} is-plain-obj@4.1.0: {} @@ -5899,6 +6172,33 @@ snapshots: ms@2.1.3: {} + msw@2.12.7(@types/node@20.19.30)(typescript@5.9.3): + dependencies: + '@inquirer/confirm': 5.1.21(@types/node@20.19.30) + '@mswjs/interceptors': 0.40.0 + '@open-draft/deferred-promise': 2.2.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.12.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 + rettime: 0.7.0 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.0 + type-fest: 5.4.1 + until-async: 3.0.2 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + + mute-stream@2.0.0: {} + nanoid@3.3.11: {} negotiator@1.0.0: {} @@ -5954,6 +6254,8 @@ snapshots: outdent@0.5.0: {} + outvariant@1.4.3: {} + p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -6001,6 +6303,8 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-to-regexp@6.3.0: {} + path-to-regexp@8.3.0: {} path-type@4.0.0: {} @@ -6212,10 +6516,14 @@ snapshots: transitivePeerDependencies: - supports-color + require-directory@2.1.1: {} + resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} + rettime@0.7.0: {} + reusify@1.1.0: {} rollup@4.55.2: @@ -6335,8 +6643,12 @@ snapshots: stackback@0.0.2: {} + statuses@2.0.2: {} + std-env@3.10.0: {} + strict-event-emitter@0.5.1: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -6387,6 +6699,8 @@ snapshots: dependencies: has-flag: 4.0.0 + tagged-tag@1.0.0: {} + tailwind-merge@3.4.0: {} tailwindcss@4.1.18: {} @@ -6422,10 +6736,20 @@ snapshots: tinyspy@3.0.2: {} + tldts-core@7.0.19: {} + + tldts@7.0.19: + dependencies: + tldts-core: 7.0.19 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + tough-cookie@6.0.0: + dependencies: + tldts: 7.0.19 + trim-lines@3.0.1: {} trough@2.2.0: {} @@ -6459,6 +6783,10 @@ snapshots: type-detect@4.1.0: {} + type-fest@5.4.1: + dependencies: + tagged-tag: 1.0.0 + typescript@5.9.3: {} ufo@1.6.3: {} @@ -6509,6 +6837,8 @@ snapshots: universalify@0.1.2: {} + until-async@3.0.2: {} + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -6624,10 +6954,10 @@ snapshots: - supports-color - terser - vitest@2.1.9(@types/node@20.19.30)(lightningcss@1.30.2): + vitest@2.1.9(@types/node@20.19.30)(lightningcss@1.30.2)(msw@2.12.7(@types/node@20.19.30)(typescript@5.9.3)): dependencies: '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@20.19.30)(lightningcss@1.30.2)) + '@vitest/mocker': 2.1.9(msw@2.12.7(@types/node@20.19.30)(typescript@5.9.3))(vite@5.4.21(@types/node@20.19.30)(lightningcss@1.30.2)) '@vitest/pretty-format': 2.1.9 '@vitest/runner': 2.1.9 '@vitest/snapshot': 2.1.9 @@ -6668,6 +6998,12 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -6680,10 +7016,26 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.2 + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yn@3.1.1: {} yocto-queue@1.2.2: {} + yoctocolors-cjs@2.1.3: {} + zod-to-json-schema@3.25.1(zod@3.25.76): dependencies: zod: 3.25.76