@objectstack/plugin-msw 0.8.1 → 0.9.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,22 @@
1
1
  # @objectstack/plugin-msw
2
2
 
3
+ ## 0.8.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 555e6a7: Refactor: Deprecated View Storage protocol in favor of Metadata Views.
8
+
9
+ - **BREAKING**: Removed `view-storage.zod.ts` and `ViewStorage` related types from `@objectstack/spec`.
10
+ - **BREAKING**: Removed `createView`, `updateView`, `deleteView`, `listViews` from `ObjectStackProtocol` interface.
11
+ - **BREAKING**: Removed in-memory View Storage implementation from `@objectstack/objectql`.
12
+ - **UPDATE**: `@objectstack/plugin-msw` now dynamically loads `@objectstack/objectql` to avoid hard dependencies.
13
+
14
+ - Updated dependencies [555e6a7]
15
+ - @objectstack/spec@0.8.2
16
+ - @objectstack/objectql@0.8.2
17
+ - @objectstack/runtime@0.8.2
18
+ - @objectstack/types@0.8.2
19
+
3
20
  ## 0.8.1
4
21
 
5
22
  ### Patch Changes
@@ -1,6 +1,5 @@
1
1
  import { http, HttpResponse, passthrough } from 'msw';
2
2
  import { setupWorker } from 'msw/browser';
3
- import { ObjectStackProtocolImplementation } from '@objectstack/objectql';
4
3
  /**
5
4
  * ObjectStack Server Mock - Provides mock database functionality
6
5
  */
