@objectstack/nextjs 3.2.8 → 3.2.9
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 +6 -0
- package/package.json +3 -3
- package/src/metadata-api.test.ts +36 -35
- package/src/nextjs.test.ts +29 -26
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/nextjs@3.2.
|
|
2
|
+
> @objectstack/nextjs@3.2.9 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 60ms
|
|
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 71ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 13961ms
|
|
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/nextjs",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.9",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"next": "^16.1.7",
|
|
9
9
|
"react": "^19.2.4",
|
|
10
10
|
"react-dom": "^19.2.4",
|
|
11
|
-
"@objectstack/runtime": "^3.2.
|
|
11
|
+
"@objectstack/runtime": "^3.2.9"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"next": "^16.1.7",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"react-dom": "^19.2.4",
|
|
17
17
|
"typescript": "^5.0.0",
|
|
18
18
|
"vitest": "^4.1.0",
|
|
19
|
-
"@objectstack/runtime": "3.2.
|
|
19
|
+
"@objectstack/runtime": "3.2.9"
|
|
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,7 +126,7 @@ 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
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
129
130
|
'objects',
|
|
130
131
|
expect.objectContaining({ request: expect.anything() }),
|
|
131
132
|
'GET',
|
|
@@ -135,8 +136,8 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
135
136
|
});
|
|
136
137
|
|
|
137
138
|
describe('GET meta/objects/account — Get single object', () => {
|
|
138
|
-
it('dispatches to
|
|
139
|
-
mockDispatcher.
|
|
139
|
+
it('dispatches to dispatch with item-level path', async () => {
|
|
140
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
140
141
|
handled: true,
|
|
141
142
|
response: {
|
|
142
143
|
body: {
|
|
@@ -151,7 +152,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
151
152
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account'] } });
|
|
152
153
|
expect(res.status).toBe(200);
|
|
153
154
|
expect(res.body.data.name).toBe('account');
|
|
154
|
-
expect(mockDispatcher.
|
|
155
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
155
156
|
'objects/account',
|
|
156
157
|
expect.objectContaining({ request: expect.anything() }),
|
|
157
158
|
'GET',
|
|
@@ -168,7 +169,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
168
169
|
data: { label: 'Project Task', fields: {} },
|
|
169
170
|
};
|
|
170
171
|
|
|
171
|
-
mockDispatcher.
|
|
172
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
172
173
|
handled: true,
|
|
173
174
|
response: { body: { success: true }, status: 201 },
|
|
174
175
|
});
|
|
@@ -176,7 +177,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
176
177
|
const req = makeReq('http://localhost/api/meta/objects', 'POST', body);
|
|
177
178
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
|
|
178
179
|
expect(res.status).toBe(201);
|
|
179
|
-
expect(mockDispatcher.
|
|
180
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
180
181
|
'objects',
|
|
181
182
|
expect.objectContaining({ request: expect.anything() }),
|
|
182
183
|
'POST',
|
|
@@ -189,7 +190,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
189
190
|
it('dispatches PUT with JSON body', async () => {
|
|
190
191
|
const body = { label: 'Updated Account' };
|
|
191
192
|
|
|
192
|
-
mockDispatcher.
|
|
193
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
193
194
|
handled: true,
|
|
194
195
|
response: { body: { success: true }, status: 200 },
|
|
195
196
|
});
|
|
@@ -197,7 +198,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
197
198
|
const req = makeReq('http://localhost/api/meta/objects/account', 'PUT', body);
|
|
198
199
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account'] } });
|
|
199
200
|
expect(res.status).toBe(200);
|
|
200
|
-
expect(mockDispatcher.
|
|
201
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
201
202
|
'objects/account',
|
|
202
203
|
expect.objectContaining({ request: expect.anything() }),
|
|
203
204
|
'PUT',
|
|
@@ -207,8 +208,8 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
207
208
|
});
|
|
208
209
|
|
|
209
210
|
describe('DELETE meta/objects/old_entity — Delete metadata', () => {
|
|
210
|
-
it('dispatches DELETE to
|
|
211
|
-
mockDispatcher.
|
|
211
|
+
it('dispatches DELETE to dispatch', async () => {
|
|
212
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
212
213
|
handled: true,
|
|
213
214
|
response: {
|
|
214
215
|
body: { success: true, data: { type: 'object', name: 'old_entity' } },
|
|
@@ -227,7 +228,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
227
228
|
it('dispatches for views', async () => {
|
|
228
229
|
const req = makeReq('http://localhost/api/meta/views');
|
|
229
230
|
await handler(req, { params: { objectstack: ['meta', 'views'] } });
|
|
230
|
-
expect(mockDispatcher.
|
|
231
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
231
232
|
'views',
|
|
232
233
|
expect.objectContaining({ request: expect.anything() }),
|
|
233
234
|
'GET',
|
|
@@ -238,7 +239,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
238
239
|
it('dispatches for flows', async () => {
|
|
239
240
|
const req = makeReq('http://localhost/api/meta/flows');
|
|
240
241
|
await handler(req, { params: { objectstack: ['meta', 'flows'] } });
|
|
241
|
-
expect(mockDispatcher.
|
|
242
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
242
243
|
'flows',
|
|
243
244
|
expect.objectContaining({ request: expect.anything() }),
|
|
244
245
|
'GET',
|
|
@@ -249,7 +250,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
249
250
|
it('dispatches for agents', async () => {
|
|
250
251
|
const req = makeReq('http://localhost/api/meta/agents');
|
|
251
252
|
await handler(req, { params: { objectstack: ['meta', 'agents'] } });
|
|
252
|
-
expect(mockDispatcher.
|
|
253
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
253
254
|
'agents',
|
|
254
255
|
expect.objectContaining({ request: expect.anything() }),
|
|
255
256
|
'GET',
|
|
@@ -273,7 +274,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
273
274
|
pageSize: 25,
|
|
274
275
|
};
|
|
275
276
|
|
|
276
|
-
mockDispatcher.
|
|
277
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
277
278
|
handled: true,
|
|
278
279
|
response: {
|
|
279
280
|
body: {
|
|
@@ -305,7 +306,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
305
306
|
describe('Bulk Operations', () => {
|
|
306
307
|
describe('POST meta/bulk/register — Bulk register', () => {
|
|
307
308
|
it('dispatches bulk register', async () => {
|
|
308
|
-
mockDispatcher.
|
|
309
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
309
310
|
handled: true,
|
|
310
311
|
response: {
|
|
311
312
|
body: { success: true, data: { total: 2, succeeded: 2, failed: 0 } },
|
|
@@ -328,7 +329,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
328
329
|
|
|
329
330
|
describe('POST meta/bulk/unregister — Bulk unregister', () => {
|
|
330
331
|
it('dispatches bulk unregister', async () => {
|
|
331
|
-
mockDispatcher.
|
|
332
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
332
333
|
handled: true,
|
|
333
334
|
response: {
|
|
334
335
|
body: { success: true, data: { total: 2, succeeded: 2, failed: 0 } },
|
|
@@ -348,7 +349,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
348
349
|
|
|
349
350
|
describe('Bulk operation with partial failures', () => {
|
|
350
351
|
it('returns error details', async () => {
|
|
351
|
-
mockDispatcher.
|
|
352
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
352
353
|
handled: true,
|
|
353
354
|
response: {
|
|
354
355
|
body: {
|
|
@@ -387,7 +388,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
387
388
|
describe('Overlay / Customization', () => {
|
|
388
389
|
describe('GET meta/objects/account/overlay — Get overlay', () => {
|
|
389
390
|
it('dispatches overlay retrieval', async () => {
|
|
390
|
-
mockDispatcher.
|
|
391
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
391
392
|
handled: true,
|
|
392
393
|
response: {
|
|
393
394
|
body: {
|
|
@@ -413,7 +414,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
413
414
|
|
|
414
415
|
describe('PUT meta/objects/account/overlay — Save overlay', () => {
|
|
415
416
|
it('dispatches overlay save', async () => {
|
|
416
|
-
mockDispatcher.
|
|
417
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
417
418
|
handled: true,
|
|
418
419
|
response: { body: { success: true }, status: 200 },
|
|
419
420
|
});
|
|
@@ -431,7 +432,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
431
432
|
|
|
432
433
|
describe('GET meta/objects/account/effective — Get effective metadata', () => {
|
|
433
434
|
it('dispatches effective metadata retrieval', async () => {
|
|
434
|
-
mockDispatcher.
|
|
435
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
435
436
|
handled: true,
|
|
436
437
|
response: {
|
|
437
438
|
body: {
|
|
@@ -457,7 +458,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
457
458
|
describe('Import / Export', () => {
|
|
458
459
|
describe('POST meta/export — Export metadata', () => {
|
|
459
460
|
it('dispatches export request', async () => {
|
|
460
|
-
mockDispatcher.
|
|
461
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
461
462
|
handled: true,
|
|
462
463
|
response: {
|
|
463
464
|
body: { success: true, data: { version: '1.0', objects: {} } },
|
|
@@ -474,7 +475,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
474
475
|
|
|
475
476
|
describe('POST meta/import — Import metadata', () => {
|
|
476
477
|
it('dispatches import request', async () => {
|
|
477
|
-
mockDispatcher.
|
|
478
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
478
479
|
handled: true,
|
|
479
480
|
response: {
|
|
480
481
|
body: { success: true, data: { total: 3, imported: 3, skipped: 0, failed: 0 } },
|
|
@@ -500,7 +501,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
500
501
|
describe('Validation', () => {
|
|
501
502
|
describe('POST meta/validate — Validate metadata', () => {
|
|
502
503
|
it('dispatches validation', async () => {
|
|
503
|
-
mockDispatcher.
|
|
504
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
504
505
|
handled: true,
|
|
505
506
|
response: {
|
|
506
507
|
body: { success: true, data: { valid: true } },
|
|
@@ -515,7 +516,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
515
516
|
});
|
|
516
517
|
|
|
517
518
|
it('returns errors for invalid metadata', async () => {
|
|
518
|
-
mockDispatcher.
|
|
519
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
519
520
|
handled: true,
|
|
520
521
|
response: {
|
|
521
522
|
body: {
|
|
@@ -544,7 +545,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
544
545
|
describe('Type Registry', () => {
|
|
545
546
|
describe('GET meta/types — List types', () => {
|
|
546
547
|
it('returns all registered types', async () => {
|
|
547
|
-
mockDispatcher.
|
|
548
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
548
549
|
handled: true,
|
|
549
550
|
response: {
|
|
550
551
|
body: { success: true, data: ['object', 'view', 'flow', 'agent'] },
|
|
@@ -561,7 +562,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
561
562
|
|
|
562
563
|
describe('GET meta/types/object — Get type info', () => {
|
|
563
564
|
it('returns type metadata', async () => {
|
|
564
|
-
mockDispatcher.
|
|
565
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
565
566
|
handled: true,
|
|
566
567
|
response: {
|
|
567
568
|
body: {
|
|
@@ -593,7 +594,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
593
594
|
describe('Dependency Tracking', () => {
|
|
594
595
|
describe('GET meta/objects/account/dependencies — Get dependencies', () => {
|
|
595
596
|
it('returns dependencies', async () => {
|
|
596
|
-
mockDispatcher.
|
|
597
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
597
598
|
handled: true,
|
|
598
599
|
response: {
|
|
599
600
|
body: {
|
|
@@ -619,7 +620,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
619
620
|
|
|
620
621
|
describe('GET meta/objects/account/dependents — Get dependents', () => {
|
|
621
622
|
it('returns dependents', async () => {
|
|
622
|
-
mockDispatcher.
|
|
623
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({
|
|
623
624
|
handled: true,
|
|
624
625
|
response: {
|
|
625
626
|
body: {
|
|
@@ -647,7 +648,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
647
648
|
|
|
648
649
|
describe('Error Handling', () => {
|
|
649
650
|
it('returns 404 when metadata not found', async () => {
|
|
650
|
-
mockDispatcher.
|
|
651
|
+
mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false });
|
|
651
652
|
|
|
652
653
|
const req = makeReq('http://localhost/api/meta/objects/nonexistent');
|
|
653
654
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'nonexistent'] } });
|
|
@@ -655,7 +656,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
655
656
|
});
|
|
656
657
|
|
|
657
658
|
it('returns 500 on dispatcher exception', async () => {
|
|
658
|
-
mockDispatcher.
|
|
659
|
+
mockDispatcher.dispatch.mockRejectedValueOnce(new Error('Internal error'));
|
|
659
660
|
|
|
660
661
|
const req = makeReq('http://localhost/api/meta/objects');
|
|
661
662
|
const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } });
|
|
@@ -664,7 +665,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
664
665
|
});
|
|
665
666
|
|
|
666
667
|
it('returns custom status code from error', async () => {
|
|
667
|
-
mockDispatcher.
|
|
668
|
+
mockDispatcher.dispatch.mockRejectedValueOnce(
|
|
668
669
|
Object.assign(new Error('Forbidden'), { statusCode: 403 }),
|
|
669
670
|
);
|
|
670
671
|
|
|
@@ -682,7 +683,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
682
683
|
it('correctly joins nested segments', async () => {
|
|
683
684
|
const req = makeReq('http://localhost/api/meta/objects/account/fields/name');
|
|
684
685
|
await handler(req, { params: { objectstack: ['meta', 'objects', 'account', 'fields', 'name'] } });
|
|
685
|
-
expect(mockDispatcher.
|
|
686
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
686
687
|
'objects/account/fields/name',
|
|
687
688
|
expect.any(Object),
|
|
688
689
|
'GET',
|
|
@@ -694,7 +695,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
694
695
|
const req = makeReq('http://localhost/api/meta');
|
|
695
696
|
// With just ['meta'], subPath becomes empty after slice(1)
|
|
696
697
|
await handler(req, { params: { objectstack: ['meta'] } });
|
|
697
|
-
expect(mockDispatcher.
|
|
698
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
698
699
|
'',
|
|
699
700
|
expect.any(Object),
|
|
700
701
|
'GET',
|
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
|
});
|