@objectstack/nextjs 4.0.3 → 4.0.5

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.
@@ -1,589 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { describe, it, expect, vi, beforeEach } from 'vitest';
4
-
5
- // Mock dispatcher instance
6
- const mockDispatcher = {
7
- getDiscoveryInfo: vi.fn().mockReturnValue({ version: '1.0', endpoints: [] }),
8
- handleAuth: vi.fn().mockResolvedValue({ handled: true, response: { body: { ok: true }, status: 200 } }),
9
- handleGraphQL: vi.fn().mockResolvedValue({ data: {} }),
10
- handleMetadata: vi.fn().mockResolvedValue({ handled: true, response: { body: { objects: [] }, status: 200 } }),
11
- handleData: vi.fn().mockResolvedValue({ handled: true, response: { body: { records: [] }, status: 200 } }),
12
- handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }),
13
- dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }),
14
- };
15
-
16
- vi.mock('@objectstack/runtime', () => {
17
- return {
18
- HttpDispatcher: function HttpDispatcher() {
19
- return mockDispatcher;
20
- },
21
- };
22
- });
23
-
24
- vi.mock('next/server', () => {
25
- class MockNextRequest {
26
- url: string;
27
- method: string;
28
- private _body: any;
29
-
30
- constructor(url: string, init?: any) {
31
- this.url = url;
32
- this.method = init?.method || 'GET';
33
- this._body = init?.body;
34
- }
35
-
36
- async json() {
37
- return this._body ? JSON.parse(this._body) : {};
38
- }
39
-
40
- async formData() {
41
- const map = new Map();
42
- map.set('file', { name: 'test.txt', type: 'text/plain' });
43
- // Return a FormData-like object with get()
44
- return { get: (key: string) => map.get(key) };
45
- }
46
- }
47
-
48
- class MockNextResponse {
49
- body: any;
50
- status: number;
51
- headers: Record<string, string>;
52
-
53
- constructor(body?: any, init?: any) {
54
- this.body = body;
55
- this.status = init?.status || 200;
56
- this.headers = init?.headers || {};
57
- }
58
-
59
- async json() {
60
- return typeof this.body === 'string' ? JSON.parse(this.body) : this.body;
61
- }
62
-
63
- static json(body: any, init?: any) {
64
- const res = new MockNextResponse(body, init);
65
- return res;
66
- }
67
-
68
- static redirect(url: string | URL) {
69
- const res = new MockNextResponse(null, { status: 307 });
70
- (res as any).redirectUrl = typeof url === 'string' ? url : url.toString();
71
- return res;
72
- }
73
- }
74
-
75
- return { NextRequest: MockNextRequest, NextResponse: MockNextResponse };
76
- });
77
-
78
- import { NextRequest } from 'next/server';
79
- import { createRouteHandler, createDiscoveryHandler } from './index';
80
-
81
- const mockKernel = { name: 'test-kernel' } as any;
82
-
83
- function makeReq(url: string, method = 'GET', body?: any) {
84
- const init: any = { method };
85
- if (body) init.body = JSON.stringify(body);
86
- return new (NextRequest as any)(url, init);
87
- }
88
-
89
- describe('createRouteHandler', () => {
90
- beforeEach(() => {
91
- vi.clearAllMocks();
92
- });
93
-
94
- describe('Discovery Endpoint', () => {
95
- it('GET with empty segments returns discovery info', async () => {
96
- const handler = createRouteHandler({ kernel: mockKernel });
97
- const req = makeReq('http://localhost/api');
98
- const res = await handler(req, { params: { objectstack: [] } });
99
- expect(res.status).toBe(200);
100
- expect(res.body).toEqual({ data: { version: '1.0', endpoints: [] } });
101
- expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/api');
102
- });
103
-
104
- it('uses custom prefix for discovery', async () => {
105
- const handler = createRouteHandler({ kernel: mockKernel, prefix: '/v2' });
106
- const req = makeReq('http://localhost/v2');
107
- const res = await handler(req, { params: { objectstack: [] } });
108
- expect(res.status).toBe(200);
109
- expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/v2');
110
- });
111
- });
112
-
113
- describe('Auth Endpoint', () => {
114
- it('POST auth/login calls handleAuth', async () => {
115
- const handler = createRouteHandler({ kernel: mockKernel });
116
- const req = makeReq('http://localhost/api/auth/login', 'POST', { email: 'a@b.com' });
117
- const res = await handler(req, { params: { objectstack: ['auth', 'login'] } });
118
- expect(res.status).toBe(200);
119
- expect(res.body).toEqual({ ok: true });
120
- expect(mockDispatcher.handleAuth).toHaveBeenCalledWith(
121
- 'login',
122
- 'POST',
123
- expect.any(Object),
124
- expect.objectContaining({ request: expect.anything() }),
125
- );
126
- });
127
-
128
- it('GET auth/callback calls handleAuth with empty body', async () => {
129
- const handler = createRouteHandler({ kernel: mockKernel });
130
- const req = makeReq('http://localhost/api/auth/callback', 'GET');
131
- const res = await handler(req, { params: { objectstack: ['auth', 'callback'] } });
132
- expect(res.status).toBe(200);
133
- expect(mockDispatcher.handleAuth).toHaveBeenCalledWith(
134
- 'callback',
135
- 'GET',
136
- {},
137
- expect.objectContaining({ request: expect.anything() }),
138
- );
139
- });
140
-
141
- it('returns error on handleAuth exception', async () => {
142
- mockDispatcher.handleAuth.mockRejectedValueOnce(
143
- Object.assign(new Error('Unauthorized'), { statusCode: 401 }),
144
- );
145
- const handler = createRouteHandler({ kernel: mockKernel });
146
- const req = makeReq('http://localhost/api/auth/login', 'POST');
147
- const res = await handler(req, { params: { objectstack: ['auth', 'login'] } });
148
- expect(res.status).toBe(401);
149
- expect(res.body.success).toBe(false);
150
- expect(res.body.error.message).toBe('Unauthorized');
151
- });
152
- });
153
-
154
- describe('Auth via AuthPlugin service', () => {
155
- it('uses kernel.getService("auth") when available', async () => {
156
- const mockHandleRequest = vi.fn().mockResolvedValue(
157
- new Response(JSON.stringify({ user: { id: '1' } }), {
158
- status: 200,
159
- headers: { 'Content-Type': 'application/json' },
160
- }),
161
- );
162
- const kernelWithAuth = {
163
- ...mockKernel,
164
- getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }),
165
- };
166
- const handler = createRouteHandler({ kernel: kernelWithAuth });
167
- const req = makeReq('http://localhost/api/auth/sign-in/email', 'POST', { email: 'a@b.com', password: 'pass' });
168
- const res = await handler(req, { params: { objectstack: ['auth', 'sign-in', 'email'] } });
169
- expect(res.status).toBe(200);
170
- expect(kernelWithAuth.getService).toHaveBeenCalledWith('auth');
171
- expect(mockHandleRequest).toHaveBeenCalled();
172
- expect(mockDispatcher.handleAuth).not.toHaveBeenCalled();
173
- });
174
-
175
- it('falls back to dispatcher.handleAuth when auth service is not available', async () => {
176
- const kernelWithoutAuth = {
177
- ...mockKernel,
178
- getService: vi.fn().mockReturnValue(null),
179
- };
180
- const handler = createRouteHandler({ kernel: kernelWithoutAuth });
181
- const req = makeReq('http://localhost/api/auth/login', 'POST', { email: 'a@b.com' });
182
- const res = await handler(req, { params: { objectstack: ['auth', 'login'] } });
183
- expect(res.status).toBe(200);
184
- expect(mockDispatcher.handleAuth).toHaveBeenCalled();
185
- });
186
-
187
- it('forwards GET requests to auth service', async () => {
188
- const mockHandleRequest = vi.fn().mockResolvedValue(
189
- new Response(JSON.stringify({ session: { token: 'abc' } }), {
190
- status: 200,
191
- headers: { 'Content-Type': 'application/json' },
192
- }),
193
- );
194
- const kernelWithAuth = {
195
- ...mockKernel,
196
- getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }),
197
- };
198
- const handler = createRouteHandler({ kernel: kernelWithAuth });
199
- const req = makeReq('http://localhost/api/auth/get-session', 'GET');
200
- const res = await handler(req, { params: { objectstack: ['auth', 'get-session'] } });
201
- expect(res.status).toBe(200);
202
- expect(mockHandleRequest).toHaveBeenCalled();
203
- });
204
-
205
- it('returns error when auth service throws', async () => {
206
- const mockHandleRequest = vi.fn().mockRejectedValue(new Error('Auth failed'));
207
- const kernelWithAuth = {
208
- ...mockKernel,
209
- getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }),
210
- };
211
- const handler = createRouteHandler({ kernel: kernelWithAuth });
212
- const req = makeReq('http://localhost/api/auth/sign-in/email', 'POST', { email: 'a@b.com' });
213
- const res = await handler(req, { params: { objectstack: ['auth', 'sign-in', 'email'] } });
214
- expect(res.status).toBe(500);
215
- expect(res.body.success).toBe(false);
216
- });
217
-
218
- it('uses kernel.getServiceAsync("auth") when available (async factory)', async () => {
219
- const mockHandleRequest = vi.fn().mockResolvedValue(
220
- new Response(JSON.stringify({ user: { id: '2' } }), {
221
- status: 200,
222
- headers: { 'Content-Type': 'application/json' },
223
- }),
224
- );
225
- const kernelWithAsyncAuth = {
226
- ...mockKernel,
227
- getServiceAsync: vi.fn().mockResolvedValue({ handleRequest: mockHandleRequest }),
228
- };
229
- const handler = createRouteHandler({ kernel: kernelWithAsyncAuth });
230
- const req = makeReq('http://localhost/api/auth/sign-in/email', 'POST', { email: 'a@b.com', password: 'pass' });
231
- const res = await handler(req, { params: { objectstack: ['auth', 'sign-in', 'email'] } });
232
- expect(res.status).toBe(200);
233
- expect(kernelWithAsyncAuth.getServiceAsync).toHaveBeenCalledWith('auth');
234
- expect(mockHandleRequest).toHaveBeenCalled();
235
- });
236
-
237
- it('falls back to dispatcher when getServiceAsync throws', async () => {
238
- const kernelWithFailingAsync = {
239
- ...mockKernel,
240
- getServiceAsync: vi.fn().mockRejectedValue(new Error("Service 'auth' not found")),
241
- };
242
- const handler = createRouteHandler({ kernel: kernelWithFailingAsync });
243
- const req = makeReq('http://localhost/api/auth/login', 'POST', { email: 'a@b.com' });
244
- const res = await handler(req, { params: { objectstack: ['auth', 'login'] } });
245
- expect(res.status).toBe(200);
246
- expect(mockDispatcher.handleAuth).toHaveBeenCalled();
247
- });
248
- });
249
-
250
- describe('GraphQL Endpoint', () => {
251
- it('POST graphql calls handleGraphQL', async () => {
252
- const handler = createRouteHandler({ kernel: mockKernel });
253
- const body = { query: '{ objects { name } }' };
254
- const req = makeReq('http://localhost/api/graphql', 'POST', body);
255
- const res = await handler(req, { params: { objectstack: ['graphql'] } });
256
- expect(res.status).toBe(200);
257
- expect(res.body).toEqual({ data: {} });
258
- expect(mockDispatcher.handleGraphQL).toHaveBeenCalledWith(
259
- body,
260
- expect.objectContaining({ request: expect.anything() }),
261
- );
262
- });
263
-
264
- it('returns error on handleGraphQL exception', async () => {
265
- mockDispatcher.handleGraphQL.mockRejectedValueOnce(new Error('Parse error'));
266
- const handler = createRouteHandler({ kernel: mockKernel });
267
- const req = makeReq('http://localhost/api/graphql', 'POST', { query: 'bad' });
268
- const res = await handler(req, { params: { objectstack: ['graphql'] } });
269
- expect(res.status).toBe(500);
270
- expect(res.body.success).toBe(false);
271
- expect(res.body.error.message).toBe('Parse error');
272
- });
273
- });
274
-
275
- describe('Metadata Endpoint', () => {
276
- it('GET meta/objects delegates to dispatch()', async () => {
277
- const handler = createRouteHandler({ kernel: mockKernel });
278
- const req = makeReq('http://localhost/api/meta/objects', 'GET');
279
- const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
280
- expect(res.status).toBe(200);
281
- expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
282
- 'GET',
283
- '/meta/objects',
284
- undefined,
285
- expect.any(Object),
286
- expect.objectContaining({ request: expect.anything() }),
287
- '/api',
288
- );
289
- });
290
-
291
- it('PUT meta/objects parses JSON body', async () => {
292
- const handler = createRouteHandler({ kernel: mockKernel });
293
- const body = { name: 'test_object' };
294
- const req = makeReq('http://localhost/api/meta/objects', 'PUT', body);
295
- const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
296
- expect(res.status).toBe(200);
297
- expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
298
- 'PUT',
299
- '/meta/objects',
300
- body,
301
- expect.any(Object),
302
- expect.objectContaining({ request: expect.anything() }),
303
- '/api',
304
- );
305
- });
306
-
307
- it('POST meta/objects parses JSON body', async () => {
308
- const handler = createRouteHandler({ kernel: mockKernel });
309
- const body = { label: 'My Object' };
310
- const req = makeReq('http://localhost/api/meta/objects', 'POST', body);
311
- const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
312
- expect(res.status).toBe(200);
313
- expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
314
- 'POST',
315
- '/meta/objects',
316
- body,
317
- expect.any(Object),
318
- expect.objectContaining({ request: expect.anything() }),
319
- '/api',
320
- );
321
- });
322
- });
323
-
324
- describe('Data Endpoint', () => {
325
- it('GET data/account delegates to dispatch()', async () => {
326
- const handler = createRouteHandler({ kernel: mockKernel });
327
- const req = makeReq('http://localhost/api/data/account', 'GET');
328
- const res = await handler(req, { params: { objectstack: ['data', 'account'] } });
329
- expect(res.status).toBe(200);
330
- expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
331
- 'GET',
332
- '/data/account',
333
- undefined,
334
- expect.any(Object),
335
- expect.objectContaining({ request: expect.anything() }),
336
- '/api',
337
- );
338
- });
339
-
340
- it('POST data/account parses JSON body', async () => {
341
- const handler = createRouteHandler({ kernel: mockKernel });
342
- const body = { name: 'Acme' };
343
- const req = makeReq('http://localhost/api/data/account', 'POST', body);
344
- const res = await handler(req, { params: { objectstack: ['data', 'account'] } });
345
- expect(res.status).toBe(200);
346
- expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
347
- 'POST',
348
- '/data/account',
349
- body,
350
- expect.any(Object),
351
- expect.objectContaining({ request: expect.anything() }),
352
- '/api',
353
- );
354
- });
355
-
356
- it('PATCH data/account parses JSON body', async () => {
357
- const handler = createRouteHandler({ kernel: mockKernel });
358
- const body = { name: 'Updated' };
359
- const req = makeReq('http://localhost/api/data/account', 'PATCH', body);
360
- const res = await handler(req, { params: { objectstack: ['data', 'account'] } });
361
- expect(res.status).toBe(200);
362
- expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
363
- 'PATCH',
364
- '/data/account',
365
- body,
366
- expect.any(Object),
367
- expect.objectContaining({ request: expect.anything() }),
368
- '/api',
369
- );
370
- });
371
-
372
- it('returns 404 when result is not handled', async () => {
373
- mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false });
374
- const handler = createRouteHandler({ kernel: mockKernel });
375
- const req = makeReq('http://localhost/api/data/missing', 'GET');
376
- const res = await handler(req, { params: { objectstack: ['data', 'missing'] } });
377
- expect(res.status).toBe(404);
378
- expect(res.body.success).toBe(false);
379
- });
380
- });
381
-
382
- describe('Storage Endpoint', () => {
383
- it('GET storage/files calls handleStorage', async () => {
384
- const handler = createRouteHandler({ kernel: mockKernel });
385
- const req = makeReq('http://localhost/api/storage/files', 'GET');
386
- const res = await handler(req, { params: { objectstack: ['storage', 'files'] } });
387
- expect(res.status).toBe(200);
388
- expect(mockDispatcher.handleStorage).toHaveBeenCalledWith(
389
- 'files',
390
- 'GET',
391
- undefined,
392
- expect.objectContaining({ request: expect.anything() }),
393
- );
394
- });
395
-
396
- it('POST storage/upload calls handleStorage with file', async () => {
397
- const handler = createRouteHandler({ kernel: mockKernel });
398
- const req = makeReq('http://localhost/api/storage/upload', 'POST');
399
- const res = await handler(req, { params: { objectstack: ['storage', 'upload'] } });
400
- expect(res.status).toBe(200);
401
- expect(mockDispatcher.handleStorage).toHaveBeenCalledWith(
402
- 'upload',
403
- 'POST',
404
- expect.objectContaining({ name: 'test.txt' }),
405
- expect.objectContaining({ request: expect.anything() }),
406
- );
407
- });
408
- });
409
-
410
- describe('Error Handling', () => {
411
- it('returns 404 for unknown route segments', async () => {
412
- mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false });
413
- const handler = createRouteHandler({ kernel: mockKernel });
414
- const req = makeReq('http://localhost/api/unknown/path', 'GET');
415
- const res = await handler(req, { params: { objectstack: ['unknown', 'path'] } });
416
- expect(res.status).toBe(404);
417
- expect(res.body.success).toBe(false);
418
- expect(res.body.error.message).toBe('Not Found');
419
- });
420
-
421
- it('returns 500 with default message on generic error', async () => {
422
- mockDispatcher.dispatch.mockRejectedValueOnce(new Error());
423
- const handler = createRouteHandler({ kernel: mockKernel });
424
- const req = makeReq('http://localhost/api/data/account', 'GET');
425
- const res = await handler(req, { params: { objectstack: ['data', 'account'] } });
426
- expect(res.status).toBe(500);
427
- expect(res.body.error.message).toBe('Internal Server Error');
428
- });
429
-
430
- it('uses custom statusCode from error', async () => {
431
- mockDispatcher.dispatch.mockRejectedValueOnce(
432
- Object.assign(new Error('Forbidden'), { statusCode: 403 }),
433
- );
434
- const handler = createRouteHandler({ kernel: mockKernel });
435
- const req = makeReq('http://localhost/api/data/account', 'GET');
436
- const res = await handler(req, { params: { objectstack: ['data', 'account'] } });
437
- expect(res.status).toBe(403);
438
- expect(res.body.error.message).toBe('Forbidden');
439
- });
440
- });
441
-
442
- describe('toResponse', () => {
443
- it('handles redirect result', async () => {
444
- mockDispatcher.dispatch.mockResolvedValueOnce({
445
- handled: true,
446
- result: { type: 'redirect', url: 'https://example.com' },
447
- });
448
- const handler = createRouteHandler({ kernel: mockKernel });
449
- const req = makeReq('http://localhost/api/data/redir', 'GET');
450
- const res = await handler(req, { params: { objectstack: ['data', 'redir'] } });
451
- expect((res as any).redirectUrl).toBe('https://example.com');
452
- });
453
-
454
- it('handles stream result', async () => {
455
- const stream = 'mock-stream';
456
- mockDispatcher.dispatch.mockResolvedValueOnce({
457
- handled: true,
458
- result: { type: 'stream', stream, headers: { 'Content-Type': 'text/plain' } },
459
- });
460
- const handler = createRouteHandler({ kernel: mockKernel });
461
- const req = makeReq('http://localhost/api/data/stream', 'GET');
462
- const res = await handler(req, { params: { objectstack: ['data', 'stream'] } });
463
- expect(res.body).toBe('mock-stream');
464
- expect(res.status).toBe(200);
465
- });
466
-
467
- it('returns raw result when handled but no response/redirect/stream', async () => {
468
- const rawResult = { type: 'custom', data: 'test' };
469
- mockDispatcher.dispatch.mockResolvedValueOnce({
470
- handled: true,
471
- result: rawResult,
472
- });
473
- const handler = createRouteHandler({ kernel: mockKernel });
474
- const req = makeReq('http://localhost/api/data/custom', 'GET');
475
- const res = await handler(req, { params: { objectstack: ['data', 'custom'] } });
476
- expect(res).toBe(rawResult);
477
- });
478
- });
479
- });
480
-
481
- describe('createDiscoveryHandler', () => {
482
- it('redirects to default /api path', async () => {
483
- const handler = createDiscoveryHandler({ kernel: mockKernel });
484
- const req = makeReq('http://localhost/.well-known/objectstack', 'GET');
485
- const res = await handler(req);
486
- expect((res as any).redirectUrl).toBe('http://localhost/api');
487
- });
488
-
489
- it('redirects to custom prefix', async () => {
490
- const handler = createDiscoveryHandler({ kernel: mockKernel, prefix: '/v2/api' });
491
- const req = makeReq('http://localhost/.well-known/objectstack', 'GET');
492
- const res = await handler(req);
493
- expect((res as any).redirectUrl).toBe('http://localhost/v2/api');
494
- });
495
- });
496
-
497
- // --- Server Actions Tests ---
498
- import { createServerActions } from './index';
499
-
500
- describe('createServerActions', () => {
501
- beforeEach(() => {
502
- vi.clearAllMocks();
503
- });
504
-
505
- it('query returns data on success', async () => {
506
- const actions = createServerActions({ kernel: mockKernel });
507
- const result = await actions.query('account');
508
- expect(result.success).toBe(true);
509
- expect(result.data).toEqual({ records: [] });
510
- expect(mockDispatcher.handleData).toHaveBeenCalledWith(
511
- '/account', 'GET', {}, {},
512
- expect.anything(),
513
- );
514
- });
515
-
516
- it('query passes params', async () => {
517
- const actions = createServerActions({ kernel: mockKernel });
518
- await actions.query('account', { limit: 10 });
519
- expect(mockDispatcher.handleData).toHaveBeenCalledWith(
520
- '/account', 'GET', {}, { limit: 10 },
521
- expect.anything(),
522
- );
523
- });
524
-
525
- it('getById returns single record', async () => {
526
- const actions = createServerActions({ kernel: mockKernel });
527
- const result = await actions.getById('account', '123');
528
- expect(result.success).toBe(true);
529
- expect(mockDispatcher.handleData).toHaveBeenCalledWith(
530
- '/account/123', 'GET', {}, {},
531
- expect.anything(),
532
- );
533
- });
534
-
535
- it('create sends data', async () => {
536
- const actions = createServerActions({ kernel: mockKernel });
537
- const result = await actions.create('account', { name: 'Acme' });
538
- expect(result.success).toBe(true);
539
- expect(mockDispatcher.handleData).toHaveBeenCalledWith(
540
- '/account', 'POST', { name: 'Acme' }, {},
541
- expect.anything(),
542
- );
543
- });
544
-
545
- it('update sends data with id', async () => {
546
- const actions = createServerActions({ kernel: mockKernel });
547
- const result = await actions.update('account', '123', { name: 'Updated' });
548
- expect(result.success).toBe(true);
549
- expect(mockDispatcher.handleData).toHaveBeenCalledWith(
550
- '/account/123', 'PATCH', { name: 'Updated' }, {},
551
- expect.anything(),
552
- );
553
- });
554
-
555
- it('remove deletes record', async () => {
556
- const actions = createServerActions({ kernel: mockKernel });
557
- const result = await actions.remove('account', '123');
558
- expect(result.success).toBe(true);
559
- expect(mockDispatcher.handleData).toHaveBeenCalledWith(
560
- '/account/123', 'DELETE', {}, {},
561
- expect.anything(),
562
- );
563
- });
564
-
565
- it('getMetadata returns metadata', async () => {
566
- const actions = createServerActions({ kernel: mockKernel });
567
- const result = await actions.getMetadata('/objects');
568
- expect(result.success).toBe(true);
569
- expect(mockDispatcher.handleMetadata).toHaveBeenCalledWith(
570
- '/objects', expect.anything(), 'GET',
571
- );
572
- });
573
-
574
- it('returns error on exception', async () => {
575
- mockDispatcher.handleData.mockRejectedValueOnce(new Error('Server down'));
576
- const actions = createServerActions({ kernel: mockKernel });
577
- const result = await actions.query('account');
578
- expect(result.success).toBe(false);
579
- expect(result.error?.message).toBe('Server down');
580
- });
581
-
582
- it('returns error when not handled', async () => {
583
- mockDispatcher.handleData.mockResolvedValueOnce({ handled: false });
584
- const actions = createServerActions({ kernel: mockKernel });
585
- const result = await actions.query('missing');
586
- expect(result.success).toBe(false);
587
- expect(result.error?.code).toBe(404);
588
- });
589
- });
package/tsconfig.json DELETED
@@ -1,26 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src",
6
- "module": "NodeNext",
7
- "moduleResolution": "NodeNext",
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "lib": [
11
- "ES2020",
12
- "DOM",
13
- "DOM.Iterable"
14
- ],
15
- "types": [
16
- "node"
17
- ]
18
- },
19
- "include": [
20
- "src/**/*"
21
- ],
22
- "exclude": [
23
- "node_modules",
24
- "dist"
25
- ]
26
- }
package/vitest.config.ts DELETED
@@ -1,16 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { defineConfig } from 'vitest/config';
4
- import path from 'node:path';
5
-
6
- export default defineConfig({
7
- test: {
8
- globals: true,
9
- environment: 'node',
10
- },
11
- resolve: {
12
- alias: {
13
- '@objectstack/runtime': path.resolve(__dirname, 'src/__mocks__/runtime.ts'),
14
- },
15
- },
16
- });