@@ -145,7 +144,7 @@ ObjectStackServer.logger = null;
145
144
  export class MSWPlugin {
146
145
  constructor(options = {}) {
147
146
  this.name = 'com.objectstack.plugin.msw';
148
- this.version = '1.0.0';
147
+ this.version = '0.9.0';
149
148
  this.handlers = [];
150
149
  this.options = {
151
150
  enableBrowser: true,
@@ -172,13 +171,40 @@ export class MSWPlugin {
172
171
  async start(ctx) {
173
172
  ctx.logger.debug('Starting MSW plugin');
174
173
  try {
175
- const dataEngine = ctx.getService('objectql');
176
- this.protocol = new ObjectStackProtocolImplementation(dataEngine);
177
- ctx.logger.debug('Protocol implementation created');
174
+ // 1. Try to get existing protocol service
175
+ try {
176
+ this.protocol = ctx.getService('protocol');
177
+ ctx.logger.debug('Protocol service found from context');
178
+ }
179
+ catch (e) {
180
+ // Ignore, will try to create default implementation
181
+ }
182
+ // 2. If not found, try to instantiate default implementation dynamically
183
+ if (!this.protocol) {
184
+ try {
185
+ const dataEngine = ctx.getService('objectql');
186
+ // Dynamically import ObjectStackProtocolImplementation to avoid hard dependency
187
+ const { ObjectStackProtocolImplementation } = await import('@objectstack/objectql');
188
+ this.protocol = new ObjectStackProtocolImplementation(dataEngine);
189
+ ctx.logger.debug('Protocol implementation created dynamically');
190
+ }
191
+ catch (e) {
192
+ if (e.code === 'ERR_MODULE_NOT_FOUND') {
193
+ ctx.logger.warn('Module @objectstack/objectql not found. Protocol not initialized.');
194
+ }
195
+ else {
196
+ throw e;
197
+ }
198
+ }
199
+ }
200
+ if (!this.protocol) {
201
+ // Without a protocol, MSW can't serve data APIs
202
+ ctx.logger.warn('No ObjectStackProtocol service available. MSW will only serve static/custom handlers if configured.');
203
+ }
178
204
  }
179
205
  catch (e) {
180
206
  ctx.logger.error('Failed to initialize protocol', e);
181
- throw new Error('[MSWPlugin] Failed to initialize protocol (missing objectql service?)');
207
+ throw new Error('[MSWPlugin] Failed to initialize protocol');
182
208
  }
183
209
  this.setupHandlers(ctx);
184
210
  await this.startWorker(ctx);
@@ -194,7 +220,11 @@ export class MSWPlugin {
194
220
  */
195
221
  setupHandlers(ctx) {
196
222
  if (!this.protocol) {
197
- throw new Error('[MSWPlugin] Protocol not initialized');
223
+ ctx.logger.warn('[MSWPlugin] Protocol not initialized. Skipping default API handlers.');
224
+ this.handlers = [
225
+ ...(this.options.customHandlers || [])
226
+ ];
227
+ return;
198
228
  }
199
229
  const protocol = this.protocol;
200
230
  // Initialize ObjectStackServer with structured logger
@@ -402,109 +432,6 @@ export class MSWPlugin {
402
432
  return HttpResponse.json({ error: message }, { status: 404 });
403
433
  }
404
434
  }),
405
- // View Storage Operations
406
- http.post(`${baseUrl}/ui/views`, async ({ request }) => {
407
- try {
408
- const body = await request.json();
409
- const result = await protocol.createView(body);
410
- if (result.success) {
411
- return HttpResponse.json(result, { status: 201 });
412
- }
413
- else {
414
- return HttpResponse.json(result, { status: 400 });
415
- }
416
- }
417
- catch (error) {
418
- const message = error instanceof Error ? error.message : 'Unknown error';
419
- return HttpResponse.json({ success: false, error: { code: 'internal_error', message } }, { status: 500 });
420
- }
421
- }),
422
- http.get(`${baseUrl}/ui/views/:id`, async ({ params }) => {
423
- try {
424
- const result = await protocol.getView({ id: params.id });
425
- if (result.success) {
426
- return HttpResponse.json(result);
427
- }
428
- else {
429
- return HttpResponse.json(result, { status: 404 });
430
- }
431
- }
432
- catch (error) {
433
- const message = error instanceof Error ? error.message : 'Unknown error';
434
- return HttpResponse.json({ success: false, error: { code: 'internal_error', message } }, { status: 500 });
435
- }
436
- }),
437
- http.get(`${baseUrl}/ui/views`, async ({ request }) => {
438
- try {
439
- const url = new URL(request.url);
440
- const queryRequest = {};
441
- if (url.searchParams.get('object'))
442
- queryRequest.object = url.searchParams.get('object');
443
- if (url.searchParams.get('type'))
444
- queryRequest.type = url.searchParams.get('type');
445
- if (url.searchParams.get('visibility'))
446
- queryRequest.visibility = url.searchParams.get('visibility');
447
- if (url.searchParams.get('createdBy'))
448
- queryRequest.createdBy = url.searchParams.get('createdBy');
449
- if (url.searchParams.get('isDefault'))
450
- queryRequest.isDefault = url.searchParams.get('isDefault') === 'true';
451
- // Parse numeric parameters with validation
452
- const limitParam = url.searchParams.get('limit');
453
- const offsetParam = url.searchParams.get('offset');
454
- if (limitParam) {
455
- const limit = parseInt(limitParam, 10);
456
- if (!isNaN(limit) && limit > 0)
457
- queryRequest.limit = limit;
458
- }
459
- if (offsetParam) {
460
- const offset = parseInt(offsetParam, 10);
461
- if (!isNaN(offset) && offset >= 0)
462
- queryRequest.offset = offset;
463
- }
464
- const result = await protocol.listViews(queryRequest);
465
- return HttpResponse.json(result);
466
- }
467
- catch (error) {
468
- const message = error instanceof Error ? error.message : 'Unknown error';
469
- return HttpResponse.json({ success: false, error: { code: 'internal_error', message } }, { status: 500 });
470
- }
471
- }),
472
- http.patch(`${baseUrl}/ui/views/:id`, async ({ params, request }) => {
473
- try {
474
- const body = await request.json();
475
- // Merge body with id parameter, ensuring body is an object
476
- const updateData = (typeof body === 'object' && body !== null)
477
- ? { ...body, id: params.id }
478
- : { id: params.id };
479
- const result = await protocol.updateView(updateData);
480
- if (result.success) {
481
- return HttpResponse.json(result);
482
- }
483
- else {
484
- const statusCode = result.error?.code === 'resource_not_found' ? 404 : 400;
485
- return HttpResponse.json(result, { status: statusCode });
486
- }
487
- }
488
- catch (error) {
489
- const message = error instanceof Error ? error.message : 'Unknown error';
490
- return HttpResponse.json({ success: false, error: { code: 'internal_error', message } }, { status: 500 });
491
- }
492
- }),
493
- http.delete(`${baseUrl}/ui/views/:id`, async ({ params }) => {
494
- try {
495
- const result = await protocol.deleteView({ id: params.id });
496
- if (result.success) {
497
- return HttpResponse.json(result);
498
- }
499
- else {
500
- return HttpResponse.json(result, { status: 404 });
501
- }
502
- }
503
- catch (error) {
504
- const message = error instanceof Error ? error.message : 'Unknown error';
505
- return HttpResponse.json({ success: false, error: { code: 'internal_error', message } }, { status: 500 });
506
- }
507
- }),
508
435
  // Add custom handlers
509
436
  ...(this.options.customHandlers || [])
510
437
  ];
package/package.json CHANGED
@@ -1,24 +1,26 @@
1
1
  {
2
2
  "name": "@objectstack/plugin-msw",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "MSW (Mock Service Worker) Plugin for ObjectStack Runtime",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "dependencies": {
8
8
  "msw": "^2.0.0",
9
- "@objectstack/spec": "0.8.1",
10
- "@objectstack/types": "0.8.1",
11
- "@objectstack/objectql": "0.8.1"
9
+ "@objectstack/spec": "0.9.0",
10
+ "@objectstack/types": "0.9.0",
11
+ "@objectstack/objectql": "0.9.0"
12
12
  },
13
13
  "devDependencies": {
14
14
  "@types/node": "^25.1.0",
15
15
  "typescript": "^5.0.0",
16
- "@objectstack/runtime": "0.8.1"
16
+ "vitest": "^4.0.18",
17
+ "@objectstack/runtime": "0.9.0"
17
18
  },
18
19
  "peerDependencies": {
19
- "@objectstack/runtime": "^0.8.1"
20
+ "@objectstack/runtime": "^0.9.0"
20
21
  },
21
22
  "scripts": {
22
- "build": "tsc"
23
+ "build": "tsc",
24
+ "test": "vitest run"
23
25
  }
24
26
  }
@@ -0,0 +1,41 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { MSWPlugin, ObjectStackServer } from './msw-plugin';
3
+
4
+ describe('MSWPlugin', () => {
5
+ it('should initialize correctly', () => {
6
+ const plugin = new MSWPlugin({ enableBrowser: false });
7
+ expect(plugin.name).toBe('com.objectstack.plugin.msw');
8
+ expect(plugin.version).toBe('0.9.0');
9
+ });
10
+
11
+ it('should handle protocol dynamic loading gracefully', async () => {
12
+ // Mock context
13
+ const context: any = {
14
+ logger: { info: vi.fn(), debug: vi.fn(), warn: vi.fn() },
15
+ getService: vi.fn().mockReturnValue(null), // No protocol service initially
16
+ registerService: vi.fn()
17
+ };
18
+
19
+ const plugin = new MSWPlugin();
20
+ await plugin.init(context);
21
+ // It should try to load ObjectStackProtocolImplementation dynamically
22
+ // Since we are in test environment, the dynamic import might fail or succeed depending on build
23
+ // But we expect it not to crash
24
+ });
25
+ });
26
+
27
+ describe('ObjectStackServer', () => {
28
+ it('should throw if used before init', async () => {
29
+ await expect(ObjectStackServer.findData('test')).rejects.toThrow('ObjectStackServer not initialized');
30
+ });
31
+
32
+ it('should delegate to protocol after init', async () => {
33
+ const protocolMock: any = {
34
+ findData: vi.fn().mockResolvedValue({ records: [] })
35
+ };
36
+ ObjectStackServer.init(protocolMock);
37
+
38
+ await ObjectStackServer.findData('test');
39
+ expect(protocolMock.findData).toHaveBeenCalled();
40
+ });
41
+ });
package/src/msw-plugin.ts CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  ObjectKernel,
7
7
  IDataEngine
8
8
  } from '@objectstack/runtime';
9
- import { ObjectStackProtocolImplementation } from '@objectstack/objectql';
9
+ // import { ObjectStackProtocolImplementation } from '@objectstack/objectql';
10
10
  import { ObjectStackProtocol } from '@objectstack/spec/api';
11
11
  // import { IDataEngine } from '@objectstack/core';
12
12
 
@@ -185,7 +185,7 @@ export class ObjectStackServer {
185
185
  */
186
186
  export class MSWPlugin implements Plugin {
187
187
  name = 'com.objectstack.plugin.msw';
188
- version = '1.0.0';
188
+ version = '0.9.0';
189
189
 
190
190
  private options: MSWPluginOptions;
191
191
  private worker: any;
@@ -221,12 +221,39 @@ export class MSWPlugin implements Plugin {
221
221
  ctx.logger.debug('Starting MSW plugin');
222
222
 
223
223
  try {
224
- const dataEngine = ctx.getService<IDataEngine>('objectql');
225
- this.protocol = new ObjectStackProtocolImplementation(dataEngine);
226
- ctx.logger.debug('Protocol implementation created');
224
+ // 1. Try to get existing protocol service
225
+ try {
226
+ this.protocol = ctx.getService<ObjectStackProtocol>('protocol');
227
+ ctx.logger.debug('Protocol service found from context');
228
+ } catch (e) {
229
+ // Ignore, will try to create default implementation
230
+ }
231
+
232
+ // 2. If not found, try to instantiate default implementation dynamically
233
+ if (!this.protocol) {
234
+ try {
235
+ const dataEngine = ctx.getService<IDataEngine>('objectql');
236
+ // Dynamically import ObjectStackProtocolImplementation to avoid hard dependency
237
+ const { ObjectStackProtocolImplementation } = await import('@objectstack/objectql');
238
+ this.protocol = new ObjectStackProtocolImplementation(dataEngine);
239
+ ctx.logger.debug('Protocol implementation created dynamically');
240
+ } catch (e: any) {
241
+ if (e.code === 'ERR_MODULE_NOT_FOUND') {
242
+ ctx.logger.warn('Module @objectstack/objectql not found. Protocol not initialized.');
243
+ } else {
244
+ throw e;
245
+ }
246
+ }
247
+ }
248
+
249
+ if (!this.protocol) {
250
+ // Without a protocol, MSW can't serve data APIs
251
+ ctx.logger.warn('No ObjectStackProtocol service available. MSW will only serve static/custom handlers if configured.');
252
+ }
253
+
227
254
  } catch (e) {
228
255
  ctx.logger.error('Failed to initialize protocol', e as Error);
229
- throw new Error('[MSWPlugin] Failed to initialize protocol (missing objectql service?)');
256
+ throw new Error('[MSWPlugin] Failed to initialize protocol');
230
257
  }
231
258
 
232
259
  this.setupHandlers(ctx);
@@ -245,7 +272,11 @@ export class MSWPlugin implements Plugin {
245
272
  */
246
273
  private setupHandlers(ctx: PluginContext) {
247
274
  if (!this.protocol) {
248
- throw new Error('[MSWPlugin] Protocol not initialized');
275
+ ctx.logger.warn('[MSWPlugin] Protocol not initialized. Skipping default API handlers.');
276
+ this.handlers = [
277
+ ...(this.options.customHandlers || [])
278
+ ];
279
+ return;
249
280
  }
250
281
 
251
282
  const protocol = this.protocol;
@@ -488,101 +519,6 @@ export class MSWPlugin implements Plugin {
488
519
  }
489
520
  }),
490
521
 
491
- // View Storage Operations
492
- http.post(`${baseUrl}/ui/views`, async ({ request }) => {
493
- try {
494
- const body = await request.json();
495
- const result = await protocol.createView(body as any);
496
- if (result.success) {
497
- return HttpResponse.json(result, { status: 201 });
498
- } else {
499
- return HttpResponse.json(result, { status: 400 });
500
- }
501
- } catch (error) {
502
- const message = error instanceof Error ? error.message : 'Unknown error';
503
- return HttpResponse.json({ success: false, error: { code: 'internal_error', message } }, { status: 500 });
504
- }
505
- }),
506
-
507
- http.get(`${baseUrl}/ui/views/:id`, async ({ params }) => {
508
- try {
509
- const result = await protocol.getView({ id: params.id as string });
510
- if (result.success) {
511
- return HttpResponse.json(result);
512
- } else {
513
- return HttpResponse.json(result, { status: 404 });
514
- }
515
- } catch (error) {
516
- const message = error instanceof Error ? error.message : 'Unknown error';
517
- return HttpResponse.json({ success: false, error: { code: 'internal_error', message } }, { status: 500 });
518
- }
519
- }),
520
-
521
- http.get(`${baseUrl}/ui/views`, async ({ request }) => {
522
- try {
523
- const url = new URL(request.url);
524
- const queryRequest: any = {};
525
- if (url.searchParams.get('object')) queryRequest.object = url.searchParams.get('object');
526
- if (url.searchParams.get('type')) queryRequest.type = url.searchParams.get('type');
527
- if (url.searchParams.get('visibility')) queryRequest.visibility = url.searchParams.get('visibility');
528
- if (url.searchParams.get('createdBy')) queryRequest.createdBy = url.searchParams.get('createdBy');
529
- if (url.searchParams.get('isDefault')) queryRequest.isDefault = url.searchParams.get('isDefault') === 'true';
530
-
531
- // Parse numeric parameters with validation
532
- const limitParam = url.searchParams.get('limit');
533
- const offsetParam = url.searchParams.get('offset');
534
- if (limitParam) {
535
- const limit = parseInt(limitParam, 10);
536
- if (!isNaN(limit) && limit > 0) queryRequest.limit = limit;
537
- }
538
- if (offsetParam) {
539
- const offset = parseInt(offsetParam, 10);
540
- if (!isNaN(offset) && offset >= 0) queryRequest.offset = offset;
541
- }
542
-
543
- const result = await protocol.listViews(queryRequest);
544
- return HttpResponse.json(result);
545
- } catch (error) {
546
- const message = error instanceof Error ? error.message : 'Unknown error';
547
- return HttpResponse.json({ success: false, error: { code: 'internal_error', message } }, { status: 500 });
548
- }
549
- }),
550
-
551
- http.patch(`${baseUrl}/ui/views/:id`, async ({ params, request }) => {
552
- try {
553
- const body = await request.json() as any;
554
- // Merge body with id parameter, ensuring body is an object
555
- const updateData = (typeof body === 'object' && body !== null)
556
- ? { ...body, id: params.id as string }
557
- : { id: params.id as string };
558
-
559
- const result = await protocol.updateView(updateData as any);
560
- if (result.success) {
561
- return HttpResponse.json(result);
562
- } else {
563
- const statusCode = result.error?.code === 'resource_not_found' ? 404 : 400;
564
- return HttpResponse.json(result, { status: statusCode });
565
- }
566
- } catch (error) {
567
- const message = error instanceof Error ? error.message : 'Unknown error';
568
- return HttpResponse.json({ success: false, error: { code: 'internal_error', message } }, { status: 500 });
569
- }
570
- }),
571
-
572
- http.delete(`${baseUrl}/ui/views/:id`, async ({ params }) => {
573
- try {
574
- const result = await protocol.deleteView({ id: params.id as string });
575
- if (result.success) {
576
- return HttpResponse.json(result);
577
- } else {
578
- return HttpResponse.json(result, { status: 404 });
579
- }
580
- } catch (error) {
581
- const message = error instanceof Error ? error.message : 'Unknown error';
582
- return HttpResponse.json({ success: false, error: { code: 'internal_error', message } }, { status: 500 });
583
- }
584
- }),
585
-
586
522
  // Add custom handlers
587
523
  ...(this.options.customHandlers || [])
588
524
  ];
package/tsconfig.json CHANGED
@@ -10,5 +10,6 @@
10
10
  "skipLibCheck": true,
11
11
  "forceConsistentCasingInFileNames": true
12
12
  },
13
- "include": ["src/**/*"]
13
+ "include": ["src/**/*"],
14
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
14
15
  }