@objectstack/plugin-msw 0.9.2 → 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 +17 -0
- package/dist/msw-plugin.d.ts +111 -5
- package/dist/msw-plugin.js +180 -12
- package/package.json +9 -8
- package/src/msw-plugin.ts +192 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
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
|
+
|
|
3
20
|
## 0.9.2
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/dist/msw-plugin.d.ts
CHANGED
|
@@ -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;
|
package/dist/msw-plugin.js
CHANGED
|
@@ -1,5 +1,70 @@
|
|
|
1
|
-
import { http, HttpResponse
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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.
|
|
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.
|
|
10
|
-
"@objectstack/types": "0.
|
|
11
|
-
"@objectstack/objectql": "0.
|
|
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.
|
|
18
|
-
},
|
|
19
|
-
"peerDependencies": {
|
|
20
|
-
"@objectstack/runtime": "^0.9.2"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
];
|