Skip to content

Add MSW plugin for API mocking in testing and development - #93

Merged
hotlong merged 4 commits into
mainfrom
copilot/add-msn-plugin-support
Jan 24, 2026
Merged

Add MSW plugin for API mocking in testing and development#93
hotlong merged 4 commits into
mainfrom
copilot/add-msn-plugin-support

Conversation

CopilotAI commented Jan 24, 2026

Copy link
Copy Markdown
Contributor

Implements Mock Service Worker integration for ObjectStack, enabling browser and Node.js API mocking without server infrastructure.

Implementation

New Package: @objectstack/plugin-msw

  • MSWPlugin - Runtime plugin implementing automatic handler generation for all ObjectStack endpoints
  • ObjectStackServer - Simplified mock API for common CRUD operations
  • Auto-generates handlers for discovery, metadata, data, and UI protocol endpoints

Example Package: @objectstack/example-msw-demo

  • Browser standalone usage pattern
  • Runtime integration pattern

Usage

Browser Mode (standalone):

import{setupWorker}from'msw/browser';import{http,HttpResponse}from'msw';import{ObjectStackServer}from'@objectstack/plugin-msw';ObjectStackServer.init(protocol);consthandlers=[http.get('/api/user/:id',async({ params })=>{constresult=awaitObjectStackServer.getUser(params.id);returnHttpResponse.json(result.data,{status: result.status});})];exportconstworker=setupWorker(...handlers);

Runtime Integration:

constkernel=newObjectStackKernel([newMSWPlugin({baseUrl: '/api/v1',logRequests: true})]);

Endpoints Mocked

  • /api/v1 - Discovery
  • /api/v1/meta[/:type[/:name]] - Metadata access
  • /api/v1/data/:object[/:id] - CRUD operations (GET, POST, PATCH, DELETE)
  • /api/v1/ui/view/:object - UI protocol
Original prompt

新增插件,支持 msn

// mocks/browser.js
import { setupWorker, rest } from 'msw';
import { ObjectStackServer } from '../server/mock-db';

// 1. 初始化模拟服务端
ObjectStackServer.init();

// 2. 定义路由处理器 (Handlers)
// 这就像是 Express/Koa 的路由定义,但运行在 Service Worker 中
const handlers = [

// 拦截 GET /api/user/:id
rest.get('/api/user/:id', async (req, res, ctx) => {
const { id } = req.params;

// 调用本地逻辑
const result = await ObjectStackServer.getUser(id);
// 返回构建的 Response
return res(
ctx.status(result.status),
ctx.json(result.data)
);

}),

// 拦截 POST /api/user
rest.post('/api/user', async (req, res, ctx) => {
const body = await req.json();

// 调用本地逻辑
const result = await ObjectStackServer.createUser(body);
return res(
ctx.status(result.status),
ctx.json(result.data)
);

}),
];

// 3. 创建 Worker 实例
export const worker = setupWorker(...handlers);


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@vercel

vercelBot commented Jan 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentReviewUpdated (UTC)
specErrorErrorJan 24, 2026 3:50am

Request Review

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
CopilotAIand others added 2 commits January 24, 2026 03:43
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
CopilotAI changed the title [WIP] Add plugin to support MSNAdd MSW plugin for API mocking in testing and developmentJan 24, 2026
CopilotAI requested a review from hotlongJanuary 24, 2026 03:51
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tooling size/l labels Jan 24, 2026
@hotlong
hotlong marked this pull request as ready for review January 24, 2026 04:09
CopilotAI review requested due to automatic review settings January 24, 2026 04:10
@hotlong
hotlong merged commit 5d6009d into mainJan 24, 2026
14 of 15 checks passed

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Mock Service Worker (MSW) integration to ObjectStack, enabling browser and Node.js API mocking without server infrastructure. The implementation includes a new plugin package and example usage demonstrations.

