@objectstack/nextjs 3.2.8 → 3.3.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/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +12 -0
- package/package.json +5 -5
- package/src/metadata-api.test.ts +63 -53
- package/src/nextjs.test.ts +29 -26
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/nextjs@3.
|
|
2
|
+
> @objectstack/nextjs@3.3.0 build /home/runner/work/spec/spec/packages/adapters/nextjs
|
|
3
3
|
> tsup --config ../../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
13
|
[32mESM[39m [1mdist/index.mjs [22m[32m7.52 KB[39m
|
|
14
14
|
[32mESM[39m [1mdist/index.mjs.map [22m[32m16.55 KB[39m
|
|
15
|
-
[32mESM[39m ⚡️ Build success in
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 79ms
|
|
16
16
|
[32mCJS[39m [1mdist/index.js [22m[32m8.76 KB[39m
|
|
17
17
|
[32mCJS[39m [1mdist/index.js.map [22m[32m16.60 KB[39m
|
|
18
|
-
[32mCJS[39m ⚡️ Build success in
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 79ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 13754ms
|
|
21
21
|
[32mDTS[39m [1mdist/index.d.mts [22m[32m2.63 KB[39m
|
|
22
22
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m2.63 KB[39m
|
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/nextjs",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"peerDependencies": {
|
|
8
|
-
"next": "^16.1
|
|
8
|
+
"next": "^16.2.1",
|
|
9
9
|
"react": "^19.2.4",
|
|
10
10
|
"react-dom": "^19.2.4",
|
|
11
|
-
"@objectstack/runtime": "^3.
|
|
11
|
+
"@objectstack/runtime": "^3.3.0"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"next": "^16.1
|
|
14
|
+
"next": "^16.2.1",
|
|
15
15
|
"react": "^19.2.4",
|
|
16
16
|
"react-dom": "^19.2.4",
|
|
17
17
|
"typescript": "^5.0.0",
|
|
18
18
|
"vitest": "^4.1.0",
|
|
19
|
-
"@objectstack/runtime": "3.
|
|
19
|
+
"@objectstack/runtime": "3.3.0"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "tsup --config ../../../tsup.config.ts",
|
package/src/metadata-api.test.ts
CHANGED
|
@@ -19,6 +19,7 @@ const mockDispatcher = {
|
|
|
19
19
|
handleMetadata: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }),
|
|
20
20
|
handleData: vi.fn().mockResolvedValue({ handled: true, response: { body: { records: [] }, status: 200 } }),
|
|
21
21
|
handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }),
|
|
22
|
+
dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }),
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
vi.mock('@objectstack/runtime', () => {
|
|
@@ -106,8 +107,8 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
106
107
|
|
|
107
108
|
describe('CRUD Operations', () => {
|
|
108
109
|
describe('GET meta/objects — List all objects', () => {
|
|
109
|
-
it('dispatches to
|
|
110
|
-
mockDispatcher.
|
|
110
|
+
it('dispatches to dispatch with correct path', async () => {
|
|
111
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
111
112
|
handled: true,
|
|
112
113
|
response: {
|
|
113
114
|
body: {
|
|
@@ -125,18 +126,19 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
125
126
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
|
|
126
127
|
expect(res.status).toBe(200);
|
|
127
128
|
expect(res.body.data).toHaveLength(2);
|
|
128
|
-
expect(mockDispatcher.
|
|
129
|
-
'objects',
|
|
130
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
129
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
131
130
|
'GET',
|
|
131
|
+
'/meta/objects',
|
|
132
132
|
undefined,
|
|
133
|
+
{},
|
|
134
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
133
135
|
);
|
|
134
136
|
});
|
|
135
137
|
});
|
|
136
138
|
|
|
137
139
|
describe('GET meta/objects/account — Get single object', () => {
|
|
138
|
-
it('dispatches to
|
|
139
|
-
mockDispatcher.
|
|
140
|
+
it('dispatches to dispatch with item-level path', async () => {
|
|
141
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
140
142
|
handled: true,
|
|
141
143
|
response: {
|
|
142
144
|
body: {
|
|
@@ -151,11 +153,12 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
151
153
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account'] } });
|
|
152
154
|
expect(res.status).toBe(200);
|
|
153
155
|
expect(res.body.data.name).toBe('account');
|
|
154
|
-
expect(mockDispatcher.
|
|
155
|
-
'objects/account',
|
|
156
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
156
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
157
157
|
'GET',
|
|
158
|
+
'/meta/objects/account',
|
|
158
159
|
undefined,
|
|
160
|
+
{},
|
|
161
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
159
162
|
);
|
|
160
163
|
});
|
|
161
164
|
});
|
|
@@ -168,7 +171,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
168
171
|
data: { label: 'Project Task', fields: {} },
|
|
169
172
|
};
|
|
170
173
|
|
|
171
|
-
mockDispatcher.
|
|
174
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
172
175
|
handled: true,
|
|
173
176
|
response: { body: { success: true }, status: 201 },
|
|
174
177
|
});
|
|
@@ -176,11 +179,12 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
176
179
|
const req = makeReq('http://localhost/api/meta/objects', 'POST', body);
|
|
177
180
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
|
|
178
181
|
expect(res.status).toBe(201);
|
|
179
|
-
expect(mockDispatcher.
|
|
180
|
-
'objects',
|
|
181
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
182
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
182
183
|
'POST',
|
|
184
|
+
'/meta/objects',
|
|
183
185
|
body,
|
|
186
|
+
{},
|
|
187
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
184
188
|
);
|
|
185
189
|
});
|
|
186
190
|
});
|
|
@@ -189,7 +193,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
189
193
|
it('dispatches PUT with JSON body', async () => {
|
|
190
194
|
const body = { label: 'Updated Account' };
|
|
191
195
|
|
|
192
|
-
mockDispatcher.
|
|
196
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
193
197
|
handled: true,
|
|
194
198
|
response: { body: { success: true }, status: 200 },
|
|
195
199
|
});
|
|
@@ -197,18 +201,19 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
197
201
|
const req = makeReq('http://localhost/api/meta/objects/account', 'PUT', body);
|
|
198
202
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account'] } });
|
|
199
203
|
expect(res.status).toBe(200);
|
|
200
|
-
expect(mockDispatcher.
|
|
201
|
-
'objects/account',
|
|
202
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
204
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
203
205
|
'PUT',
|
|
206
|
+
'/meta/objects/account',
|
|
204
207
|
body,
|
|
208
|
+
{},
|
|
209
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
205
210
|
);
|
|
206
211
|
});
|
|
207
212
|
});
|
|
208
213
|
|
|
209
214
|
describe('DELETE meta/objects/old_entity — Delete metadata', () => {
|
|
210
|
-
it('dispatches DELETE to
|
|
211
|
-
mockDispatcher.
|
|
215
|
+
it('dispatches DELETE to dispatch', async () => {
|
|
216
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
212
217
|
handled: true,
|
|
213
218
|
response: {
|
|
214
219
|
body: { success: true, data: { type: 'object', name: 'old_entity' } },
|
|
@@ -227,33 +232,36 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
227
232
|
it('dispatches for views', async () => {
|
|
228
233
|
const req = makeReq('http://localhost/api/meta/views');
|
|
229
234
|
await handler(req, { params: { objectstack: ['meta', 'views'] } });
|
|
230
|
-
expect(mockDispatcher.
|
|
231
|
-
'views',
|
|
232
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
235
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
233
236
|
'GET',
|
|
237
|
+
'/meta/views',
|
|
234
238
|
undefined,
|
|
239
|
+
{},
|
|
240
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
235
241
|
);
|
|
236
242
|
});
|
|
237
243
|
|
|
238
244
|
it('dispatches for flows', async () => {
|
|
239
245
|
const req = makeReq('http://localhost/api/meta/flows');
|
|
240
246
|
await handler(req, { params: { objectstack: ['meta', 'flows'] } });
|
|
241
|
-
expect(mockDispatcher.
|
|
242
|
-
'flows',
|
|
243
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
247
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
244
248
|
'GET',
|
|
249
|
+
'/meta/flows',
|
|
245
250
|
undefined,
|
|
251
|
+
{},
|
|
252
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
246
253
|
);
|
|
247
254
|
});
|
|
248
255
|
|
|
249
256
|
it('dispatches for agents', async () => {
|
|
250
257
|
const req = makeReq('http://localhost/api/meta/agents');
|
|
251
258
|
await handler(req, { params: { objectstack: ['meta', 'agents'] } });
|
|
252
|
-
expect(mockDispatcher.
|
|
253
|
-
'agents',
|
|
254
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
259
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
255
260
|
'GET',
|
|
261
|
+
'/meta/agents',
|
|
256
262
|
undefined,
|
|
263
|
+
{},
|
|
264
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
257
265
|
);
|
|
258
266
|
});
|
|
259
267
|
});
|
|
@@ -273,7 +281,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
273
281
|
pageSize: 25,
|
|
274
282
|
};
|
|
275
283
|
|
|
276
|
-
mockDispatcher.
|
|
284
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
277
285
|
handled: true,
|
|
278
286
|
response: {
|
|
279
287
|
body: {
|
|
@@ -305,7 +313,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
305
313
|
describe('Bulk Operations', () => {
|
|
306
314
|
describe('POST meta/bulk/register — Bulk register', () => {
|
|
307
315
|
it('dispatches bulk register', async () => {
|
|
308
|
-
mockDispatcher.
|
|
316
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
309
317
|
handled: true,
|
|
310
318
|
response: {
|
|
311
319
|
body: { success: true, data: { total: 2, succeeded: 2, failed: 0 } },
|
|
@@ -328,7 +336,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
328
336
|
|
|
329
337
|
describe('POST meta/bulk/unregister — Bulk unregister', () => {
|
|
330
338
|
it('dispatches bulk unregister', async () => {
|
|
331
|
-
mockDispatcher.
|
|
339
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
332
340
|
handled: true,
|
|
333
341
|
response: {
|
|
334
342
|
body: { success: true, data: { total: 2, succeeded: 2, failed: 0 } },
|
|
@@ -348,7 +356,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
348
356
|
|
|
349
357
|
describe('Bulk operation with partial failures', () => {
|
|
350
358
|
it('returns error details', async () => {
|
|
351
|
-
mockDispatcher.
|
|
359
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
352
360
|
handled: true,
|
|
353
361
|
response: {
|
|
354
362
|
body: {
|
|
@@ -387,7 +395,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
387
395
|
describe('Overlay / Customization', () => {
|
|
388
396
|
describe('GET meta/objects/account/overlay — Get overlay', () => {
|
|
389
397
|
it('dispatches overlay retrieval', async () => {
|
|
390
|
-
mockDispatcher.
|
|
398
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
391
399
|
handled: true,
|
|
392
400
|
response: {
|
|
393
401
|
body: {
|
|
@@ -413,7 +421,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
413
421
|
|
|
414
422
|
describe('PUT meta/objects/account/overlay — Save overlay', () => {
|
|
415
423
|
it('dispatches overlay save', async () => {
|
|
416
|
-
mockDispatcher.
|
|
424
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
417
425
|
handled: true,
|
|
418
426
|
response: { body: { success: true }, status: 200 },
|
|
419
427
|
});
|
|
@@ -431,7 +439,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
431
439
|
|
|
432
440
|
describe('GET meta/objects/account/effective — Get effective metadata', () => {
|
|
433
441
|
it('dispatches effective metadata retrieval', async () => {
|
|
434
|
-
mockDispatcher.
|
|
442
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
435
443
|
handled: true,
|
|
436
444
|
response: {
|
|
437
445
|
body: {
|
|
@@ -457,7 +465,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
457
465
|
describe('Import / Export', () => {
|
|
458
466
|
describe('POST meta/export — Export metadata', () => {
|
|
459
467
|
it('dispatches export request', async () => {
|
|
460
|
-
mockDispatcher.
|
|
468
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
461
469
|
handled: true,
|
|
462
470
|
response: {
|
|
463
471
|
body: { success: true, data: { version: '1.0', objects: {} } },
|
|
@@ -474,7 +482,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
474
482
|
|
|
475
483
|
describe('POST meta/import — Import metadata', () => {
|
|
476
484
|
it('dispatches import request', async () => {
|
|
477
|
-
mockDispatcher.
|
|
485
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
478
486
|
handled: true,
|
|
479
487
|
response: {
|
|
480
488
|
body: { success: true, data: { total: 3, imported: 3, skipped: 0, failed: 0 } },
|
|
@@ -500,7 +508,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
500
508
|
describe('Validation', () => {
|
|
501
509
|
describe('POST meta/validate — Validate metadata', () => {
|
|
502
510
|
it('dispatches validation', async () => {
|
|
503
|
-
mockDispatcher.
|
|
511
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
504
512
|
handled: true,
|
|
505
513
|
response: {
|
|
506
514
|
body: { success: true, data: { valid: true } },
|
|
@@ -515,7 +523,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
515
523
|
});
|
|
516
524
|
|
|
517
525
|
it('returns errors for invalid metadata', async () => {
|
|
518
|
-
mockDispatcher.
|
|
526
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
519
527
|
handled: true,
|
|
520
528
|
response: {
|
|
521
529
|
body: {
|
|
@@ -544,7 +552,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
544
552
|
describe('Type Registry', () => {
|
|
545
553
|
describe('GET meta/types — List types', () => {
|
|
546
554
|
it('returns all registered types', async () => {
|
|
547
|
-
mockDispatcher.
|
|
555
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
548
556
|
handled: true,
|
|
549
557
|
response: {
|
|
550
558
|
body: { success: true, data: ['object', 'view', 'flow', 'agent'] },
|
|
@@ -561,7 +569,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
561
569
|
|
|
562
570
|
describe('GET meta/types/object — Get type info', () => {
|
|
563
571
|
it('returns type metadata', async () => {
|
|
564
|
-
mockDispatcher.
|
|
572
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
565
573
|
handled: true,
|
|
566
574
|
response: {
|
|
567
575
|
body: {
|
|
@@ -593,7 +601,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
593
601
|
describe('Dependency Tracking', () => {
|
|
594
602
|
describe('GET meta/objects/account/dependencies — Get dependencies', () => {
|
|
595
603
|
it('returns dependencies', async () => {
|
|
596
|
-
mockDispatcher.
|
|
604
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
597
605
|
handled: true,
|
|
598
606
|
response: {
|
|
599
607
|
body: {
|
|
@@ -619,7 +627,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
619
627
|
|
|
620
628
|
describe('GET meta/objects/account/dependents — Get dependents', () => {
|
|
621
629
|
it('returns dependents', async () => {
|
|
622
|
-
mockDispatcher.
|
|
630
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
623
631
|
handled: true,
|
|
624
632
|
response: {
|
|
625
633
|
body: {
|
|
@@ -647,7 +655,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
647
655
|
|
|
648
656
|
describe('Error Handling', () => {
|
|
649
657
|
it('returns 404 when metadata not found', async () => {
|
|
650
|
-
mockDispatcher.
|
|
658
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false });
|
|
651
659
|
|
|
652
660
|
const req = makeReq('http://localhost/api/meta/objects/nonexistent');
|
|
653
661
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'nonexistent'] } });
|
|
@@ -655,7 +663,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
655
663
|
});
|
|
656
664
|
|
|
657
665
|
it('returns 500 on dispatcher exception', async () => {
|
|
658
|
-
mockDispatcher.
|
|
666
|
+
mockDispatcher.dispatch.mockRejectedValueOnce(new Error('Internal error'));
|
|
659
667
|
|
|
660
668
|
const req = makeReq('http://localhost/api/meta/objects');
|
|
661
669
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
|
|
@@ -664,7 +672,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
664
672
|
});
|
|
665
673
|
|
|
666
674
|
it('returns custom status code from error', async () => {
|
|
667
|
-
mockDispatcher.
|
|
675
|
+
mockDispatcher.dispatch.mockRejectedValueOnce(
|
|
668
676
|
Object.assign(new Error('Forbidden'), { statusCode: 403 }),
|
|
669
677
|
);
|
|
670
678
|
|
|
@@ -682,11 +690,12 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
682
690
|
it('correctly joins nested segments', async () => {
|
|
683
691
|
const req = makeReq('http://localhost/api/meta/objects/account/fields/name');
|
|
684
692
|
await handler(req, { params: { objectstack: ['meta', 'objects', 'account', 'fields', 'name'] } });
|
|
685
|
-
expect(mockDispatcher.
|
|
686
|
-
'objects/account/fields/name',
|
|
687
|
-
expect.any(Object),
|
|
693
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
688
694
|
'GET',
|
|
695
|
+
'/meta/objects/account/fields/name',
|
|
689
696
|
undefined,
|
|
697
|
+
{},
|
|
698
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
690
699
|
);
|
|
691
700
|
});
|
|
692
701
|
|
|
@@ -694,11 +703,12 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
694
703
|
const req = makeReq('http://localhost/api/meta');
|
|
695
704
|
// With just ['meta'], subPath becomes empty after slice(1)
|
|
696
705
|
await handler(req, { params: { objectstack: ['meta'] } });
|
|
697
|
-
expect(mockDispatcher.
|
|
698
|
-
'',
|
|
699
|
-
expect.any(Object),
|
|
706
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
700
707
|
'GET',
|
|
708
|
+
'/meta',
|
|
701
709
|
undefined,
|
|
710
|
+
{},
|
|
711
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
702
712
|
);
|
|
703
713
|
});
|
|
704
714
|
});
|
package/src/nextjs.test.ts
CHANGED
|
@@ -10,6 +10,7 @@ const mockDispatcher = {
|
|
|
10
10
|
handleMetadata: vi.fn().mockResolvedValue({ handled: true, response: { body: { objects: [] }, status: 200 } }),
|
|
11
11
|
handleData: vi.fn().mockResolvedValue({ handled: true, response: { body: { records: [] }, status: 200 } }),
|
|
12
12
|
handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }),
|
|
13
|
+
dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }),
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
vi.mock('@objectstack/runtime', () => {
|
|
@@ -272,17 +273,17 @@ describe('createRouteHandler', () => {
|
|
|
272
273
|
});
|
|
273
274
|
|
|
274
275
|
describe('Metadata Endpoint', () => {
|
|
275
|
-
it('GET meta/objects
|
|
276
|
+
it('GET meta/objects delegates to dispatch()', async () => {
|
|
276
277
|
const handler = createRouteHandler({ kernel: mockKernel });
|
|
277
278
|
const req = makeReq('http://localhost/api/meta/objects', 'GET');
|
|
278
279
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
|
|
279
280
|
expect(res.status).toBe(200);
|
|
280
|
-
expect(
|
|
281
|
-
expect(mockDispatcher.handleMetadata).toHaveBeenCalledWith(
|
|
282
|
-
'objects',
|
|
283
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
281
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
284
282
|
'GET',
|
|
283
|
+
'/meta/objects',
|
|
285
284
|
undefined,
|
|
285
|
+
expect.any(Object),
|
|
286
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
286
287
|
);
|
|
287
288
|
});
|
|
288
289
|
|
|
@@ -292,11 +293,12 @@ describe('createRouteHandler', () => {
|
|
|
292
293
|
const req = makeReq('http://localhost/api/meta/objects', 'PUT', body);
|
|
293
294
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
|
|
294
295
|
expect(res.status).toBe(200);
|
|
295
|
-
expect(mockDispatcher.
|
|
296
|
-
'objects',
|
|
297
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
296
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
298
297
|
'PUT',
|
|
298
|
+
'/meta/objects',
|
|
299
299
|
body,
|
|
300
|
+
expect.any(Object),
|
|
301
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
300
302
|
);
|
|
301
303
|
});
|
|
302
304
|
|
|
@@ -306,26 +308,26 @@ describe('createRouteHandler', () => {
|
|
|
306
308
|
const req = makeReq('http://localhost/api/meta/objects', 'POST', body);
|
|
307
309
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
|
|
308
310
|
expect(res.status).toBe(200);
|
|
309
|
-
expect(mockDispatcher.
|
|
310
|
-
'objects',
|
|
311
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
311
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
312
312
|
'POST',
|
|
313
|
+
'/meta/objects',
|
|
313
314
|
body,
|
|
315
|
+
expect.any(Object),
|
|
316
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
314
317
|
);
|
|
315
318
|
});
|
|
316
319
|
});
|
|
317
320
|
|
|
318
321
|
describe('Data Endpoint', () => {
|
|
319
|
-
it('GET data/account
|
|
322
|
+
it('GET data/account delegates to dispatch()', async () => {
|
|
320
323
|
const handler = createRouteHandler({ kernel: mockKernel });
|
|
321
324
|
const req = makeReq('http://localhost/api/data/account', 'GET');
|
|
322
325
|
const res = await handler(req, { params: { objectstack: ['data', 'account'] } });
|
|
323
326
|
expect(res.status).toBe(200);
|
|
324
|
-
expect(
|
|
325
|
-
expect(mockDispatcher.handleData).toHaveBeenCalledWith(
|
|
326
|
-
'account',
|
|
327
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
327
328
|
'GET',
|
|
328
|
-
|
|
329
|
+
'/data/account',
|
|
330
|
+
undefined,
|
|
329
331
|
expect.any(Object),
|
|
330
332
|
expect.objectContaining({ request: expect.anything() }),
|
|
331
333
|
);
|
|
@@ -337,9 +339,9 @@ describe('createRouteHandler', () => {
|
|
|
337
339
|
const req = makeReq('http://localhost/api/data/account', 'POST', body);
|
|
338
340
|
const res = await handler(req, { params: { objectstack: ['data', 'account'] } });
|
|
339
341
|
expect(res.status).toBe(200);
|
|
340
|
-
expect(mockDispatcher.
|
|
341
|
-
'account',
|
|
342
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
342
343
|
'POST',
|
|
344
|
+
'/data/account',
|
|
343
345
|
body,
|
|
344
346
|
expect.any(Object),
|
|
345
347
|
expect.objectContaining({ request: expect.anything() }),
|
|
@@ -352,9 +354,9 @@ describe('createRouteHandler', () => {
|
|
|
352
354
|
const req = makeReq('http://localhost/api/data/account', 'PATCH', body);
|
|
353
355
|
const res = await handler(req, { params: { objectstack: ['data', 'account'] } });
|
|
354
356
|
expect(res.status).toBe(200);
|
|
355
|
-
expect(mockDispatcher.
|
|
356
|
-
'account',
|
|
357
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
357
358
|
'PATCH',
|
|
359
|
+
'/data/account',
|
|
358
360
|
body,
|
|
359
361
|
expect.any(Object),
|
|
360
362
|
expect.objectContaining({ request: expect.anything() }),
|
|
@@ -362,7 +364,7 @@ describe('createRouteHandler', () => {
|
|
|
362
364
|
});
|
|
363
365
|
|
|
364
366
|
it('returns 404 when result is not handled', async () => {
|
|
365
|
-
mockDispatcher.
|
|
367
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false });
|
|
366
368
|
const handler = createRouteHandler({ kernel: mockKernel });
|
|
367
369
|
const req = makeReq('http://localhost/api/data/missing', 'GET');
|
|
368
370
|
const res = await handler(req, { params: { objectstack: ['data', 'missing'] } });
|
|
@@ -401,6 +403,7 @@ describe('createRouteHandler', () => {
|
|
|
401
403
|
|
|
402
404
|
describe('Error Handling', () => {
|
|
403
405
|
it('returns 404 for unknown route segments', async () => {
|
|
406
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false });
|
|
404
407
|
const handler = createRouteHandler({ kernel: mockKernel });
|
|
405
408
|
const req = makeReq('http://localhost/api/unknown/path', 'GET');
|
|
406
409
|
const res = await handler(req, { params: { objectstack: ['unknown', 'path'] } });
|
|
@@ -410,7 +413,7 @@ describe('createRouteHandler', () => {
|
|
|
410
413
|
});
|
|
411
414
|
|
|
412
415
|
it('returns 500 with default message on generic error', async () => {
|
|
413
|
-
mockDispatcher.
|
|
416
|
+
mockDispatcher.dispatch.mockRejectedValueOnce(new Error());
|
|
414
417
|
const handler = createRouteHandler({ kernel: mockKernel });
|
|
415
418
|
const req = makeReq('http://localhost/api/data/account', 'GET');
|
|
416
419
|
const res = await handler(req, { params: { objectstack: ['data', 'account'] } });
|
|
@@ -419,7 +422,7 @@ describe('createRouteHandler', () => {
|
|
|
419
422
|
});
|
|
420
423
|
|
|
421
424
|
it('uses custom statusCode from error', async () => {
|
|
422
|
-
mockDispatcher.
|
|
425
|
+
mockDispatcher.dispatch.mockRejectedValueOnce(
|
|
423
426
|
Object.assign(new Error('Forbidden'), { statusCode: 403 }),
|
|
424
427
|
);
|
|
425
428
|
const handler = createRouteHandler({ kernel: mockKernel });
|
|
@@ -432,7 +435,7 @@ describe('createRouteHandler', () => {
|
|
|
432
435
|
|
|
433
436
|
describe('toResponse', () => {
|
|
434
437
|
it('handles redirect result', async () => {
|
|
435
|
-
mockDispatcher.
|
|
438
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
436
439
|
handled: true,
|
|
437
440
|
result: { type: 'redirect', url: 'https://example.com' },
|
|
438
441
|
});
|
|
@@ -444,7 +447,7 @@ describe('createRouteHandler', () => {
|
|
|
444
447
|
|
|
445
448
|
it('handles stream result', async () => {
|
|
446
449
|
const stream = 'mock-stream';
|
|
447
|
-
mockDispatcher.
|
|
450
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
448
451
|
handled: true,
|
|
449
452
|
result: { type: 'stream', stream, headers: { 'Content-Type': 'text/plain' } },
|
|
450
453
|
});
|
|
@@ -457,7 +460,7 @@ describe('createRouteHandler', () => {
|
|
|
457
460
|
|
|
458
461
|
it('returns raw result when handled but no response/redirect/stream', async () => {
|
|
459
462
|
const rawResult = { type: 'custom', data: 'test' };
|
|
460
|
-
mockDispatcher.
|
|
463
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
461
464
|
handled: true,
|
|
462
465
|
result: rawResult,
|
|
463
466
|
});
|