@objectstack/plugin-msw 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +22 -0
- package/CHANGELOG.md +29 -0
- package/dist/{msw-plugin.d.ts → index.d.mts} +9 -5
- package/dist/index.d.ts +126 -35
- package/dist/index.js +275 -41
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +239 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +8 -8
- package/src/msw-plugin.ts +2 -2
- package/dist/msw-plugin.js +0 -286
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
> @objectstack/plugin-msw@1.0.6 build /home/runner/work/spec/spec/packages/plugins/plugin-msw
|
|
3
|
+
> tsup --config ../../../tsup.config.ts
|
|
4
|
+
|
|
5
|
+
[34mCLI[39m Building entry: src/index.ts
|
|
6
|
+
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
+
[34mCLI[39m tsup v8.5.1
|
|
8
|
+
[34mCLI[39m Using tsup config: /home/runner/work/spec/spec/tsup.config.ts
|
|
9
|
+
[34mCLI[39m Target: es2020
|
|
10
|
+
[34mCLI[39m Cleaning output folder
|
|
11
|
+
[34mESM[39m Build start
|
|
12
|
+
[34mCJS[39m Build start
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m8.76 KB[39m
|
|
14
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m18.15 KB[39m
|
|
15
|
+
[32mCJS[39m ⚡️ Build success in 57ms
|
|
16
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m7.05 KB[39m
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m16.75 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 60ms
|
|
19
|
+
[34mDTS[39m Build start
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 9816ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m3.19 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m3.19 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @objectstack/plugin-msw
|
|
2
2
|
|
|
3
|
+
## 1.0.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [a7f7b9d]
|
|
8
|
+
- @objectstack/spec@1.0.6
|
|
9
|
+
- @objectstack/core@1.0.6
|
|
10
|
+
- @objectstack/objectql@1.0.6
|
|
11
|
+
- @objectstack/runtime@1.0.6
|
|
12
|
+
- @objectstack/types@1.0.6
|
|
13
|
+
|
|
14
|
+
## 1.0.5
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- b1d24bd: refactor: migrate build system from tsc to tsup for faster builds
|
|
19
|
+
- Replaced `tsc` with `tsup` (using esbuild) across all packages
|
|
20
|
+
- Added shared `tsup.config.ts` in workspace root
|
|
21
|
+
- Added `tsup` as workspace dev dependency
|
|
22
|
+
- significantly improved build performance
|
|
23
|
+
- 877b864: fix: add SPA fallback to hono, fix msw context binding, improve runtime resilience, and fix client-react build types
|
|
24
|
+
- Updated dependencies [b1d24bd]
|
|
25
|
+
- Updated dependencies [877b864]
|
|
26
|
+
- @objectstack/core@1.0.5
|
|
27
|
+
- @objectstack/objectql@1.0.5
|
|
28
|
+
- @objectstack/runtime@1.0.5
|
|
29
|
+
- @objectstack/types@1.0.5
|
|
30
|
+
- @objectstack/spec@1.0.5
|
|
31
|
+
|
|
3
32
|
## 1.0.4
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Plugin, PluginContext } from '@objectstack/runtime';
|
|
2
2
|
import { ObjectStackProtocol } from '@objectstack/spec/api';
|
|
3
|
-
export
|
|
3
|
+
export { HttpHandler, HttpResponse } from 'msw';
|
|
4
|
+
|
|
5
|
+
interface MSWPluginOptions {
|
|
4
6
|
/**
|
|
5
7
|
* Enable MSW in the browser environment
|
|
6
8
|
*/
|
|
@@ -37,7 +39,7 @@ export interface MSWPluginOptions {
|
|
|
37
39
|
* }));
|
|
38
40
|
* ```
|
|
39
41
|
*/
|
|
40
|
-
|
|
42
|
+
declare class MSWPlugin implements Plugin {
|
|
41
43
|
name: string;
|
|
42
44
|
version: string;
|
|
43
45
|
private options;
|
|
@@ -49,11 +51,11 @@ export declare class MSWPlugin implements Plugin {
|
|
|
49
51
|
/**
|
|
50
52
|
* Init phase
|
|
51
53
|
*/
|
|
52
|
-
init(ctx: PluginContext)
|
|
54
|
+
init: (ctx: PluginContext) => Promise<void>;
|
|
53
55
|
/**
|
|
54
56
|
* Start phase
|
|
55
57
|
*/
|
|
56
|
-
start(ctx: PluginContext)
|
|
58
|
+
start: (ctx: PluginContext) => Promise<void>;
|
|
57
59
|
/**
|
|
58
60
|
* Destroy phase
|
|
59
61
|
*/
|
|
@@ -82,7 +84,7 @@ export declare class MSWPlugin implements Plugin {
|
|
|
82
84
|
/**
|
|
83
85
|
* Static helper for interacting with ObjectStack protocol in MSW handlers
|
|
84
86
|
*/
|
|
85
|
-
|
|
87
|
+
declare class ObjectStackServer {
|
|
86
88
|
private static protocol;
|
|
87
89
|
static init(protocol: ObjectStackProtocol): void;
|
|
88
90
|
private static getProtocol;
|
|
@@ -128,3 +130,5 @@ export declare class ObjectStackServer {
|
|
|
128
130
|
status: number;
|
|
129
131
|
}>;
|
|
130
132
|
}
|
|
133
|
+
|
|
134
|
+
export { MSWPlugin, type MSWPluginOptions, ObjectStackServer };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,43 +1,134 @@
|
|
|
1
|
+
import { Plugin, PluginContext } from '@objectstack/runtime';
|
|
2
|
+
import { ObjectStackProtocol } from '@objectstack/spec/api';
|
|
3
|
+
export { HttpHandler, HttpResponse } from 'msw';
|
|
4
|
+
|
|
5
|
+
interface MSWPluginOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Enable MSW in the browser environment
|
|
8
|
+
*/
|
|
9
|
+
enableBrowser?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Custom handlers to add to MSW
|
|
12
|
+
*/
|
|
13
|
+
customHandlers?: Array<any>;
|
|
14
|
+
/**
|
|
15
|
+
* Base URL for API endpoints
|
|
16
|
+
*/
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Whether to log requests
|
|
20
|
+
*/
|
|
21
|
+
logRequests?: boolean;
|
|
22
|
+
}
|
|
1
23
|
/**
|
|
2
|
-
*
|
|
24
|
+
* MSW Plugin for ObjectStack
|
|
25
|
+
|
|
3
26
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* This plugin enables seamless integration with Mock Service Worker for
|
|
7
|
-
* testing and development environments. It automatically generates MSW
|
|
8
|
-
* handlers for all ObjectStack API endpoints.
|
|
27
|
+
* This plugin enables Mock Service Worker integration for testing and development.
|
|
28
|
+
* It automatically mocks API endpoints using the ObjectStack runtime protocol.
|
|
9
29
|
*
|
|
10
30
|
* @example
|
|
11
31
|
* ```typescript
|
|
12
|
-
* import { MSWPlugin
|
|
13
|
-
* import { ObjectStackRuntime } from '@objectstack/runtime';
|
|
14
|
-
*
|
|
15
|
-
* // Use with runtime
|
|
16
|
-
* const runtime = new ObjectStackRuntime({
|
|
17
|
-
* plugins: [
|
|
18
|
-
* new MSWPlugin({
|
|
19
|
-
* enableBrowser: true,
|
|
20
|
-
* baseUrl: '/api/v1'
|
|
21
|
-
* })
|
|
22
|
-
* ]
|
|
23
|
-
* });
|
|
24
|
-
*
|
|
25
|
-
* // Or use standalone in browser
|
|
26
|
-
* import { setupWorker, http } from 'msw/browser';
|
|
27
|
-
*
|
|
28
|
-
* ObjectStackServer.init(protocol);
|
|
32
|
+
* import { MSWPlugin } from '@objectstack/plugin-msw';
|
|
29
33
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* const worker = setupWorker(...handlers);
|
|
38
|
-
* await worker.start();
|
|
34
|
+
* // With ObjectKernel
|
|
35
|
+
* const kernel = new ObjectKernel();
|
|
36
|
+
* kernel.use(new MSWPlugin({
|
|
37
|
+
* enableBrowser: true,
|
|
38
|
+
* baseUrl: '/api/v1'
|
|
39
|
+
* }));
|
|
39
40
|
* ```
|
|
40
41
|
*/
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
declare class MSWPlugin implements Plugin {
|
|
43
|
+
name: string;
|
|
44
|
+
version: string;
|
|
45
|
+
private options;
|
|
46
|
+
private worker;
|
|
47
|
+
private handlers;
|
|
48
|
+
private protocol?;
|
|
49
|
+
private dispatcher?;
|
|
50
|
+
constructor(options?: MSWPluginOptions);
|
|
51
|
+
/**
|
|
52
|
+
* Init phase
|
|
53
|
+
*/
|
|
54
|
+
init: (ctx: PluginContext) => Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Start phase
|
|
57
|
+
*/
|
|
58
|
+
start: (ctx: PluginContext) => Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Destroy phase
|
|
61
|
+
*/
|
|
62
|
+
destroy(): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Setup MSW handlers
|
|
65
|
+
*/
|
|
66
|
+
private setupHandlers;
|
|
67
|
+
/**
|
|
68
|
+
* Start the MSW worker
|
|
69
|
+
*/
|
|
70
|
+
private startWorker;
|
|
71
|
+
/**
|
|
72
|
+
* Stop the MSW worker
|
|
73
|
+
*/
|
|
74
|
+
private stopWorker;
|
|
75
|
+
/**
|
|
76
|
+
* Get the MSW worker instance for advanced use cases
|
|
77
|
+
*/
|
|
78
|
+
getWorker(): any;
|
|
79
|
+
/**
|
|
80
|
+
* Get registered handlers
|
|
81
|
+
*/
|
|
82
|
+
getHandlers(): any[];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Static helper for interacting with ObjectStack protocol in MSW handlers
|
|
86
|
+
*/
|
|
87
|
+
declare class ObjectStackServer {
|
|
88
|
+
private static protocol;
|
|
89
|
+
static init(protocol: ObjectStackProtocol): void;
|
|
90
|
+
private static getProtocol;
|
|
91
|
+
static findData(objectName: string, query?: any): Promise<{
|
|
92
|
+
data: {
|
|
93
|
+
object: string;
|
|
94
|
+
records: Record<string, any>[];
|
|
95
|
+
total?: number | undefined;
|
|
96
|
+
hasMore?: boolean | undefined;
|
|
97
|
+
};
|
|
98
|
+
status: number;
|
|
99
|
+
}>;
|
|
100
|
+
static getData(objectName: string, id: string): Promise<{
|
|
101
|
+
data: {
|
|
102
|
+
object: string;
|
|
103
|
+
record: Record<string, any>;
|
|
104
|
+
id: string;
|
|
105
|
+
};
|
|
106
|
+
status: number;
|
|
107
|
+
}>;
|
|
108
|
+
static createData(objectName: string, data: any): Promise<{
|
|
109
|
+
data: {
|
|
110
|
+
object: string;
|
|
111
|
+
record: Record<string, any>;
|
|
112
|
+
id: string;
|
|
113
|
+
};
|
|
114
|
+
status: number;
|
|
115
|
+
}>;
|
|
116
|
+
static updateData(objectName: string, id: string, data: any): Promise<{
|
|
117
|
+
data: {
|
|
118
|
+
object: string;
|
|
119
|
+
record: Record<string, any>;
|
|
120
|
+
id: string;
|
|
121
|
+
};
|
|
122
|
+
status: number;
|
|
123
|
+
}>;
|
|
124
|
+
static deleteData(objectName: string, id: string): Promise<{
|
|
125
|
+
data: {
|
|
126
|
+
object: string;
|
|
127
|
+
id: string;
|
|
128
|
+
success: boolean;
|
|
129
|
+
};
|
|
130
|
+
status: number;
|
|
131
|
+
}>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export { MSWPlugin, type MSWPluginOptions, ObjectStackServer };
|
package/dist/index.js
CHANGED
|
@@ -1,41 +1,275 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
MSWPlugin: () => MSWPlugin,
|
|
34
|
+
ObjectStackServer: () => ObjectStackServer
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/msw-plugin.ts
|
|
39
|
+
var import_msw = require("msw");
|
|
40
|
+
var import_browser = require("msw/browser");
|
|
41
|
+
var import_runtime = require("@objectstack/runtime");
|
|
42
|
+
function parseQueryParams(url) {
|
|
43
|
+
const params = {};
|
|
44
|
+
const keys = Array.from(new Set(url.searchParams.keys()));
|
|
45
|
+
for (const key of keys) {
|
|
46
|
+
const values = url.searchParams.getAll(key);
|
|
47
|
+
const rawValue = values.length === 1 ? values[0] : values;
|
|
48
|
+
const parseValue = (val) => {
|
|
49
|
+
if (val === "true") return true;
|
|
50
|
+
if (val === "false") return false;
|
|
51
|
+
if (val === "null") return null;
|
|
52
|
+
if (val === "undefined") return void 0;
|
|
53
|
+
const num = Number(val);
|
|
54
|
+
if (!isNaN(num) && val.trim() !== "" && String(num) === val) {
|
|
55
|
+
return num;
|
|
56
|
+
}
|
|
57
|
+
if (val.startsWith("{") && val.endsWith("}") || val.startsWith("[") && val.endsWith("]")) {
|
|
58
|
+
try {
|
|
59
|
+
return JSON.parse(val);
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return val;
|
|
64
|
+
};
|
|
65
|
+
if (Array.isArray(rawValue)) {
|
|
66
|
+
params[key] = rawValue.map(parseValue);
|
|
67
|
+
} else {
|
|
68
|
+
params[key] = parseValue(rawValue);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return params;
|
|
72
|
+
}
|
|
73
|
+
var MSWPlugin = class {
|
|
74
|
+
constructor(options = {}) {
|
|
75
|
+
this.name = "com.objectstack.plugin.msw";
|
|
76
|
+
this.version = "0.9.0";
|
|
77
|
+
this.handlers = [];
|
|
78
|
+
/**
|
|
79
|
+
* Init phase
|
|
80
|
+
*/
|
|
81
|
+
this.init = async (ctx) => {
|
|
82
|
+
ctx.logger.debug("Initializing MSW plugin", {
|
|
83
|
+
enableBrowser: this.options.enableBrowser,
|
|
84
|
+
baseUrl: this.options.baseUrl,
|
|
85
|
+
logRequests: this.options.logRequests
|
|
86
|
+
});
|
|
87
|
+
ctx.logger.info("MSW plugin initialized");
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Start phase
|
|
91
|
+
*/
|
|
92
|
+
this.start = async (ctx) => {
|
|
93
|
+
ctx.logger.debug("Starting MSW plugin");
|
|
94
|
+
try {
|
|
95
|
+
try {
|
|
96
|
+
this.protocol = ctx.getService("protocol");
|
|
97
|
+
ctx.logger.debug("Protocol service found from context");
|
|
98
|
+
} catch (e) {
|
|
99
|
+
}
|
|
100
|
+
if (!this.protocol) {
|
|
101
|
+
try {
|
|
102
|
+
const dataEngine = ctx.getService("objectql");
|
|
103
|
+
const { ObjectStackProtocolImplementation } = await import("@objectstack/objectql");
|
|
104
|
+
this.protocol = new ObjectStackProtocolImplementation(dataEngine);
|
|
105
|
+
ctx.logger.debug("Protocol implementation created dynamically");
|
|
106
|
+
} catch (e) {
|
|
107
|
+
if (e.code === "ERR_MODULE_NOT_FOUND") {
|
|
108
|
+
ctx.logger.warn("Module @objectstack/objectql not found. Protocol not initialized.");
|
|
109
|
+
} else {
|
|
110
|
+
throw e;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (!this.protocol) {
|
|
115
|
+
ctx.logger.warn("No ObjectStackProtocol service available. MSW will only serve static/custom handlers if configured.");
|
|
116
|
+
}
|
|
117
|
+
} catch (e) {
|
|
118
|
+
ctx.logger.error("Failed to initialize protocol", e);
|
|
119
|
+
throw new Error("[MSWPlugin] Failed to initialize protocol");
|
|
120
|
+
}
|
|
121
|
+
this.setupHandlers(ctx);
|
|
122
|
+
await this.startWorker(ctx);
|
|
123
|
+
};
|
|
124
|
+
this.options = {
|
|
125
|
+
enableBrowser: true,
|
|
126
|
+
baseUrl: "/api/v1",
|
|
127
|
+
logRequests: true,
|
|
128
|
+
...options
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Destroy phase
|
|
133
|
+
*/
|
|
134
|
+
async destroy() {
|
|
135
|
+
await this.stopWorker();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Setup MSW handlers
|
|
139
|
+
*/
|
|
140
|
+
setupHandlers(ctx) {
|
|
141
|
+
try {
|
|
142
|
+
this.dispatcher = new import_runtime.HttpDispatcher(ctx.getKernel());
|
|
143
|
+
} catch (e) {
|
|
144
|
+
ctx.logger.warn("[MSWPlugin] Could not initialize HttpDispatcher via Kernel. Falling back to simple handlers.");
|
|
145
|
+
}
|
|
146
|
+
const baseUrl = this.options.baseUrl || "/api/v1";
|
|
147
|
+
this.handlers = [
|
|
148
|
+
...this.options.customHandlers || []
|
|
149
|
+
];
|
|
150
|
+
if (this.dispatcher) {
|
|
151
|
+
const dispatcher = this.dispatcher;
|
|
152
|
+
const catchAll = async ({ request, params }) => {
|
|
153
|
+
const url = new URL(request.url);
|
|
154
|
+
let path = url.pathname;
|
|
155
|
+
if (path.startsWith(baseUrl)) {
|
|
156
|
+
path = path.slice(baseUrl.length);
|
|
157
|
+
}
|
|
158
|
+
let body = void 0;
|
|
159
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
160
|
+
try {
|
|
161
|
+
body = await request.clone().json();
|
|
162
|
+
} catch (e) {
|
|
163
|
+
try {
|
|
164
|
+
} catch (e2) {
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const query = parseQueryParams(url);
|
|
169
|
+
const result = await dispatcher.dispatch(
|
|
170
|
+
request.method,
|
|
171
|
+
path,
|
|
172
|
+
body,
|
|
173
|
+
query,
|
|
174
|
+
{ request }
|
|
175
|
+
);
|
|
176
|
+
if (result.handled) {
|
|
177
|
+
if (result.response) {
|
|
178
|
+
return import_msw.HttpResponse.json(result.response.body, {
|
|
179
|
+
status: result.response.status,
|
|
180
|
+
headers: result.response.headers
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (result.result) {
|
|
184
|
+
if (result.result.type === "redirect") {
|
|
185
|
+
return import_msw.HttpResponse.redirect(result.result.url);
|
|
186
|
+
}
|
|
187
|
+
return import_msw.HttpResponse.json(result.result);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return void 0;
|
|
191
|
+
};
|
|
192
|
+
this.handlers.push(
|
|
193
|
+
import_msw.http.all(`${baseUrl}/*`, catchAll),
|
|
194
|
+
import_msw.http.all(`${baseUrl}`, catchAll)
|
|
195
|
+
// Handle root if needed
|
|
196
|
+
);
|
|
197
|
+
ctx.logger.info("MSW handlers set up using HttpDispatcher", { baseUrl });
|
|
198
|
+
} else {
|
|
199
|
+
ctx.logger.warn("[MSWPlugin] No dispatcher available. No API routes registered.");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Start the MSW worker
|
|
204
|
+
*/
|
|
205
|
+
async startWorker(ctx) {
|
|
206
|
+
if (this.options.enableBrowser && typeof window !== "undefined") {
|
|
207
|
+
ctx.logger.debug("Starting MSW in browser mode");
|
|
208
|
+
this.worker = (0, import_browser.setupWorker)(...this.handlers);
|
|
209
|
+
await this.worker.start({
|
|
210
|
+
onUnhandledRequest: "bypass"
|
|
211
|
+
});
|
|
212
|
+
ctx.logger.info("MSW started in browser mode");
|
|
213
|
+
} else {
|
|
214
|
+
ctx.logger.debug("MSW browser mode disabled or not in browser environment");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Stop the MSW worker
|
|
219
|
+
*/
|
|
220
|
+
async stopWorker() {
|
|
221
|
+
if (this.worker) {
|
|
222
|
+
this.worker.stop();
|
|
223
|
+
console.log("[MSWPlugin] Stopped MSW worker");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get the MSW worker instance for advanced use cases
|
|
228
|
+
*/
|
|
229
|
+
getWorker() {
|
|
230
|
+
return this.worker;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get registered handlers
|
|
234
|
+
*/
|
|
235
|
+
getHandlers() {
|
|
236
|
+
return this.handlers;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
var ObjectStackServer = class {
|
|
240
|
+
static init(protocol) {
|
|
241
|
+
this.protocol = protocol;
|
|
242
|
+
}
|
|
243
|
+
static getProtocol() {
|
|
244
|
+
if (!this.protocol) {
|
|
245
|
+
throw new Error("ObjectStackServer not initialized. Call ObjectStackServer.init(protocol) first.");
|
|
246
|
+
}
|
|
247
|
+
return this.protocol;
|
|
248
|
+
}
|
|
249
|
+
static async findData(objectName, query) {
|
|
250
|
+
const body = await this.getProtocol().findData({ object: objectName, query });
|
|
251
|
+
return { data: body, status: 200 };
|
|
252
|
+
}
|
|
253
|
+
static async getData(objectName, id) {
|
|
254
|
+
const body = await this.getProtocol().getData({ object: objectName, id });
|
|
255
|
+
return { data: body, status: 200 };
|
|
256
|
+
}
|
|
257
|
+
static async createData(objectName, data) {
|
|
258
|
+
const body = await this.getProtocol().createData({ object: objectName, data });
|
|
259
|
+
return { data: body, status: 201 };
|
|
260
|
+
}
|
|
261
|
+
static async updateData(objectName, id, data) {
|
|
262
|
+
const body = await this.getProtocol().updateData({ object: objectName, id, data });
|
|
263
|
+
return { data: body, status: 200 };
|
|
264
|
+
}
|
|
265
|
+
static async deleteData(objectName, id) {
|
|
266
|
+
const body = await this.getProtocol().deleteData({ object: objectName, id });
|
|
267
|
+
return { data: body, status: 200 };
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
271
|
+
0 && (module.exports = {
|
|
272
|
+
MSWPlugin,
|
|
273
|
+
ObjectStackServer
|
|
274
|
+
});
|
|
275
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/msw-plugin.ts"],"sourcesContent":["/**\n * @objectstack/plugin-msw\n * \n * MSW (Mock Service Worker) Plugin for ObjectStack Runtime\n * \n * This plugin enables seamless integration with Mock Service Worker for\n * testing and development environments. It automatically generates MSW\n * handlers for all ObjectStack API endpoints.\n * \n * @example\n * ```typescript\n * import { MSWPlugin, ObjectStackServer } from '@objectstack/plugin-msw';\n * import { ObjectStackRuntime } from '@objectstack/runtime';\n * \n * // Use with runtime\n * const runtime = new ObjectStackRuntime({\n * plugins: [\n * new MSWPlugin({\n * enableBrowser: true,\n * baseUrl: '/api/v1'\n * })\n * ]\n * });\n * \n * // Or use standalone in browser\n * import { setupWorker, http } from 'msw/browser';\n * \n * ObjectStackServer.init(protocol);\n * \n * const handlers = [\n * http.get('/api/user/:id', async ({ params }) => {\n * const result = await ObjectStackServer.getData('user', params.id);\n * return HttpResponse.json(result.data, { status: result.status });\n * })\n * ];\n * \n * const worker = setupWorker(...handlers);\n * await worker.start();\n * ```\n */\n\nexport { MSWPlugin, ObjectStackServer } from './msw-plugin';\nexport type { MSWPluginOptions } from './msw-plugin';\n\n// Re-export MSW types for convenience\nexport type { HttpHandler, HttpResponse } from 'msw';\n","import { http, HttpResponse, passthrough } from 'msw';\nimport { setupWorker } from 'msw/browser';\nimport { \n Plugin, \n PluginContext, \n ObjectKernel,\n HttpDispatcher,\n HttpDispatcherResult\n} from '@objectstack/runtime';\n// import { ObjectStackProtocolImplementation } from '@objectstack/objectql';\nimport { ObjectStackProtocol } from '@objectstack/spec/api';\nimport { IDataEngine } from '@objectstack/core';\n\n// Helper for parsing query parameters\nfunction parseQueryParams(url: URL): Record<string, any> {\n const params: Record<string, any> = {};\n const keys = Array.from(new Set(url.searchParams.keys()));\n\n for (const key of keys) {\n const values = url.searchParams.getAll(key);\n // If single value, use it directly. If multiple, keep as array.\n const rawValue = values.length === 1 ? values[0] : values;\n \n // Helper to parse individual value\n const parseValue = (val: string) => {\n if (val === 'true') return true;\n if (val === 'false') return false;\n if (val === 'null') return null;\n if (val === 'undefined') return undefined;\n \n // Try number (integers only or floats)\n // Safety check: Don't convert if it loses information (like leading zeros)\n const num = Number(val);\n if (!isNaN(num) && val.trim() !== '' && String(num) === val) {\n return num;\n }\n \n // Try JSON\n if ((val.startsWith('{') && val.endsWith('}')) || (val.startsWith('[') && val.endsWith(']'))) {\n try {\n return JSON.parse(val);\n } catch {}\n }\n \n return val;\n };\n\n if (Array.isArray(rawValue)) {\n params[key] = rawValue.map(parseValue);\n } else {\n params[key] = parseValue(rawValue as string);\n }\n }\n \n return params;\n}\n\nexport interface MSWPluginOptions {\n /**\n * Enable MSW in the browser environment\n */\n enableBrowser?: boolean;\n \n /**\n * Custom handlers to add to MSW\n */\n customHandlers?: Array<any>;\n \n /**\n * Base URL for API endpoints\n */\n baseUrl?: string;\n \n /**\n * Whether to log requests\n */\n logRequests?: boolean;\n}\n\n/**\n * MSW Plugin for ObjectStack\n\n * \n * This plugin enables Mock Service Worker integration for testing and development.\n * It automatically mocks API endpoints using the ObjectStack runtime protocol.\n * \n * @example\n * ```typescript\n * import { MSWPlugin } from '@objectstack/plugin-msw';\n * \n * // With ObjectKernel\n * const kernel = new ObjectKernel();\n * kernel.use(new MSWPlugin({\n * enableBrowser: true,\n * baseUrl: '/api/v1'\n * }));\n * ```\n */\nexport class MSWPlugin implements Plugin {\n name = 'com.objectstack.plugin.msw';\n version = '0.9.0';\n \n private options: MSWPluginOptions;\n private worker: any;\n private handlers: Array<any> = [];\n private protocol?: ObjectStackProtocol;\n private dispatcher?: HttpDispatcher;\n\n constructor(options: MSWPluginOptions = {}) {\n this.options = {\n enableBrowser: true,\n baseUrl: '/api/v1',\n logRequests: true,\n ...options\n };\n }\n\n /**\n * Init phase\n */\n init = async (ctx: PluginContext) => {\n ctx.logger.debug('Initializing MSW plugin', { \n enableBrowser: this.options.enableBrowser,\n baseUrl: this.options.baseUrl,\n logRequests: this.options.logRequests\n });\n // Protocol will be created in start phase\n ctx.logger.info('MSW plugin initialized');\n }\n\n /**\n * Start phase\n */\n start = async (ctx: PluginContext) => {\n ctx.logger.debug('Starting MSW plugin');\n \n try {\n // 1. Try to get existing protocol service\n try {\n this.protocol = ctx.getService<ObjectStackProtocol>('protocol');\n ctx.logger.debug('Protocol service found from context');\n } catch (e) {\n // Ignore, will try to create default implementation\n }\n\n // 2. If not found, try to instantiate default implementation dynamically\n if (!this.protocol) {\n try {\n const dataEngine = ctx.getService<IDataEngine>('objectql');\n // Dynamically import ObjectStackProtocolImplementation to avoid hard dependency\n const { ObjectStackProtocolImplementation } = await import('@objectstack/objectql');\n this.protocol = new ObjectStackProtocolImplementation(dataEngine);\n ctx.logger.debug('Protocol implementation created dynamically');\n } catch (e: any) {\n if (e.code === 'ERR_MODULE_NOT_FOUND') {\n ctx.logger.warn('Module @objectstack/objectql not found. Protocol not initialized.');\n } else {\n throw e;\n }\n }\n }\n \n if (!this.protocol) {\n // Without a protocol, MSW can't serve data APIs\n ctx.logger.warn('No ObjectStackProtocol service available. MSW will only serve static/custom handlers if configured.');\n }\n\n } catch (e) {\n ctx.logger.error('Failed to initialize protocol', e as Error);\n throw new Error('[MSWPlugin] Failed to initialize protocol');\n }\n \n this.setupHandlers(ctx);\n await this.startWorker(ctx);\n }\n\n /**\n * Destroy phase\n */\n async destroy() {\n await this.stopWorker();\n }\n\n /**\n * Setup MSW handlers\n */\n private setupHandlers(ctx: PluginContext) {\n // Initialize HttpDispatcher\n try {\n this.dispatcher = new HttpDispatcher(ctx.getKernel());\n } catch (e) {\n ctx.logger.warn('[MSWPlugin] Could not initialize HttpDispatcher via Kernel. Falling back to simple handlers.');\n }\n\n const baseUrl = this.options.baseUrl || '/api/v1';\n\n // Custom handlers have priority\n this.handlers = [\n ...(this.options.customHandlers || [])\n ];\n\n if (this.dispatcher) {\n const dispatcher = this.dispatcher;\n \n // Catch-all handler for ObjectStack Runtime\n // We use a wildcard to capture all methods and paths under baseUrl\n const catchAll = async ({ request, params }: any) => {\n const url = new URL(request.url);\n // Calculate path relative to API prefix\n // e.g. /api/v1/data/contacts -> /data/contacts\n let path = url.pathname;\n if (path.startsWith(baseUrl)) {\n path = path.slice(baseUrl.length);\n }\n \n // Parse Body if present\n let body: any = undefined;\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n try {\n body = await request.clone().json();\n } catch (e) {\n try {\n // Try form data if json fails? \n // Dispatcher expects objects usually.\n // For file upload, body might be FormData logic needed?\n // For now assume JSON or text\n } catch (e2) {}\n }\n }\n\n // Parse Query\n const query = parseQueryParams(url);\n \n // Dispatch\n const result = await dispatcher.dispatch(\n request.method, \n path, \n body, \n query, \n { request }\n );\n\n if (result.handled) {\n if (result.response) {\n return HttpResponse.json(result.response.body, { \n status: result.response.status,\n headers: result.response.headers as any\n });\n }\n if (result.result) {\n // Handle special results (streams/redirects - unlikely in MSW but possible)\n if (result.result.type === 'redirect') {\n return HttpResponse.redirect(result.result.url);\n }\n // Fallback for others\n return HttpResponse.json(result.result);\n }\n }\n \n // Not handled by dispatcher (404 for this route subset)\n return undefined; // Let MSW pass through or handle next\n };\n\n this.handlers.push(\n http.all(`${baseUrl}/*`, catchAll),\n http.all(`${baseUrl}`, catchAll) // Handle root if needed\n );\n \n ctx.logger.info('MSW handlers set up using HttpDispatcher', { baseUrl });\n } else {\n ctx.logger.warn('[MSWPlugin] No dispatcher available. No API routes registered.');\n }\n }\n\n\n /**\n * Start the MSW worker\n */\n private async startWorker(ctx: PluginContext) {\n if (this.options.enableBrowser && typeof window !== 'undefined') {\n // Browser environment\n ctx.logger.debug('Starting MSW in browser mode');\n this.worker = setupWorker(...this.handlers);\n await this.worker.start({\n onUnhandledRequest: 'bypass',\n });\n ctx.logger.info('MSW started in browser mode');\n } else {\n ctx.logger.debug('MSW browser mode disabled or not in browser environment');\n }\n }\n\n /**\n * Stop the MSW worker\n */\n private async stopWorker() {\n if (this.worker) {\n this.worker.stop();\n console.log('[MSWPlugin] Stopped MSW worker');\n }\n }\n\n /**\n * Get the MSW worker instance for advanced use cases\n */\n getWorker() {\n return this.worker;\n }\n\n /**\n * Get registered handlers\n */\n getHandlers() {\n return this.handlers;\n }\n}\n\n/**\n * Static helper for interacting with ObjectStack protocol in MSW handlers\n */\nexport class ObjectStackServer {\n private static protocol: ObjectStackProtocol;\n\n static init(protocol: ObjectStackProtocol) {\n this.protocol = protocol;\n }\n\n private static getProtocol(): ObjectStackProtocol {\n if (!this.protocol) {\n throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init(protocol) first.');\n }\n return this.protocol;\n }\n\n static async findData(objectName: string, query?: any) {\n const body = await this.getProtocol().findData({ object: objectName, query });\n return { data: body, status: 200 };\n }\n\n static async getData(objectName: string, id: string) {\n const body = await this.getProtocol().getData({ object: objectName, id });\n return { data: body, status: 200 };\n }\n\n static async createData(objectName: string, data: any) {\n const body = await this.getProtocol().createData({ object: objectName, data });\n return { data: body, status: 201 };\n }\n\n static async updateData(objectName: string, id: string, data: any) {\n const body = await this.getProtocol().updateData({ object: objectName, id, data });\n return { data: body, status: 200 };\n }\n\n static async deleteData(objectName: string, id: string) {\n const body = await this.getProtocol().deleteData({ object: objectName, id });\n return { data: body, status: 200 };\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// src/msw-plugin.ts
|
|
2
|
+
import { http, HttpResponse } from "msw";
|
|
3
|
+
import { setupWorker } from "msw/browser";
|
|
4
|
+
import {
|
|
5
|
+
HttpDispatcher
|
|
6
|
+
} from "@objectstack/runtime";
|
|
7
|
+
function parseQueryParams(url) {
|
|
8
|
+
const params = {};
|
|
9
|
+
const keys = Array.from(new Set(url.searchParams.keys()));
|
|
10
|
+
for (const key of keys) {
|
|
11
|
+
const values = url.searchParams.getAll(key);
|
|
12
|
+
const rawValue = values.length === 1 ? values[0] : values;
|
|
13
|
+
const parseValue = (val) => {
|
|
14
|
+
if (val === "true") return true;
|
|
15
|
+
if (val === "false") return false;
|
|
16
|
+
if (val === "null") return null;
|
|
17
|
+
if (val === "undefined") return void 0;
|
|
18
|
+
const num = Number(val);
|
|
19
|
+
if (!isNaN(num) && val.trim() !== "" && String(num) === val) {
|
|
20
|
+
return num;
|
|
21
|
+
}
|
|
22
|
+
if (val.startsWith("{") && val.endsWith("}") || val.startsWith("[") && val.endsWith("]")) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(val);
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return val;
|
|
29
|
+
};
|
|
30
|
+
if (Array.isArray(rawValue)) {
|
|
31
|
+
params[key] = rawValue.map(parseValue);
|
|
32
|
+
} else {
|
|
33
|
+
params[key] = parseValue(rawValue);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return params;
|
|
37
|
+
}
|
|
38
|
+
var MSWPlugin = class {
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.name = "com.objectstack.plugin.msw";
|
|
41
|
+
this.version = "0.9.0";
|
|
42
|
+
this.handlers = [];
|
|
43
|
+
/**
|
|
44
|
+
* Init phase
|
|
45
|
+
*/
|
|
46
|
+
this.init = async (ctx) => {
|
|
47
|
+
ctx.logger.debug("Initializing MSW plugin", {
|
|
48
|
+
enableBrowser: this.options.enableBrowser,
|
|
49
|
+
baseUrl: this.options.baseUrl,
|
|
50
|
+
logRequests: this.options.logRequests
|
|
51
|
+
});
|
|
52
|
+
ctx.logger.info("MSW plugin initialized");
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Start phase
|
|
56
|
+
*/
|
|
57
|
+
this.start = async (ctx) => {
|
|
58
|
+
ctx.logger.debug("Starting MSW plugin");
|
|
59
|
+
try {
|
|
60
|
+
try {
|
|
61
|
+
this.protocol = ctx.getService("protocol");
|
|
62
|
+
ctx.logger.debug("Protocol service found from context");
|
|
63
|
+
} catch (e) {
|
|
64
|
+
}
|
|
65
|
+
if (!this.protocol) {
|
|
66
|
+
try {
|
|
67
|
+
const dataEngine = ctx.getService("objectql");
|
|
68
|
+
const { ObjectStackProtocolImplementation } = await import("@objectstack/objectql");
|
|
69
|
+
this.protocol = new ObjectStackProtocolImplementation(dataEngine);
|
|
70
|
+
ctx.logger.debug("Protocol implementation created dynamically");
|
|
71
|
+
} catch (e) {
|
|
72
|
+
if (e.code === "ERR_MODULE_NOT_FOUND") {
|
|
73
|
+
ctx.logger.warn("Module @objectstack/objectql not found. Protocol not initialized.");
|
|
74
|
+
} else {
|
|
75
|
+
throw e;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!this.protocol) {
|
|
80
|
+
ctx.logger.warn("No ObjectStackProtocol service available. MSW will only serve static/custom handlers if configured.");
|
|
81
|
+
}
|
|
82
|
+
} catch (e) {
|
|
83
|
+
ctx.logger.error("Failed to initialize protocol", e);
|
|
84
|
+
throw new Error("[MSWPlugin] Failed to initialize protocol");
|
|
85
|
+
}
|
|
86
|
+
this.setupHandlers(ctx);
|
|
87
|
+
await this.startWorker(ctx);
|
|
88
|
+
};
|
|
89
|
+
this.options = {
|
|
90
|
+
enableBrowser: true,
|
|
91
|
+
baseUrl: "/api/v1",
|
|
92
|
+
logRequests: true,
|
|
93
|
+
...options
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Destroy phase
|
|
98
|
+
*/
|
|
99
|
+
async destroy() {
|
|
100
|
+
await this.stopWorker();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Setup MSW handlers
|
|
104
|
+
*/
|
|
105
|
+
setupHandlers(ctx) {
|
|
106
|
+
try {
|
|
107
|
+
this.dispatcher = new HttpDispatcher(ctx.getKernel());
|
|
108
|
+
} catch (e) {
|
|
109
|
+
ctx.logger.warn("[MSWPlugin] Could not initialize HttpDispatcher via Kernel. Falling back to simple handlers.");
|
|
110
|
+
}
|
|
111
|
+
const baseUrl = this.options.baseUrl || "/api/v1";
|
|
112
|
+
this.handlers = [
|
|
113
|
+
...this.options.customHandlers || []
|
|
114
|
+
];
|
|
115
|
+
if (this.dispatcher) {
|
|
116
|
+
const dispatcher = this.dispatcher;
|
|
117
|
+
const catchAll = async ({ request, params }) => {
|
|
118
|
+
const url = new URL(request.url);
|
|
119
|
+
let path = url.pathname;
|
|
120
|
+
if (path.startsWith(baseUrl)) {
|
|
121
|
+
path = path.slice(baseUrl.length);
|
|
122
|
+
}
|
|
123
|
+
let body = void 0;
|
|
124
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
125
|
+
try {
|
|
126
|
+
body = await request.clone().json();
|
|
127
|
+
} catch (e) {
|
|
128
|
+
try {
|
|
129
|
+
} catch (e2) {
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const query = parseQueryParams(url);
|
|
134
|
+
const result = await dispatcher.dispatch(
|
|
135
|
+
request.method,
|
|
136
|
+
path,
|
|
137
|
+
body,
|
|
138
|
+
query,
|
|
139
|
+
{ request }
|
|
140
|
+
);
|
|
141
|
+
if (result.handled) {
|
|
142
|
+
if (result.response) {
|
|
143
|
+
return HttpResponse.json(result.response.body, {
|
|
144
|
+
status: result.response.status,
|
|
145
|
+
headers: result.response.headers
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (result.result) {
|
|
149
|
+
if (result.result.type === "redirect") {
|
|
150
|
+
return HttpResponse.redirect(result.result.url);
|
|
151
|
+
}
|
|
152
|
+
return HttpResponse.json(result.result);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return void 0;
|
|
156
|
+
};
|
|
157
|
+
this.handlers.push(
|
|
158
|
+
http.all(`${baseUrl}/*`, catchAll),
|
|
159
|
+
http.all(`${baseUrl}`, catchAll)
|
|
160
|
+
// Handle root if needed
|
|
161
|
+
);
|
|
162
|
+
ctx.logger.info("MSW handlers set up using HttpDispatcher", { baseUrl });
|
|
163
|
+
} else {
|
|
164
|
+
ctx.logger.warn("[MSWPlugin] No dispatcher available. No API routes registered.");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Start the MSW worker
|
|
169
|
+
*/
|
|
170
|
+
async startWorker(ctx) {
|
|
171
|
+
if (this.options.enableBrowser && typeof window !== "undefined") {
|
|
172
|
+
ctx.logger.debug("Starting MSW in browser mode");
|
|
173
|
+
this.worker = setupWorker(...this.handlers);
|
|
174
|
+
await this.worker.start({
|
|
175
|
+
onUnhandledRequest: "bypass"
|
|
176
|
+
});
|
|
177
|
+
ctx.logger.info("MSW started in browser mode");
|
|
178
|
+
} else {
|
|
179
|
+
ctx.logger.debug("MSW browser mode disabled or not in browser environment");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Stop the MSW worker
|
|
184
|
+
*/
|
|
185
|
+
async stopWorker() {
|
|
186
|
+
if (this.worker) {
|
|
187
|
+
this.worker.stop();
|
|
188
|
+
console.log("[MSWPlugin] Stopped MSW worker");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get the MSW worker instance for advanced use cases
|
|
193
|
+
*/
|
|
194
|
+
getWorker() {
|
|
195
|
+
return this.worker;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Get registered handlers
|
|
199
|
+
*/
|
|
200
|
+
getHandlers() {
|
|
201
|
+
return this.handlers;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
var ObjectStackServer = class {
|
|
205
|
+
static init(protocol) {
|
|
206
|
+
this.protocol = protocol;
|
|
207
|
+
}
|
|
208
|
+
static getProtocol() {
|
|
209
|
+
if (!this.protocol) {
|
|
210
|
+
throw new Error("ObjectStackServer not initialized. Call ObjectStackServer.init(protocol) first.");
|
|
211
|
+
}
|
|
212
|
+
return this.protocol;
|
|
213
|
+
}
|
|
214
|
+
static async findData(objectName, query) {
|
|
215
|
+
const body = await this.getProtocol().findData({ object: objectName, query });
|
|
216
|
+
return { data: body, status: 200 };
|
|
217
|
+
}
|
|
218
|
+
static async getData(objectName, id) {
|
|
219
|
+
const body = await this.getProtocol().getData({ object: objectName, id });
|
|
220
|
+
return { data: body, status: 200 };
|
|
221
|
+
}
|
|
222
|
+
static async createData(objectName, data) {
|
|
223
|
+
const body = await this.getProtocol().createData({ object: objectName, data });
|
|
224
|
+
return { data: body, status: 201 };
|
|
225
|
+
}
|
|
226
|
+
static async updateData(objectName, id, data) {
|
|
227
|
+
const body = await this.getProtocol().updateData({ object: objectName, id, data });
|
|
228
|
+
return { data: body, status: 200 };
|
|
229
|
+
}
|
|
230
|
+
static async deleteData(objectName, id) {
|
|
231
|
+
const body = await this.getProtocol().deleteData({ object: objectName, id });
|
|
232
|
+
return { data: body, status: 200 };
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
export {
|
|
236
|
+
MSWPlugin,
|
|
237
|
+
ObjectStackServer
|
|
238
|
+
};
|
|
239
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/msw-plugin.ts"],"sourcesContent":["import { http, HttpResponse, passthrough } from 'msw';\nimport { setupWorker } from 'msw/browser';\nimport { \n Plugin, \n PluginContext, \n ObjectKernel,\n HttpDispatcher,\n HttpDispatcherResult\n} from '@objectstack/runtime';\n// import { ObjectStackProtocolImplementation } from '@objectstack/objectql';\nimport { ObjectStackProtocol } from '@objectstack/spec/api';\nimport { IDataEngine } from '@objectstack/core';\n\n// Helper for parsing query parameters\nfunction parseQueryParams(url: URL): Record<string, any> {\n const params: Record<string, any> = {};\n const keys = Array.from(new Set(url.searchParams.keys()));\n\n for (const key of keys) {\n const values = url.searchParams.getAll(key);\n // If single value, use it directly. If multiple, keep as array.\n const rawValue = values.length === 1 ? values[0] : values;\n \n // Helper to parse individual value\n const parseValue = (val: string) => {\n if (val === 'true') return true;\n if (val === 'false') return false;\n if (val === 'null') return null;\n if (val === 'undefined') return undefined;\n \n // Try number (integers only or floats)\n // Safety check: Don't convert if it loses information (like leading zeros)\n const num = Number(val);\n if (!isNaN(num) && val.trim() !== '' && String(num) === val) {\n return num;\n }\n \n // Try JSON\n if ((val.startsWith('{') && val.endsWith('}')) || (val.startsWith('[') && val.endsWith(']'))) {\n try {\n return JSON.parse(val);\n } catch {}\n }\n \n return val;\n };\n\n if (Array.isArray(rawValue)) {\n params[key] = rawValue.map(parseValue);\n } else {\n params[key] = parseValue(rawValue as string);\n }\n }\n \n return params;\n}\n\nexport interface MSWPluginOptions {\n /**\n * Enable MSW in the browser environment\n */\n enableBrowser?: boolean;\n \n /**\n * Custom handlers to add to MSW\n */\n customHandlers?: Array<any>;\n \n /**\n * Base URL for API endpoints\n */\n baseUrl?: string;\n \n /**\n * Whether to log requests\n */\n logRequests?: boolean;\n}\n\n/**\n * MSW Plugin for ObjectStack\n\n * \n * This plugin enables Mock Service Worker integration for testing and development.\n * It automatically mocks API endpoints using the ObjectStack runtime protocol.\n * \n * @example\n * ```typescript\n * import { MSWPlugin } from '@objectstack/plugin-msw';\n * \n * // With ObjectKernel\n * const kernel = new ObjectKernel();\n * kernel.use(new MSWPlugin({\n * enableBrowser: true,\n * baseUrl: '/api/v1'\n * }));\n * ```\n */\nexport class MSWPlugin implements Plugin {\n name = 'com.objectstack.plugin.msw';\n version = '0.9.0';\n \n private options: MSWPluginOptions;\n private worker: any;\n private handlers: Array<any> = [];\n private protocol?: ObjectStackProtocol;\n private dispatcher?: HttpDispatcher;\n\n constructor(options: MSWPluginOptions = {}) {\n this.options = {\n enableBrowser: true,\n baseUrl: '/api/v1',\n logRequests: true,\n ...options\n };\n }\n\n /**\n * Init phase\n */\n init = async (ctx: PluginContext) => {\n ctx.logger.debug('Initializing MSW plugin', { \n enableBrowser: this.options.enableBrowser,\n baseUrl: this.options.baseUrl,\n logRequests: this.options.logRequests\n });\n // Protocol will be created in start phase\n ctx.logger.info('MSW plugin initialized');\n }\n\n /**\n * Start phase\n */\n start = async (ctx: PluginContext) => {\n ctx.logger.debug('Starting MSW plugin');\n \n try {\n // 1. Try to get existing protocol service\n try {\n this.protocol = ctx.getService<ObjectStackProtocol>('protocol');\n ctx.logger.debug('Protocol service found from context');\n } catch (e) {\n // Ignore, will try to create default implementation\n }\n\n // 2. If not found, try to instantiate default implementation dynamically\n if (!this.protocol) {\n try {\n const dataEngine = ctx.getService<IDataEngine>('objectql');\n // Dynamically import ObjectStackProtocolImplementation to avoid hard dependency\n const { ObjectStackProtocolImplementation } = await import('@objectstack/objectql');\n this.protocol = new ObjectStackProtocolImplementation(dataEngine);\n ctx.logger.debug('Protocol implementation created dynamically');\n } catch (e: any) {\n if (e.code === 'ERR_MODULE_NOT_FOUND') {\n ctx.logger.warn('Module @objectstack/objectql not found. Protocol not initialized.');\n } else {\n throw e;\n }\n }\n }\n \n if (!this.protocol) {\n // Without a protocol, MSW can't serve data APIs\n ctx.logger.warn('No ObjectStackProtocol service available. MSW will only serve static/custom handlers if configured.');\n }\n\n } catch (e) {\n ctx.logger.error('Failed to initialize protocol', e as Error);\n throw new Error('[MSWPlugin] Failed to initialize protocol');\n }\n \n this.setupHandlers(ctx);\n await this.startWorker(ctx);\n }\n\n /**\n * Destroy phase\n */\n async destroy() {\n await this.stopWorker();\n }\n\n /**\n * Setup MSW handlers\n */\n private setupHandlers(ctx: PluginContext) {\n // Initialize HttpDispatcher\n try {\n this.dispatcher = new HttpDispatcher(ctx.getKernel());\n } catch (e) {\n ctx.logger.warn('[MSWPlugin] Could not initialize HttpDispatcher via Kernel. Falling back to simple handlers.');\n }\n\n const baseUrl = this.options.baseUrl || '/api/v1';\n\n // Custom handlers have priority\n this.handlers = [\n ...(this.options.customHandlers || [])\n ];\n\n if (this.dispatcher) {\n const dispatcher = this.dispatcher;\n \n // Catch-all handler for ObjectStack Runtime\n // We use a wildcard to capture all methods and paths under baseUrl\n const catchAll = async ({ request, params }: any) => {\n const url = new URL(request.url);\n // Calculate path relative to API prefix\n // e.g. /api/v1/data/contacts -> /data/contacts\n let path = url.pathname;\n if (path.startsWith(baseUrl)) {\n path = path.slice(baseUrl.length);\n }\n \n // Parse Body if present\n let body: any = undefined;\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n try {\n body = await request.clone().json();\n } catch (e) {\n try {\n // Try form data if json fails? \n // Dispatcher expects objects usually.\n // For file upload, body might be FormData logic needed?\n // For now assume JSON or text\n } catch (e2) {}\n }\n }\n\n // Parse Query\n const query = parseQueryParams(url);\n \n // Dispatch\n const result = await dispatcher.dispatch(\n request.method, \n path, \n body, \n query, \n { request }\n );\n\n if (result.handled) {\n if (result.response) {\n return HttpResponse.json(result.response.body, { \n status: result.response.status,\n headers: result.response.headers as any\n });\n }\n if (result.result) {\n // Handle special results (streams/redirects - unlikely in MSW but possible)\n if (result.result.type === 'redirect') {\n return HttpResponse.redirect(result.result.url);\n }\n // Fallback for others\n return HttpResponse.json(result.result);\n }\n }\n \n // Not handled by dispatcher (404 for this route subset)\n return undefined; // Let MSW pass through or handle next\n };\n\n this.handlers.push(\n http.all(`${baseUrl}/*`, catchAll),\n http.all(`${baseUrl}`, catchAll) // Handle root if needed\n );\n \n ctx.logger.info('MSW handlers set up using HttpDispatcher', { baseUrl });\n } else {\n ctx.logger.warn('[MSWPlugin] No dispatcher available. No API routes registered.');\n }\n }\n\n\n /**\n * Start the MSW worker\n */\n private async startWorker(ctx: PluginContext) {\n if (this.options.enableBrowser && typeof window !== 'undefined') {\n // Browser environment\n ctx.logger.debug('Starting MSW in browser mode');\n this.worker = setupWorker(...this.handlers);\n await this.worker.start({\n onUnhandledRequest: 'bypass',\n });\n ctx.logger.info('MSW started in browser mode');\n } else {\n ctx.logger.debug('MSW browser mode disabled or not in browser environment');\n }\n }\n\n /**\n * Stop the MSW worker\n */\n private async stopWorker() {\n if (this.worker) {\n this.worker.stop();\n console.log('[MSWPlugin] Stopped MSW worker');\n }\n }\n\n /**\n * Get the MSW worker instance for advanced use cases\n */\n getWorker() {\n return this.worker;\n }\n\n /**\n * Get registered handlers\n */\n getHandlers() {\n return this.handlers;\n }\n}\n\n/**\n * Static helper for interacting with ObjectStack protocol in MSW handlers\n */\nexport class ObjectStackServer {\n private static protocol: ObjectStackProtocol;\n\n static init(protocol: ObjectStackProtocol) {\n this.protocol = protocol;\n }\n\n private static getProtocol(): ObjectStackProtocol {\n if (!this.protocol) {\n throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init(protocol) first.');\n }\n return this.protocol;\n }\n\n static async findData(objectName: string, query?: any) {\n const body = await this.getProtocol().findData({ object: objectName, query });\n return { data: body, status: 200 };\n }\n\n static async getData(objectName: string, id: string) {\n const body = await this.getProtocol().getData({ object: objectName, id });\n return { data: body, status: 200 };\n }\n\n static async createData(objectName: string, data: any) {\n const body = await this.getProtocol().createData({ object: objectName, data });\n return { data: body, status: 201 };\n }\n\n static async updateData(objectName: string, id: string, data: any) {\n const body = await this.getProtocol().updateData({ object: objectName, id, data });\n return { data: body, status: 200 };\n }\n\n static async deleteData(objectName: string, id: string) {\n const body = await this.getProtocol().deleteData({ object: objectName, id });\n return { data: body, status: 200 };\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/plugin-msw",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "MSW (Mock Service Worker) Plugin for ObjectStack Runtime",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"peerDependencies": {
|
|
9
|
-
"@objectstack/runtime": "
|
|
9
|
+
"@objectstack/runtime": "1.0.6"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"msw": "^2.0.0",
|
|
13
|
-
"@objectstack/core": "1.0.
|
|
14
|
-
"@objectstack/objectql": "1.0.
|
|
15
|
-
"@objectstack/spec": "1.0.
|
|
16
|
-
"@objectstack/types": "1.0.
|
|
13
|
+
"@objectstack/core": "1.0.6",
|
|
14
|
+
"@objectstack/objectql": "1.0.6",
|
|
15
|
+
"@objectstack/spec": "1.0.6",
|
|
16
|
+
"@objectstack/types": "1.0.6"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/node": "^25.1.0",
|
|
20
20
|
"typescript": "^5.0.0",
|
|
21
21
|
"vitest": "^4.0.18",
|
|
22
|
-
"@objectstack/runtime": "1.0.
|
|
22
|
+
"@objectstack/runtime": "1.0.6"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
|
-
"build": "
|
|
25
|
+
"build": "tsup --config ../../../tsup.config.ts",
|
|
26
26
|
"test": "vitest run"
|
|
27
27
|
}
|
|
28
28
|
}
|
package/src/msw-plugin.ts
CHANGED
|
@@ -118,7 +118,7 @@ export class MSWPlugin implements Plugin {
|
|
|
118
118
|
/**
|
|
119
119
|
* Init phase
|
|
120
120
|
*/
|
|
121
|
-
async
|
|
121
|
+
init = async (ctx: PluginContext) => {
|
|
122
122
|
ctx.logger.debug('Initializing MSW plugin', {
|
|
123
123
|
enableBrowser: this.options.enableBrowser,
|
|
124
124
|
baseUrl: this.options.baseUrl,
|
|
@@ -131,7 +131,7 @@ export class MSWPlugin implements Plugin {
|
|
|
131
131
|
/**
|
|
132
132
|
* Start phase
|
|
133
133
|
*/
|
|
134
|
-
async
|
|
134
|
+
start = async (ctx: PluginContext) => {
|
|
135
135
|
ctx.logger.debug('Starting MSW plugin');
|
|
136
136
|
|
|
137
137
|
try {
|
package/dist/msw-plugin.js
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import { http, HttpResponse } from 'msw';
|
|
2
|
-
import { setupWorker } from 'msw/browser';
|
|
3
|
-
import { HttpDispatcher } from '@objectstack/runtime';
|
|
4
|
-
// Helper for parsing query parameters
|
|
5
|
-
function parseQueryParams(url) {
|
|
6
|
-
const params = {};
|
|
7
|
-
const keys = Array.from(new Set(url.searchParams.keys()));
|
|
8
|
-
for (const key of keys) {
|
|
9
|
-
const values = url.searchParams.getAll(key);
|
|
10
|
-
// If single value, use it directly. If multiple, keep as array.
|
|
11
|
-
const rawValue = values.length === 1 ? values[0] : values;
|
|
12
|
-
// Helper to parse individual value
|
|
13
|
-
const parseValue = (val) => {
|
|
14
|
-
if (val === 'true')
|
|
15
|
-
return true;
|
|
16
|
-
if (val === 'false')
|
|
17
|
-
return false;
|
|
18
|
-
if (val === 'null')
|
|
19
|
-
return null;
|
|
20
|
-
if (val === 'undefined')
|
|
21
|
-
return undefined;
|
|
22
|
-
// Try number (integers only or floats)
|
|
23
|
-
// Safety check: Don't convert if it loses information (like leading zeros)
|
|
24
|
-
const num = Number(val);
|
|
25
|
-
if (!isNaN(num) && val.trim() !== '' && String(num) === val) {
|
|
26
|
-
return num;
|
|
27
|
-
}
|
|
28
|
-
// Try JSON
|
|
29
|
-
if ((val.startsWith('{') && val.endsWith('}')) || (val.startsWith('[') && val.endsWith(']'))) {
|
|
30
|
-
try {
|
|
31
|
-
return JSON.parse(val);
|
|
32
|
-
}
|
|
33
|
-
catch { }
|
|
34
|
-
}
|
|
35
|
-
return val;
|
|
36
|
-
};
|
|
37
|
-
if (Array.isArray(rawValue)) {
|
|
38
|
-
params[key] = rawValue.map(parseValue);
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
params[key] = parseValue(rawValue);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return params;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* MSW Plugin for ObjectStack
|
|
48
|
-
|
|
49
|
-
*
|
|
50
|
-
* This plugin enables Mock Service Worker integration for testing and development.
|
|
51
|
-
* It automatically mocks API endpoints using the ObjectStack runtime protocol.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```typescript
|
|
55
|
-
* import { MSWPlugin } from '@objectstack/plugin-msw';
|
|
56
|
-
*
|
|
57
|
-
* // With ObjectKernel
|
|
58
|
-
* const kernel = new ObjectKernel();
|
|
59
|
-
* kernel.use(new MSWPlugin({
|
|
60
|
-
* enableBrowser: true,
|
|
61
|
-
* baseUrl: '/api/v1'
|
|
62
|
-
* }));
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
export class MSWPlugin {
|
|
66
|
-
constructor(options = {}) {
|
|
67
|
-
this.name = 'com.objectstack.plugin.msw';
|
|
68
|
-
this.version = '0.9.0';
|
|
69
|
-
this.handlers = [];
|
|
70
|
-
this.options = {
|
|
71
|
-
enableBrowser: true,
|
|
72
|
-
baseUrl: '/api/v1',
|
|
73
|
-
logRequests: true,
|
|
74
|
-
...options
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Init phase
|
|
79
|
-
*/
|
|
80
|
-
async init(ctx) {
|
|
81
|
-
ctx.logger.debug('Initializing MSW plugin', {
|
|
82
|
-
enableBrowser: this.options.enableBrowser,
|
|
83
|
-
baseUrl: this.options.baseUrl,
|
|
84
|
-
logRequests: this.options.logRequests
|
|
85
|
-
});
|
|
86
|
-
// Protocol will be created in start phase
|
|
87
|
-
ctx.logger.info('MSW plugin initialized');
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Start phase
|
|
91
|
-
*/
|
|
92
|
-
async start(ctx) {
|
|
93
|
-
ctx.logger.debug('Starting MSW plugin');
|
|
94
|
-
try {
|
|
95
|
-
// 1. Try to get existing protocol service
|
|
96
|
-
try {
|
|
97
|
-
this.protocol = ctx.getService('protocol');
|
|
98
|
-
ctx.logger.debug('Protocol service found from context');
|
|
99
|
-
}
|
|
100
|
-
catch (e) {
|
|
101
|
-
// Ignore, will try to create default implementation
|
|
102
|
-
}
|
|
103
|
-
// 2. If not found, try to instantiate default implementation dynamically
|
|
104
|
-
if (!this.protocol) {
|
|
105
|
-
try {
|
|
106
|
-
const dataEngine = ctx.getService('objectql');
|
|
107
|
-
// Dynamically import ObjectStackProtocolImplementation to avoid hard dependency
|
|
108
|
-
const { ObjectStackProtocolImplementation } = await import('@objectstack/objectql');
|
|
109
|
-
this.protocol = new ObjectStackProtocolImplementation(dataEngine);
|
|
110
|
-
ctx.logger.debug('Protocol implementation created dynamically');
|
|
111
|
-
}
|
|
112
|
-
catch (e) {
|
|
113
|
-
if (e.code === 'ERR_MODULE_NOT_FOUND') {
|
|
114
|
-
ctx.logger.warn('Module @objectstack/objectql not found. Protocol not initialized.');
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
throw e;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (!this.protocol) {
|
|
122
|
-
// Without a protocol, MSW can't serve data APIs
|
|
123
|
-
ctx.logger.warn('No ObjectStackProtocol service available. MSW will only serve static/custom handlers if configured.');
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
catch (e) {
|
|
127
|
-
ctx.logger.error('Failed to initialize protocol', e);
|
|
128
|
-
throw new Error('[MSWPlugin] Failed to initialize protocol');
|
|
129
|
-
}
|
|
130
|
-
this.setupHandlers(ctx);
|
|
131
|
-
await this.startWorker(ctx);
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Destroy phase
|
|
135
|
-
*/
|
|
136
|
-
async destroy() {
|
|
137
|
-
await this.stopWorker();
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Setup MSW handlers
|
|
141
|
-
*/
|
|
142
|
-
setupHandlers(ctx) {
|
|
143
|
-
// Initialize HttpDispatcher
|
|
144
|
-
try {
|
|
145
|
-
this.dispatcher = new HttpDispatcher(ctx.getKernel());
|
|
146
|
-
}
|
|
147
|
-
catch (e) {
|
|
148
|
-
ctx.logger.warn('[MSWPlugin] Could not initialize HttpDispatcher via Kernel. Falling back to simple handlers.');
|
|
149
|
-
}
|
|
150
|
-
const baseUrl = this.options.baseUrl || '/api/v1';
|
|
151
|
-
// Custom handlers have priority
|
|
152
|
-
this.handlers = [
|
|
153
|
-
...(this.options.customHandlers || [])
|
|
154
|
-
];
|
|
155
|
-
if (this.dispatcher) {
|
|
156
|
-
const dispatcher = this.dispatcher;
|
|
157
|
-
// Catch-all handler for ObjectStack Runtime
|
|
158
|
-
// We use a wildcard to capture all methods and paths under baseUrl
|
|
159
|
-
const catchAll = async ({ request, params }) => {
|
|
160
|
-
const url = new URL(request.url);
|
|
161
|
-
// Calculate path relative to API prefix
|
|
162
|
-
// e.g. /api/v1/data/contacts -> /data/contacts
|
|
163
|
-
let path = url.pathname;
|
|
164
|
-
if (path.startsWith(baseUrl)) {
|
|
165
|
-
path = path.slice(baseUrl.length);
|
|
166
|
-
}
|
|
167
|
-
// Parse Body if present
|
|
168
|
-
let body = undefined;
|
|
169
|
-
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
|
170
|
-
try {
|
|
171
|
-
body = await request.clone().json();
|
|
172
|
-
}
|
|
173
|
-
catch (e) {
|
|
174
|
-
try {
|
|
175
|
-
// Try form data if json fails?
|
|
176
|
-
// Dispatcher expects objects usually.
|
|
177
|
-
// For file upload, body might be FormData logic needed?
|
|
178
|
-
// For now assume JSON or text
|
|
179
|
-
}
|
|
180
|
-
catch (e2) { }
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
// Parse Query
|
|
184
|
-
const query = parseQueryParams(url);
|
|
185
|
-
// Dispatch
|
|
186
|
-
const result = await dispatcher.dispatch(request.method, path, body, query, { request });
|
|
187
|
-
if (result.handled) {
|
|
188
|
-
if (result.response) {
|
|
189
|
-
return HttpResponse.json(result.response.body, {
|
|
190
|
-
status: result.response.status,
|
|
191
|
-
headers: result.response.headers
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
if (result.result) {
|
|
195
|
-
// Handle special results (streams/redirects - unlikely in MSW but possible)
|
|
196
|
-
if (result.result.type === 'redirect') {
|
|
197
|
-
return HttpResponse.redirect(result.result.url);
|
|
198
|
-
}
|
|
199
|
-
// Fallback for others
|
|
200
|
-
return HttpResponse.json(result.result);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
// Not handled by dispatcher (404 for this route subset)
|
|
204
|
-
return undefined; // Let MSW pass through or handle next
|
|
205
|
-
};
|
|
206
|
-
this.handlers.push(http.all(`${baseUrl}/*`, catchAll), http.all(`${baseUrl}`, catchAll) // Handle root if needed
|
|
207
|
-
);
|
|
208
|
-
ctx.logger.info('MSW handlers set up using HttpDispatcher', { baseUrl });
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
ctx.logger.warn('[MSWPlugin] No dispatcher available. No API routes registered.');
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Start the MSW worker
|
|
216
|
-
*/
|
|
217
|
-
async startWorker(ctx) {
|
|
218
|
-
if (this.options.enableBrowser && typeof window !== 'undefined') {
|
|
219
|
-
// Browser environment
|
|
220
|
-
ctx.logger.debug('Starting MSW in browser mode');
|
|
221
|
-
this.worker = setupWorker(...this.handlers);
|
|
222
|
-
await this.worker.start({
|
|
223
|
-
onUnhandledRequest: 'bypass',
|
|
224
|
-
});
|
|
225
|
-
ctx.logger.info('MSW started in browser mode');
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
ctx.logger.debug('MSW browser mode disabled or not in browser environment');
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Stop the MSW worker
|
|
233
|
-
*/
|
|
234
|
-
async stopWorker() {
|
|
235
|
-
if (this.worker) {
|
|
236
|
-
this.worker.stop();
|
|
237
|
-
console.log('[MSWPlugin] Stopped MSW worker');
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Get the MSW worker instance for advanced use cases
|
|
242
|
-
*/
|
|
243
|
-
getWorker() {
|
|
244
|
-
return this.worker;
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Get registered handlers
|
|
248
|
-
*/
|
|
249
|
-
getHandlers() {
|
|
250
|
-
return this.handlers;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Static helper for interacting with ObjectStack protocol in MSW handlers
|
|
255
|
-
*/
|
|
256
|
-
export class ObjectStackServer {
|
|
257
|
-
static init(protocol) {
|
|
258
|
-
this.protocol = protocol;
|
|
259
|
-
}
|
|
260
|
-
static getProtocol() {
|
|
261
|
-
if (!this.protocol) {
|
|
262
|
-
throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init(protocol) first.');
|
|
263
|
-
}
|
|
264
|
-
return this.protocol;
|
|
265
|
-
}
|
|
266
|
-
static async findData(objectName, query) {
|
|
267
|
-
const body = await this.getProtocol().findData({ object: objectName, query });
|
|
268
|
-
return { data: body, status: 200 };
|
|
269
|
-
}
|
|
270
|
-
static async getData(objectName, id) {
|
|
271
|
-
const body = await this.getProtocol().getData({ object: objectName, id });
|
|
272
|
-
return { data: body, status: 200 };
|
|
273
|
-
}
|
|
274
|
-
static async createData(objectName, data) {
|
|
275
|
-
const body = await this.getProtocol().createData({ object: objectName, data });
|
|
276
|
-
return { data: body, status: 201 };
|
|
277
|
-
}
|
|
278
|
-
static async updateData(objectName, id, data) {
|
|
279
|
-
const body = await this.getProtocol().updateData({ object: objectName, id, data });
|
|
280
|
-
return { data: body, status: 200 };
|
|
281
|
-
}
|
|
282
|
-
static async deleteData(objectName, id) {
|
|
283
|
-
const body = await this.getProtocol().deleteData({ object: objectName, id });
|
|
284
|
-
return { data: body, status: 200 };
|
|
285
|
-
}
|
|
286
|
-
}
|