Changes:

  • New @objectstack/plugin-msw package with MSWPlugin and ObjectStackServer classes for automatic MSW handler generation
  • Example package @objectstack/example-msw-demo demonstrating both browser standalone and runtime integration patterns
  • Automatic endpoint generation for discovery, metadata, data CRUD operations, and UI protocol

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
FileDescription
packages/plugin-msw/package.jsonPackage configuration with MSW 2.0+ dependency and runtime peer dependency
packages/plugin-msw/src/msw-plugin.tsMain plugin implementing RuntimePlugin interface with handler generation logic
packages/plugin-msw/src/index.tsPackage exports for MSWPlugin, ObjectStackServer, and MSW types
packages/plugin-msw/tsconfig.jsonTypeScript configuration for ES2020 modules
packages/plugin-msw/README.mdComprehensive documentation with usage examples and API reference
packages/plugin-msw/CHANGELOG.mdVersion 0.3.1 initial release notes
examples/msw-demo/package.jsonExample package dependencies and scripts
examples/msw-demo/src/server.tsRuntime integration pattern example
examples/msw-demo/src/browser.tsBrowser standalone usage example
examples/msw-demo/tsconfig.jsonTypeScript configuration for NodeNext modules
examples/msw-demo/README.mdExample usage documentation
pnpm-lock.yamlLock file updates for new packages and MSW dependencies
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +328 to +334
async onStop() {
if (this.worker) {
this.worker.stop();
console.log(`[MSWPlugin] Stopped MSW worker.`);
}
}

CopilotAIJan 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onStop lifecycle method is not part of the RuntimePlugin interface. The interface only defines optional install and onStart methods (see packages/runtime/src/types.ts). This method will never be called by the kernel and represents dead code. Consider removing it or extending the RuntimePlugin interface to include an onStop hook if this functionality is needed.

Suggested change
asynconStop(){
if(this.worker){
this.worker.stop();
console.log(`[MSWPlugin] Stopped MSW worker.`);
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +22
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;

CopilotAIJan 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mock protocol object uses TypeScript any type casting without proper error handling or type checking. This could lead to runtime errors if the protocol methods are not properly implemented. Consider creating a proper mock that implements the expected protocol interface or using a testing library to create type-safe mocks.

Suggested change
constmockProtocol={
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
}asany;
interfaceMockProtocol{
getData(object: string,id: string): Promise<unknown>;
createData(object: string,data: unknown): Promise<unknown>;
}
constmockProtocol: MockProtocol={
getData: async(object: string,id: string)=>{
return{ id, object,name: `Mock ${object}`,status: 'active'};
},
createData: async(object: string,data: unknown)=>{
return{id: 'new-id', ...dataasRecord<string,unknown>};
},
// Add other methods as needed
};

Copilot uses AI. Check for mistakes.
/**
* Custom handlers to add to MSW
*/
customHandlers?: Array<any>;

CopilotAIJan 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Array<any> for handler types weakens type safety. MSW provides specific handler types (e.g., RequestHandler from 'msw') that should be used instead. This would provide better type checking and IDE support for consumers of this API.

Copilot uses AI. Check for mistakes.
name = 'msw';
private options: MSWPluginOptions;
private worker: any;
private handlers: Array<any> = [];

CopilotAIJan 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Array<any> for the handlers property weakens type safety. Consider using a more specific type like RequestHandler[] from MSW to provide better type checking.

Copilot uses AI. Check for mistakes.
export class MSWPlugin implements RuntimePlugin {
name = 'msw';
private options: MSWPluginOptions;
private worker: any;

CopilotAIJan 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using any type for the worker weakens type safety. Consider importing and using the proper type from MSW (e.g., SetupWorker from 'msw/browser') to provide better type checking and IDE support.

Suggested change
private worker: any;
privateworker: ReturnType<typeofsetupWorker>|null=null;

Copilot uses AI. Check for mistakes.
Comment on lines +241 to +250
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 });
}

CopilotAIJan 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling pattern here catches errors and returns a 404 status, but the getData method in ObjectStackServer.getData already handles errors and returns a response object with status and data. This creates a double error handling that could mask the original error. The outer try-catch may be unnecessary since ObjectStackServer.getData already handles errors internally.

Suggested change
try{
constresult=awaitObjectStackServer.getData(
params.objectasstring,
params.idasstring
);
returnHttpResponse.json(result.data,{status: result.status});
}catch(error){
constmessage=errorinstanceofError ? error.message : 'Unknown error';
returnHttpResponse.json({error: message},{status: 404});
}
constresult=awaitObjectStackServer.getData(
params.objectasstring,
params.idasstring
);
returnHttpResponse.json(result.data,{status: result.status});

Copilot uses AI. Check for mistakes.
* ```
*/
export class MSWPlugin implements RuntimePlugin {
name = 'msw';

CopilotAIJan 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plugin name 'msw' is inconsistent with the naming pattern used in other ObjectStack plugins. For example, HonoServerPlugin uses 'com.objectstack.server.hono' (reverse domain notation). Consider using a similar pattern like 'com.objectstack.msw' or 'com.objectstack.plugin.msw' for consistency across the codebase.

Copilot uses AI. Check for mistakes.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/ltooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@hotlong