@objectstack/runtime 3.1.1 → 3.2.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 +6 -6
- package/CHANGELOG.md +10 -0
- package/package.json +5 -5
- package/src/http-dispatcher.test.ts +95 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/runtime@3.
|
|
2
|
+
> @objectstack/runtime@3.2.0 build /home/runner/work/spec/spec/packages/runtime
|
|
3
3
|
> tsup --config ../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[32mCJS[39m [1mdist/index.cjs [22m[32m57.43 KB[39m
|
|
14
|
-
[32mCJS[39m [1mdist/index.cjs.map [22m[32m123.73 KB[39m
|
|
15
|
-
[32mCJS[39m ⚡️ Build success in 149ms
|
|
16
13
|
[32mESM[39m [1mdist/index.js [22m[32m55.51 KB[39m
|
|
17
14
|
[32mESM[39m [1mdist/index.js.map [22m[32m123.66 KB[39m
|
|
18
|
-
[32mESM[39m ⚡️ Build success in
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 91ms
|
|
16
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m57.43 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.cjs.map [22m[32m123.73 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 92ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 8046ms
|
|
21
21
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m20.64 KB[39m
|
|
22
22
|
[32mDTS[39m [1mdist/index.d.cts [22m[32m20.64 KB[39m
|
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/runtime",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "ObjectStack Core Runtime & Query Engine",
|
|
6
6
|
"type": "module",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"zod": "^4.3.6",
|
|
18
|
-
"@objectstack/core": "3.
|
|
19
|
-
"@objectstack/rest": "3.
|
|
20
|
-
"@objectstack/spec": "3.
|
|
21
|
-
"@objectstack/types": "3.
|
|
18
|
+
"@objectstack/core": "3.2.0",
|
|
19
|
+
"@objectstack/rest": "3.2.0",
|
|
20
|
+
"@objectstack/spec": "3.2.0",
|
|
21
|
+
"@objectstack/types": "3.2.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"typescript": "^5.0.0",
|
|
@@ -439,6 +439,101 @@ describe('HttpDispatcher', () => {
|
|
|
439
439
|
});
|
|
440
440
|
});
|
|
441
441
|
|
|
442
|
+
// ═══════════════════════════════════════════════════════════════
|
|
443
|
+
// handleData — expand/populate parameter flow
|
|
444
|
+
// ═══════════════════════════════════════════════════════════════
|
|
445
|
+
|
|
446
|
+
describe('handleData', () => {
|
|
447
|
+
it('should pass expand and select to broker for GET /data/:object/:id', async () => {
|
|
448
|
+
mockBroker.call.mockResolvedValue({ object: 'order_item', id: 'oi_1', record: { _id: 'oi_1' } });
|
|
449
|
+
|
|
450
|
+
const result = await dispatcher.handleData(
|
|
451
|
+
'/order_item/oi_1', 'GET', {},
|
|
452
|
+
{ expand: 'order,product', select: 'name,total' },
|
|
453
|
+
{ request: {} }
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
expect(result.handled).toBe(true);
|
|
457
|
+
expect(result.response?.status).toBe(200);
|
|
458
|
+
expect(mockBroker.call).toHaveBeenCalledWith(
|
|
459
|
+
'data.get',
|
|
460
|
+
{ object: 'order_item', id: 'oi_1', expand: 'order,product', select: 'name,total' },
|
|
461
|
+
{ request: {} }
|
|
462
|
+
);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('should NOT pass non-allowlisted params for GET /data/:object/:id', async () => {
|
|
466
|
+
mockBroker.call.mockResolvedValue({ object: 'task', id: 't1', record: {} });
|
|
467
|
+
|
|
468
|
+
await dispatcher.handleData(
|
|
469
|
+
'/task/t1', 'GET', {},
|
|
470
|
+
{ expand: 'assignee', malicious: 'drop_table', filter: 'hack' },
|
|
471
|
+
{ request: {} }
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
// Only expand is passed; malicious and filter are dropped
|
|
475
|
+
expect(mockBroker.call).toHaveBeenCalledWith(
|
|
476
|
+
'data.get',
|
|
477
|
+
{ object: 'task', id: 't1', expand: 'assignee' },
|
|
478
|
+
{ request: {} }
|
|
479
|
+
);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it('should pass full query (with expand/populate) for GET /data/:object list', async () => {
|
|
483
|
+
mockBroker.call.mockResolvedValue({ object: 'task', records: [], total: 0 });
|
|
484
|
+
|
|
485
|
+
const query = { populate: 'assignee,project', top: '10', skip: '0' };
|
|
486
|
+
const result = await dispatcher.handleData(
|
|
487
|
+
'/task', 'GET', {},
|
|
488
|
+
query,
|
|
489
|
+
{ request: {} }
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
expect(result.handled).toBe(true);
|
|
493
|
+
expect(mockBroker.call).toHaveBeenCalledWith(
|
|
494
|
+
'data.query',
|
|
495
|
+
{ object: 'task', query },
|
|
496
|
+
{ request: {} }
|
|
497
|
+
);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('should pass expand in query for GET /data/:object list', async () => {
|
|
501
|
+
mockBroker.call.mockResolvedValue({ object: 'order', records: [], total: 0 });
|
|
502
|
+
|
|
503
|
+
const query = { expand: 'customer,products' };
|
|
504
|
+
await dispatcher.handleData('/order', 'GET', {}, query, { request: {} });
|
|
505
|
+
|
|
506
|
+
expect(mockBroker.call).toHaveBeenCalledWith(
|
|
507
|
+
'data.query',
|
|
508
|
+
{ object: 'order', query: { expand: 'customer,products' } },
|
|
509
|
+
{ request: {} }
|
|
510
|
+
);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('should return error if object name is missing', async () => {
|
|
514
|
+
const result = await dispatcher.handleData('/', 'GET', {}, {}, { request: {} });
|
|
515
|
+
expect(result.handled).toBe(true);
|
|
516
|
+
expect(result.response?.status).toBe(400);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('should handle POST /data/:object/query with body containing expand', async () => {
|
|
520
|
+
mockBroker.call.mockResolvedValue({ object: 'task', records: [] });
|
|
521
|
+
|
|
522
|
+
await dispatcher.handleData(
|
|
523
|
+
'/task/query', 'POST',
|
|
524
|
+
{ filter: { status: 'active' }, populate: ['assignee'] },
|
|
525
|
+
{},
|
|
526
|
+
{ request: {} }
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
expect(mockBroker.call).toHaveBeenCalledWith(
|
|
530
|
+
'data.query',
|
|
531
|
+
{ object: 'task', filter: { status: 'active' }, populate: ['assignee'] },
|
|
532
|
+
{ request: {} }
|
|
533
|
+
);
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
|
|
442
537
|
// ═══════════════════════════════════════════════════════════════
|
|
443
538
|
// Error handling for service method failures
|
|
444
539
|
// ═══════════════════════════════════════════════════════════════
|