@objectstack/plugin-msw 0.9.1 → 1.0.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @objectstack/plugin-msw
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - Major version release for ObjectStack Protocol v1.0.
8
+ - Stabilized Protocol Definitions
9
+ - Enhanced Runtime Plugin Support
10
+ - Fixed Type Compliance across Monorepo
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+ - @objectstack/spec@1.0.0
16
+ - @objectstack/runtime@1.0.0
17
+ - @objectstack/objectql@1.0.0
18
+ - @objectstack/types@1.0.0
19
+
20
+ ## 0.9.2
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies
25
+ - @objectstack/spec@0.9.2
26
+ - @objectstack/objectql@0.9.2
27
+ - @objectstack/runtime@0.9.2
28
+ - @objectstack/types@0.9.2
29
+
3
30
  ## 0.9.1
4
31
 
5
32
  ### Patch Changes
package/README.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  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.
4
4
 
5
+ ## 🤖 AI Development Context
6
+
7
+ **Role**: Test Mocking Adapter
8
+ **Usage**:
9
+ - Intercepts network requests in tests (Browser/Node).
10
+ - Simulates a real ObjectStack backend.
11
+
5
12
  ## Plugin Capabilities
6
13
 
7
14
  This plugin implements the ObjectStack plugin capability protocol:
