@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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/nextjs@3.2.8 build /home/runner/work/spec/spec/packages/adapters/nextjs
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
  CLI Building entry: src/index.ts
@@ -12,11 +12,11 @@
12
12
  CJS Build start
13
13
  ESM dist/index.mjs 7.52 KB
14
14
  ESM dist/index.mjs.map 16.55 KB
15
- ESM ⚡️ Build success in 115ms
15
+ ESM ⚡️ Build success in 79ms
16
16
  CJS dist/index.js 8.76 KB
17
17
  CJS dist/index.js.map 16.60 KB
18
- CJS ⚡️ Build success in 116ms
18
+ CJS ⚡️ Build success in 79ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 13439ms
20
+ DTS ⚡️ Build success in 13754ms
21
21
  DTS dist/index.d.mts 2.63 KB
22
22
  DTS dist/index.d.ts 2.63 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @objectstack/nextjs
2
2
 
3
+ ## 3.3.0
4
+
5
+ ### Patch Changes
6
+
7
+ - @objectstack/runtime@3.3.0
8
+
9
+ ## 3.2.9
10
+
11
+ ### Patch Changes
12
+
13
+ - @objectstack/runtime@3.2.9
14
+
3
15
  ## 3.2.8
4
16
 
5
17
  ### Patch Changes
package/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "@objectstack/nextjs",
3
- "version": "3.2.8",
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.7",
8
+ "next": "^16.2.1",
9
9
  "react": "^19.2.4",
10
10
  "react-dom": "^19.2.4",
11
- "@objectstack/runtime": "^3.2.8"
11
+ "@objectstack/runtime": "^3.3.0"
12
12
  },
13
13
  "devDependencies": {
14
- "next": "^16.1.7",
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.2.8"
19
+ "@objectstack/runtime": "3.3.0"
20
20
  },
21
21
  "scripts": {
22
22
  "build": "tsup --config ../../../tsup.config.ts",
@@ -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 handleMetadata with correct path', async () => {
110
- mockDispatcher.handleMetadata.mockResolvedValueOnce({
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.handleMetadata).toHaveBeenCalledWith(
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 handleMetadata with item-level path', async () => {
139
- mockDispatcher.handleMetadata.mockResolvedValueOnce({
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.handleMetadata).toHaveBeenCalledWith(
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata).toHaveBeenCalledWith(
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata).toHaveBeenCalledWith(
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 handleMetadata', async () => {
211
- mockDispatcher.handleMetadata.mockResolvedValueOnce({
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.handleMetadata).toHaveBeenCalledWith(
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.handleMetadata).toHaveBeenCalledWith(
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.handleMetadata).toHaveBeenCalledWith(
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({
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.handleMetadata.mockResolvedValueOnce({ handled: false });
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.handleMetadata.mockRejectedValueOnce(new Error('Internal error'));
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.handleMetadata.mockRejectedValueOnce(
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.handleMetadata).toHaveBeenCalledWith(
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.handleMetadata).toHaveBeenCalledWith(
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
  });
@@ -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 calls handleMetadata', async () => {
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(res.body).toEqual({ objects: [] });
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.handleMetadata).toHaveBeenCalledWith(
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.handleMetadata).toHaveBeenCalledWith(
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 calls handleData', async () => {
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(res.body).toEqual({ records: [] });
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.handleData).toHaveBeenCalledWith(
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.handleData).toHaveBeenCalledWith(
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.handleData.mockResolvedValueOnce({ handled: false });
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.handleData.mockRejectedValueOnce(new Error());
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.handleData.mockRejectedValueOnce(
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.handleData.mockResolvedValueOnce({
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.handleData.mockResolvedValueOnce({
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.handleData.mockResolvedValueOnce({
463
+ mockDispatcher.dispatch.mockResolvedValueOnce({
461
464
  handled: true,
462
465
  result: rawResult,
463
466
  });