@@ -38,8 +38,8 @@ export declare class ObjectStackServer {
38
38
  status: number;
39
39
  data: {
40
40
  object: string;
41
- id: string;
42
41
  record: Record<string, any>;
42
+ id: string;
43
43
  };
44
44
  } | {
45
45
  status: number;
@@ -51,8 +51,8 @@ export declare class ObjectStackServer {
51
51
  status: number;
52
52
  data: {
53
53
  object: string;
54
- id: string;
55
54
  record: Record<string, any>;
55
+ id: string;
56
56
  };
57
57
  } | {
58
58
  status: number;
@@ -64,8 +64,8 @@ export declare class ObjectStackServer {
64
64
  status: number;
65
65
  data: {
66
66
  object: string;
67
- id: string;
68
67
  record: Record<string, any>;
68
+ id: string;
69
69
  };
70
70
  } | {
71
71
  status: number;
@@ -86,12 +86,118 @@ export declare class ObjectStackServer {
86
86
  error: string;
87
87
  };
88
88
  }>;
89
+ static analyticsQuery(request: any): Promise<{
90
+ status: number;
91
+ data: {
92
+ data: {
93
+ fields: {
94
+ type: string;
95
+ name: string;
96
+ }[];
97
+ rows: Record<string, any>[];
98
+ sql?: string | undefined;
99
+ };
100
+ success: boolean;
101
+ error?: {
102
+ message: string;
103
+ code: string;
104
+ category?: string | undefined;
105
+ requestId?: string | undefined;
106
+ details?: any;
107
+ } | undefined;
108
+ meta?: {
109
+ timestamp: string;
110
+ duration?: number | undefined;
111
+ requestId?: string | undefined;
112
+ traceId?: string | undefined;
113
+ } | undefined;
114
+ };
115
+ } | {
116
+ status: number;
117
+ data: {
118
+ error: string;
119
+ };
120
+ }>;
121
+ static getAnalyticsMeta(request: any): Promise<{
122
+ status: number;
123
+ data: {
124
+ data: {
125
+ cubes: {
126
+ dimensions: Record<string, {
127
+ type: "string" | "number" | "boolean" | "time" | "geo";
128
+ label: string;
129
+ name: string;
130
+ sql: string;
131
+ description?: string | undefined;
132
+ granularities?: ("second" | "minute" | "hour" | "day" | "week" | "month" | "quarter" | "year")[] | undefined;
133
+ }>;
134
+ name: string;
135
+ sql: string;
136
+ measures: Record<string, {
137
+ type: "string" | "number" | "boolean" | "count" | "sum" | "avg" | "min" | "max" | "count_distinct";
138
+ label: string;
139
+ name: string;
140
+ sql: string;
141
+ description?: string | undefined;
142
+ format?: string | undefined;
143
+ filters?: {
144
+ sql: string;
145
+ }[] | undefined;
146
+ }>;
147
+ public: boolean;
148
+ joins?: Record<string, {
149
+ name: string;
150
+ sql: string;
151
+ relationship: "one_to_one" | "one_to_many" | "many_to_one";
152
+ }> | undefined;
153
+ description?: string | undefined;
154
+ title?: string | undefined;
155
+ refreshKey?: {
156
+ every?: string | undefined;
157
+ sql?: string | undefined;
158
+ } | undefined;
159
+ }[];
160
+ };
161
+ success: boolean;
162
+ error?: {
163
+ message: string;
164
+ code: string;
165
+ category?: string | undefined;
166
+ requestId?: string | undefined;
167
+ details?: any;
168
+ } | undefined;
169
+ meta?: {
170
+ timestamp: string;
171
+ duration?: number | undefined;
172
+ requestId?: string | undefined;
173
+ traceId?: string | undefined;
174
+ } | undefined;
175
+ };
176
+ } | {
177
+ status: number;
178
+ data: {
179
+ error: string;
180
+ };
181
+ }>;
182
+ static triggerAutomation(request: any): Promise<{
183
+ status: number;
184
+ data: {
185
+ success: boolean;
186
+ result?: any;
187
+ jobId?: string | undefined;
188
+ };
189
+ } | {
190
+ status: number;
191
+ data: {
192
+ error: string;
193
+ };
194
+ }>;
89
195
  static getUser(id: string): Promise<{
90
196
  status: number;
91
197
  data: {
92
198
  object: string;
93
- id: string;
94
199
  record: Record<string, any>;
200
+ id: string;
95
201
  };
96
202
  } | {
97
203
  status: number;
@@ -103,8 +209,8 @@ export declare class ObjectStackServer {
103
209
  status: number;
104
210
  data: {
105
211
  object: string;
106
- id: string;
107
212
  record: Record<string, any>;
213
+ id: string;
108
214
  };
109
215
  } | {
110
216
  status: number;
@@ -1,5 +1,70 @@
1
- import { http, HttpResponse, passthrough } from 'msw';
1
+ import { http, HttpResponse } from 'msw';
2
2
  import { setupWorker } from 'msw/browser';
3
+ // import { IDataEngine } from '@objectstack/core';
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
+ // Helper to normalize flat parameters into 'where' clause
47
+ function normalizeQuery(params) {
48
+ // If 'where' is already present, trust it
49
+ if (params.where)
50
+ return params;
51
+ const reserved = ['select', 'order', 'orderBy', 'sort', 'limit', 'skip', 'offset', 'top', 'page', 'pageSize', 'count'];
52
+ const where = {};
53
+ let hasWhere = false;
54
+ for (const key in params) {
55
+ if (!reserved.includes(key)) {
56
+ where[key] = params[key];
57
+ hasWhere = true;
58
+ }
59
+ }
60
+ if (hasWhere) {
61
+ // Keep original params but add where.
62
+ // This allows protocols that look at root properties to still work,
63
+ // while providing 'where' for strict drivers.
64
+ return { ...params, where };
65
+ }
66
+ return params;
67
+ }
3
68
  /**
4
69
  * ObjectStack Server Mock - Provides mock database functionality
5
70
  */
@@ -113,6 +178,72 @@ export class ObjectStackServer {
113
178
  };
114
179
  }
115
180
  }
181
+ static async analyticsQuery(request) {
182
+ if (!this.protocol) {
183
+ throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.');
184
+ }
185
+ this.logger?.debug?.('MSW: Executing analytics query', { request });
186
+ try {
187
+ const result = await this.protocol.analyticsQuery(request);
188
+ this.logger?.debug?.('MSW: Analytics query completed', { result });
189
+ return {
190
+ status: 200,
191
+ data: result
192
+ };
193
+ }
194
+ catch (error) {
195
+ this.logger?.error?.('MSW: Analytics query failed', error);
196
+ const message = error instanceof Error ? error.message : 'Unknown error';
197
+ return {
198
+ status: 400,
199
+ data: { error: message }
200
+ };
201
+ }
202
+ }
203
+ static async getAnalyticsMeta(request) {
204
+ if (!this.protocol) {
205
+ throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.');
206
+ }
207
+ this.logger?.debug?.('MSW: Getting analytics metadata', { request });
208
+ try {
209
+ const result = await this.protocol.getAnalyticsMeta(request);
210
+ this.logger?.debug?.('MSW: Analytics metadata retrieved', { result });
211
+ return {
212
+ status: 200,
213
+ data: result
214
+ };
215
+ }
216
+ catch (error) {
217
+ this.logger?.error?.('MSW: Analytics metadata failed', error);
218
+ const message = error instanceof Error ? error.message : 'Unknown error';
219
+ return {
220
+ status: 400,
221
+ data: { error: message }
222
+ };
223
+ }
224
+ }
225
+ static async triggerAutomation(request) {
226
+ if (!this.protocol) {
227
+ throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.');
228
+ }
229
+ this.logger?.debug?.('MSW: Triggering automation', { request });
230
+ try {
231
+ const result = await this.protocol.triggerAutomation(request);
232
+ this.logger?.info?.('MSW: Automation triggered', { result });
233
+ return {
234
+ status: 200,
235
+ data: result
236
+ };
237
+ }
238
+ catch (error) {
239
+ this.logger?.error?.('MSW: Automation trigger failed', error);
240
+ const message = error instanceof Error ? error.message : 'Unknown error';
241
+ return {
242
+ status: 400,
243
+ data: { error: message }
244
+ };
245
+ }
246
+ }
116
247
  // Legacy method names for compatibility
117
248
  static async getUser(id) {
118
249
  return this.getData('user', id);
@@ -233,9 +364,6 @@ export class MSWPlugin {
233
364
  const baseUrl = this.options.baseUrl || '/api/v1';
234
365
  // Define standard ObjectStack API handlers
235
366
  this.handlers = [
236
- // Passthrough for external resources
237
- http.get('https://fonts.googleapis.com/*', () => passthrough()),
238
- http.get('https://fonts.gstatic.com/*', () => passthrough()),
239
367
  // Discovery endpoint
240
368
  http.get(`${baseUrl}`, async () => {
241
369
  const discovery = await protocol.getDiscovery({});
@@ -250,11 +378,15 @@ export class MSWPlugin {
250
378
  });
251
379
  }),
252
380
  // Meta endpoints
253
- http.get(`${baseUrl}/meta`, async () => {
254
- return HttpResponse.json(await protocol.getMetaTypes({}));
381
+ http.get(`${baseUrl}/meta`, async ({ request }) => {
382
+ const url = new URL(request.url);
383
+ const query = parseQueryParams(url);
384
+ return HttpResponse.json(await protocol.getMetaTypes({ query }));
255
385
  }),
256
- http.get(`${baseUrl}/meta/:type`, async ({ params }) => {
257
- return HttpResponse.json(await protocol.getMetaItems({ type: params.type }));
386
+ http.get(`${baseUrl}/meta/:type`, async ({ params, request }) => {
387
+ const url = new URL(request.url);
388
+ const query = parseQueryParams(url);
389
+ return HttpResponse.json(await protocol.getMetaItems({ type: params.type, query }));
258
390
  }),
259
391
  http.get(`${baseUrl}/meta/:type/:name`, async ({ params }) => {
260
392
  try {
@@ -269,10 +401,10 @@ export class MSWPlugin {
269
401
  http.get(`${baseUrl}/data/:object`, async ({ params, request }) => {
270
402
  try {
271
403
  const url = new URL(request.url);
272
- const queryParams = {};
273
- url.searchParams.forEach((value, key) => {
274
- queryParams[key] = value;
275
- });
404
+ // Use helper to parse properly (handle multiple values, JSON strings, numbers)
405
+ const rawParams = parseQueryParams(url);
406
+ // Normalize to standard query object (move flats to 'where')
407
+ const queryParams = normalizeQuery(rawParams);
276
408
  const result = await ObjectStackServer.findData(params.object, queryParams);
277
409
  return HttpResponse.json(result.data, {
278
410
  status: result.status,
@@ -432,6 +564,42 @@ export class MSWPlugin {
432
564
  return HttpResponse.json({ error: message }, { status: 404 });
433
565
  }
434
566
  }),
567
+ // Analytics Operations
568
+ http.post(`${baseUrl}/analytics/query`, async ({ request }) => {
569
+ try {
570
+ const body = await request.json();
571
+ const result = await ObjectStackServer.analyticsQuery(body);
572
+ return HttpResponse.json(result.data, { status: result.status });
573
+ }
574
+ catch (error) {
575
+ const message = error instanceof Error ? error.message : 'Unknown error';
576
+ return HttpResponse.json({ error: message }, { status: 400 });
577
+ }
578
+ }),
579
+ http.get(`${baseUrl}/analytics/meta`, async ({ request }) => {
580
+ try {
581
+ const url = new URL(request.url);
582
+ const query = parseQueryParams(url);
583
+ const result = await ObjectStackServer.getAnalyticsMeta(query);
584
+ return HttpResponse.json(result.data, { status: result.status });
585
+ }
586
+ catch (error) {
587
+ const message = error instanceof Error ? error.message : 'Unknown error';
588
+ return HttpResponse.json({ error: message }, { status: 400 });
589
+ }
590
+ }),
591
+ // Automation Operations
592
+ http.post(`${baseUrl}/automation/trigger`, async ({ request }) => {
593
+ try {
594
+ const body = await request.json();
595
+ const result = await ObjectStackServer.triggerAutomation(body);
596
+ return HttpResponse.json(result.data, { status: result.status });
597
+ }
598
+ catch (error) {
599
+ const message = error instanceof Error ? error.message : 'Unknown error';
600
+ return HttpResponse.json({ error: message }, { status: 400 });
601
+ }
602
+ }),
435
603
  // Add custom handlers
436
604
  ...(this.options.customHandlers || [])
437
605
  ];
package/package.json CHANGED
@@ -1,23 +1,24 @@
1
1
  {
2
2
  "name": "@objectstack/plugin-msw",
3
- "version": "0.9.1",
3
+ "version": "1.0.0",
4
4
  "description": "MSW (Mock Service Worker) Plugin for ObjectStack Runtime",
5
+ "license": "Apache-2.0",
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
8
+ "peerDependencies": {
9
+ "@objectstack/runtime": "^1.0.0"
10
+ },
7
11
  "dependencies": {
8
12
  "msw": "^2.0.0",
9
- "@objectstack/spec": "0.9.1",
10
- "@objectstack/types": "0.9.1",
11
- "@objectstack/objectql": "0.9.1"
13
+ "@objectstack/spec": "1.0.0",
14
+ "@objectstack/types": "1.0.0",
15
+ "@objectstack/objectql": "1.0.0"
12
16
  },
13
17
  "devDependencies": {
14
18
  "@types/node": "^25.1.0",
15
19
  "typescript": "^5.0.0",
16
20
  "vitest": "^4.0.18",
17
- "@objectstack/runtime": "0.9.1"
18
- },
19
- "peerDependencies": {
20
- "@objectstack/runtime": "^0.9.1"
21
+ "@objectstack/runtime": "1.0.0"
21
22
  },
22
23
  "scripts": {
23
24
  "build": "tsc",
package/src/msw-plugin.ts CHANGED
@@ -10,6 +10,77 @@ import {
10
10
  import { ObjectStackProtocol } from '@objectstack/spec/api';
11
11
  // import { IDataEngine } from '@objectstack/core';
12
12
 
13
+ // Helper for parsing query parameters
14
+ function parseQueryParams(url: URL): Record<string, any> {
15
+ const params: Record<string, any> = {};
16
+ const keys = Array.from(new Set(url.searchParams.keys()));
17
+
18
+ for (const key of keys) {
19
+ const values = url.searchParams.getAll(key);
20
+ // If single value, use it directly. If multiple, keep as array.
21
+ const rawValue = values.length === 1 ? values[0] : values;
22
+
23
+ // Helper to parse individual value
24
+ const parseValue = (val: string) => {
25
+ if (val === 'true') return true;
26
+ if (val === 'false') return false;
27
+ if (val === 'null') return null;
28
+ if (val === 'undefined') return undefined;
29
+
30
+ // Try number (integers only or floats)
31
+ // Safety check: Don't convert if it loses information (like leading zeros)
32
+ const num = Number(val);
33
+ if (!isNaN(num) && val.trim() !== '' && String(num) === val) {
34
+ return num;
35
+ }
36
+
37
+ // Try JSON
38
+ if ((val.startsWith('{') && val.endsWith('}')) || (val.startsWith('[') && val.endsWith(']'))) {
39
+ try {
40
+ return JSON.parse(val);
41
+ } catch {}
42
+ }
43
+
44
+ return val;
45
+ };
46
+
47
+ if (Array.isArray(rawValue)) {
48
+ params[key] = rawValue.map(parseValue);
49
+ } else {
50
+ params[key] = parseValue(rawValue as string);
51
+ }
52
+ }
53
+
54
+ return params;
55
+ }
56
+
57
+ // Helper to normalize flat parameters into 'where' clause
58
+ function normalizeQuery(params: Record<string, any>): Record<string, any> {
59
+ // If 'where' is already present, trust it
60
+ if (params.where) return params;
61
+
62
+ const reserved = ['select', 'order', 'orderBy', 'sort', 'limit', 'skip', 'offset', 'top', 'page', 'pageSize', 'count'];
63
+ const where: Record<string, any> = {};
64
+ let hasWhere = false;
65
+
66
+ for (const key in params) {
67
+ if (!reserved.includes(key)) {
68
+ where[key] = params[key];
69
+ hasWhere = true;
70
+ }
71
+ }
72
+
73
+ if (hasWhere) {
74
+ // Keep original params but add where.
75
+ // This allows protocols that look at root properties to still work,
76
+ // while providing 'where' for strict drivers.
77
+ return { ...params, where };
78
+ }
79
+
80
+ return params;
81
+ }
82
+
83
+
13
84
  export interface MSWPluginOptions {
14
85
  /**
15
86
  * Enable MSW in the browser environment
@@ -155,6 +226,75 @@ export class ObjectStackServer {
155
226
  }
156
227
  }
157
228
 
229
+ static async analyticsQuery(request: any) {
230
+ if (!this.protocol) {
231
+ throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.');
232
+ }
233
+
234
+ this.logger?.debug?.('MSW: Executing analytics query', { request });
235
+ try {
236
+ const result = await this.protocol.analyticsQuery(request);
237
+ this.logger?.debug?.('MSW: Analytics query completed', { result });
238
+ return {
239
+ status: 200,
240
+ data: result
241
+ };
242
+ } catch (error) {
243
+ this.logger?.error?.('MSW: Analytics query failed', error);
244
+ const message = error instanceof Error ? error.message : 'Unknown error';
245
+ return {
246
+ status: 400,
247
+ data: { error: message }
248
+ };
249
+ }
250
+ }
251
+
252
+ static async getAnalyticsMeta(request: any) {
253
+ if (!this.protocol) {
254
+ throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.');
255
+ }
256
+
257
+ this.logger?.debug?.('MSW: Getting analytics metadata', { request });
258
+ try {
259
+ const result = await this.protocol.getAnalyticsMeta(request);
260
+ this.logger?.debug?.('MSW: Analytics metadata retrieved', { result });
261
+ return {
262
+ status: 200,
263
+ data: result
264
+ };
265
+ } catch (error) {
266
+ this.logger?.error?.('MSW: Analytics metadata failed', error);
267
+ const message = error instanceof Error ? error.message : 'Unknown error';
268
+ return {
269
+ status: 400,
270
+ data: { error: message }
271
+ };
272
+ }
273
+ }
274
+
275
+ static async triggerAutomation(request: any) {
276
+ if (!this.protocol) {
277
+ throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init() first.');
278
+ }
279
+
280
+ this.logger?.debug?.('MSW: Triggering automation', { request });
281
+ try {
282
+ const result = await this.protocol.triggerAutomation(request);
283
+ this.logger?.info?.('MSW: Automation triggered', { result });
284
+ return {
285
+ status: 200,
286
+ data: result
287
+ };
288
+ } catch (error) {
289
+ this.logger?.error?.('MSW: Automation trigger failed', error);
290
+ const message = error instanceof Error ? error.message : 'Unknown error';
291
+ return {
292
+ status: 400,
293
+ data: { error: message }
294
+ };
295
+ }
296
+ }
297
+
158
298
  // Legacy method names for compatibility
159
299
  static async getUser(id: string) {
160
300
  return this.getData('user', id);
@@ -293,10 +433,6 @@ export class MSWPlugin implements Plugin {
293
433
 
294
434
  // Define standard ObjectStack API handlers
295
435
  this.handlers = [
296
- // Passthrough for external resources
297
- http.get('https://fonts.googleapis.com/*', () => passthrough()),
298
- http.get('https://fonts.gstatic.com/*', () => passthrough()),
299
-
300
436
  // Discovery endpoint
301
437
  http.get(`${baseUrl}`, async () => {
302
438
  const discovery = await protocol.getDiscovery({});
@@ -312,18 +448,22 @@ export class MSWPlugin implements Plugin {
312
448
  }),
313
449
 
314
450
  // Meta endpoints
315
- http.get(`${baseUrl}/meta`, async () => {
316
- return HttpResponse.json(await protocol.getMetaTypes({}));
451
+ http.get(`${baseUrl}/meta`, async ({ request }) => {
452
+ const url = new URL(request.url);
453
+ const query = parseQueryParams(url);
454
+ return HttpResponse.json(await protocol.getMetaTypes({ query }));
317
455
  }),
318
456
 
319
- http.get(`${baseUrl}/meta/:type`, async ({ params }) => {
320
- return HttpResponse.json(await protocol.getMetaItems({ type: params.type as string }));
457
+ http.get(`${baseUrl}/meta/:type`, async ({ params, request }) => {
458
+ const url = new URL(request.url);
459
+ const query = parseQueryParams(url);
460
+ return HttpResponse.json(await protocol.getMetaItems({ type: params.type as string, query } as any));
321
461
  }),
322
462
 
323
463
  http.get(`${baseUrl}/meta/:type/:name`, async ({ params }) => {
324
464
  try {
325
465
  return HttpResponse.json(
326
- await protocol.getMetaItem({ type: params.type as string, name: params.name as string })
466
+ await protocol.getMetaItem({ type: params.type as string, name: params.name as string } as any)
327
467
  );
328
468
  } catch (error) {
329
469
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -335,10 +475,12 @@ export class MSWPlugin implements Plugin {
335
475
  http.get(`${baseUrl}/data/:object`, async ({ params, request }) => {
336
476
  try {
337
477
  const url = new URL(request.url);
338
- const queryParams: Record<string, any> = {};
339
- url.searchParams.forEach((value, key) => {
340
- queryParams[key] = value;
341
- });
478
+
479
+ // Use helper to parse properly (handle multiple values, JSON strings, numbers)
480
+ const rawParams = parseQueryParams(url);
481
+
482
+ // Normalize to standard query object (move flats to 'where')
483
+ const queryParams = normalizeQuery(rawParams);
342
484
 
343
485
  const result = await ObjectStackServer.findData(
344
486
  params.object as string,
@@ -478,7 +620,7 @@ export class MSWPlugin implements Plugin {
478
620
  type: params.type as string,
479
621
  name: params.name as string,
480
622
  cacheRequest
481
- });
623
+ } as any);
482
624
 
483
625
  if (result.notModified) {
484
626
  return new HttpResponse(null, { status: 304 });
@@ -519,6 +661,42 @@ export class MSWPlugin implements Plugin {
519
661
  }
520
662
  }),
521
663
 
664
+ // Analytics Operations
665
+ http.post(`${baseUrl}/analytics/query`, async ({ request }) => {
666
+ try {
667
+ const body = await request.json();
668
+ const result = await ObjectStackServer.analyticsQuery(body);
669
+ return HttpResponse.json(result.data, { status: result.status });
670
+ } catch (error) {
671
+ const message = error instanceof Error ? error.message : 'Unknown error';
672
+ return HttpResponse.json({ error: message }, { status: 400 });
673
+ }
674
+ }),
675
+
676
+ http.get(`${baseUrl}/analytics/meta`, async ({ request }) => {
677
+ try {
678
+ const url = new URL(request.url);
679
+ const query = parseQueryParams(url);
680
+ const result = await ObjectStackServer.getAnalyticsMeta(query);
681
+ return HttpResponse.json(result.data, { status: result.status });
682
+ } catch (error) {
683
+ const message = error instanceof Error ? error.message : 'Unknown error';
684
+ return HttpResponse.json({ error: message }, { status: 400 });
685
+ }
686
+ }),
687
+
688
+ // Automation Operations
689
+ http.post(`${baseUrl}/automation/trigger`, async ({ request }) => {
690
+ try {
691
+ const body = await request.json();
692
+ const result = await ObjectStackServer.triggerAutomation(body);
693
+ return HttpResponse.json(result.data, { status: result.status });
694
+ } catch (error) {
695
+ const message = error instanceof Error ? error.message : 'Unknown error';
696
+ return HttpResponse.json({ error: message }, { status: 400 });
697
+ }
698
+ }),
699
+
522
700
  // Add custom handlers
523
701
  ...(this.options.customHandlers || [])
524
702
  ];