@geekmidas/constructs 0.2.4 → 0.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/dist/{AWSLambdaFunction-H65WfXLt.mjs → AWSLambdaFunction-C54a1doJ.mjs} +3 -3
- package/dist/{AWSLambdaFunction-H65WfXLt.mjs.map → AWSLambdaFunction-C54a1doJ.mjs.map} +1 -1
- package/dist/{AWSLambdaFunction-C-fuCLA3.cjs → AWSLambdaFunction-EPGY4s7i.cjs} +3 -3
- package/dist/{AWSLambdaFunction-C-fuCLA3.cjs.map → AWSLambdaFunction-EPGY4s7i.cjs.map} +1 -1
- package/dist/{AmazonApiGatewayEndpointAdaptor-pEWzF2uY.mjs → AmazonApiGatewayEndpointAdaptor-BT9JXihC.mjs} +2 -2
- package/dist/{AmazonApiGatewayEndpointAdaptor-pEWzF2uY.mjs.map → AmazonApiGatewayEndpointAdaptor-BT9JXihC.mjs.map} +1 -1
- package/dist/{AmazonApiGatewayEndpointAdaptor-Bk6ssx3K.cjs → AmazonApiGatewayEndpointAdaptor-DNFvvdmW.cjs} +2 -2
- package/dist/{AmazonApiGatewayEndpointAdaptor-Bk6ssx3K.cjs.map → AmazonApiGatewayEndpointAdaptor-DNFvvdmW.cjs.map} +1 -1
- package/dist/{AmazonApiGatewayV1EndpointAdaptor-MJpRbIaQ.mjs → AmazonApiGatewayV1EndpointAdaptor-BbkfC1dk.mjs} +2 -2
- package/dist/{AmazonApiGatewayV1EndpointAdaptor-MJpRbIaQ.mjs.map → AmazonApiGatewayV1EndpointAdaptor-BbkfC1dk.mjs.map} +1 -1
- package/dist/{AmazonApiGatewayV1EndpointAdaptor-uBp_4zLf.cjs → AmazonApiGatewayV1EndpointAdaptor-BiNzaAxD.cjs} +2 -2
- package/dist/{AmazonApiGatewayV1EndpointAdaptor-uBp_4zLf.cjs.map → AmazonApiGatewayV1EndpointAdaptor-BiNzaAxD.cjs.map} +1 -1
- package/dist/{AmazonApiGatewayV2EndpointAdaptor-I1W23Nvn.cjs → AmazonApiGatewayV2EndpointAdaptor-DZclCykB.cjs} +2 -2
- package/dist/{AmazonApiGatewayV2EndpointAdaptor-I1W23Nvn.cjs.map → AmazonApiGatewayV2EndpointAdaptor-DZclCykB.cjs.map} +1 -1
- package/dist/{AmazonApiGatewayV2EndpointAdaptor-ChO8BlDz.mjs → AmazonApiGatewayV2EndpointAdaptor-jRrQrIdm.mjs} +2 -2
- package/dist/{AmazonApiGatewayV2EndpointAdaptor-ChO8BlDz.mjs.map → AmazonApiGatewayV2EndpointAdaptor-jRrQrIdm.mjs.map} +1 -1
- package/dist/{BaseFunctionBuilder-B5gkW0Kt.mjs → BaseFunctionBuilder-Czi1Jwza.mjs} +2 -2
- package/dist/{BaseFunctionBuilder-B5gkW0Kt.mjs.map → BaseFunctionBuilder-Czi1Jwza.mjs.map} +1 -1
- package/dist/{BaseFunctionBuilder-C5Se7pdL.cjs → BaseFunctionBuilder-MYG3C9ug.cjs} +2 -2
- package/dist/{BaseFunctionBuilder-C5Se7pdL.cjs.map → BaseFunctionBuilder-MYG3C9ug.cjs.map} +1 -1
- package/dist/{Construct-BYSPikVm.cjs → Construct-Ba5cMxib.cjs} +2 -2
- package/dist/{Construct-BYSPikVm.cjs.map → Construct-Ba5cMxib.cjs.map} +1 -1
- package/dist/{Construct-LWeB1rSQ.mjs → Construct-DdyGHuag.mjs} +2 -2
- package/dist/{Construct-LWeB1rSQ.mjs.map → Construct-DdyGHuag.mjs.map} +1 -1
- package/dist/Construct.cjs +1 -1
- package/dist/Construct.mjs +1 -1
- package/dist/{Cron-Dy_HW2Vv.mjs → Cron-BxhGs5up.mjs} +3 -3
- package/dist/{Cron-Dy_HW2Vv.mjs.map → Cron-BxhGs5up.mjs.map} +1 -1
- package/dist/{Cron-Bi3QOge_.cjs → Cron-CGF4YAfM.cjs} +3 -3
- package/dist/{Cron-Bi3QOge_.cjs.map → Cron-CGF4YAfM.cjs.map} +1 -1
- package/dist/{CronBuilder-Dv_w7Yri.cjs → CronBuilder-CcxKRtVP.cjs} +4 -4
- package/dist/{CronBuilder-Dv_w7Yri.cjs.map → CronBuilder-CcxKRtVP.cjs.map} +1 -1
- package/dist/{CronBuilder-Bl3A2Zp4.mjs → CronBuilder-d2jh-IB2.mjs} +4 -4
- package/dist/{CronBuilder-Bl3A2Zp4.mjs.map → CronBuilder-d2jh-IB2.mjs.map} +1 -1
- package/dist/{Endpoint-BJo9Hhwm.cjs → Endpoint-BVGZXFyV.cjs} +3 -3
- package/dist/{Endpoint-BJo9Hhwm.cjs.map → Endpoint-BVGZXFyV.cjs.map} +1 -1
- package/dist/{Endpoint-B70_KKhu.mjs → Endpoint-CuOEswxJ.mjs} +3 -3
- package/dist/{Endpoint-B70_KKhu.mjs.map → Endpoint-CuOEswxJ.mjs.map} +1 -1
- package/dist/{EndpointBuilder-DeswNQdG.cjs → EndpointBuilder-Cgj1P_ra.cjs} +4 -4
- package/dist/{EndpointBuilder-DeswNQdG.cjs.map → EndpointBuilder-Cgj1P_ra.cjs.map} +1 -1
- package/dist/{EndpointBuilder-FyyoFTJ5.mjs → EndpointBuilder-DnCB1h1j.mjs} +4 -4
- package/dist/{EndpointBuilder-FyyoFTJ5.mjs.map → EndpointBuilder-DnCB1h1j.mjs.map} +1 -1
- package/dist/{EndpointFactory-eZc-XpNm.cjs → EndpointFactory-CYj6BYok.cjs} +2 -2
- package/dist/{EndpointFactory-eZc-XpNm.cjs.map → EndpointFactory-CYj6BYok.cjs.map} +1 -1
- package/dist/{EndpointFactory-CAneQs06.mjs → EndpointFactory-CbdxPCIH.mjs} +2 -2
- package/dist/{EndpointFactory-CAneQs06.mjs.map → EndpointFactory-CbdxPCIH.mjs.map} +1 -1
- package/dist/{Function-DfKsM5Kx.mjs → Function-BVHqIDp9.mjs} +2 -2
- package/dist/{Function-DfKsM5Kx.mjs.map → Function-BVHqIDp9.mjs.map} +1 -1
- package/dist/{Function-DagDbeXo.cjs → Function-DDZb1525.cjs} +2 -2
- package/dist/{Function-DagDbeXo.cjs.map → Function-DDZb1525.cjs.map} +1 -1
- package/dist/{FunctionBuilder-CVT7bG2o.mjs → FunctionBuilder-CrDYgfiI.mjs} +4 -4
- package/dist/{FunctionBuilder-CVT7bG2o.mjs.map → FunctionBuilder-CrDYgfiI.mjs.map} +1 -1
- package/dist/{FunctionBuilder-DXvG_XD-.cjs → FunctionBuilder-DswJ-9sD.cjs} +4 -4
- package/dist/{FunctionBuilder-DXvG_XD-.cjs.map → FunctionBuilder-DswJ-9sD.cjs.map} +1 -1
- package/dist/{FunctionExecutionWrapper-DkNycmOh.cjs → FunctionExecutionWrapper-BYI2bGTL.cjs} +2 -2
- package/dist/{FunctionExecutionWrapper-DkNycmOh.cjs.map → FunctionExecutionWrapper-BYI2bGTL.cjs.map} +1 -1
- package/dist/{FunctionExecutionWrapper-Bubnr0zA.mjs → FunctionExecutionWrapper-CLDh7Z2_.mjs} +2 -2
- package/dist/{FunctionExecutionWrapper-Bubnr0zA.mjs.map → FunctionExecutionWrapper-CLDh7Z2_.mjs.map} +1 -1
- package/dist/{HonoEndpointAdaptor-BusUWu1w.d.cts → HonoEndpointAdaptor-Bx9Y1bCZ.d.cts} +3 -3
- package/dist/{HonoEndpointAdaptor-CcvXzoYV.mjs → HonoEndpointAdaptor-NLlQk5iU.mjs} +4 -5
- package/dist/{HonoEndpointAdaptor-CcvXzoYV.mjs.map → HonoEndpointAdaptor-NLlQk5iU.mjs.map} +1 -1
- package/dist/{HonoEndpointAdaptor-DodwLM0-.cjs → HonoEndpointAdaptor-e6l9eVDU.cjs} +4 -5
- package/dist/{HonoEndpointAdaptor-DodwLM0-.cjs.map → HonoEndpointAdaptor-e6l9eVDU.cjs.map} +1 -1
- package/dist/{HonoEndpointAdaptor-g8xxh3tS.d.mts → HonoEndpointAdaptor-kb1ByjUL.d.mts} +3 -3
- package/dist/{Subscriber-DOt3svUC.cjs → Subscriber-BiHjVXtM.cjs} +2 -2
- package/dist/{Subscriber-DOt3svUC.cjs.map → Subscriber-BiHjVXtM.cjs.map} +1 -1
- package/dist/{Subscriber-kCHbH2fZ.mjs → Subscriber-BmPf9GFb.mjs} +2 -2
- package/dist/{Subscriber-kCHbH2fZ.mjs.map → Subscriber-BmPf9GFb.mjs.map} +1 -1
- package/dist/{SubscriberBuilder-Cj2u9k5Q.cjs → SubscriberBuilder-Cp1C-xtT.cjs} +2 -2
- package/dist/{SubscriberBuilder-Cj2u9k5Q.cjs.map → SubscriberBuilder-Cp1C-xtT.cjs.map} +1 -1
- package/dist/{SubscriberBuilder-DmxMU89X.mjs → SubscriberBuilder-DJPEeYDJ.mjs} +2 -2
- package/dist/{SubscriberBuilder-DmxMU89X.mjs.map → SubscriberBuilder-DJPEeYDJ.mjs.map} +1 -1
- package/dist/{TestEndpointAdaptor-wA-fmq4v.cjs → TestEndpointAdaptor-opEisC30.cjs} +2 -2
- package/dist/{TestEndpointAdaptor-wA-fmq4v.cjs.map → TestEndpointAdaptor-opEisC30.cjs.map} +1 -1
- package/dist/{TestEndpointAdaptor-1pPixE6y.mjs → TestEndpointAdaptor-oq5mfglM.mjs} +2 -2
- package/dist/{TestEndpointAdaptor-1pPixE6y.mjs.map → TestEndpointAdaptor-oq5mfglM.mjs.map} +1 -1
- package/dist/adaptors/aws.cjs +11 -11
- package/dist/adaptors/aws.mjs +11 -11
- package/dist/adaptors/hono.cjs +8 -8
- package/dist/adaptors/hono.d.cts +1 -1
- package/dist/adaptors/hono.d.mts +1 -1
- package/dist/adaptors/hono.mjs +8 -8
- package/dist/adaptors/testing.cjs +7 -7
- package/dist/adaptors/testing.mjs +7 -7
- package/dist/crons/Cron.cjs +6 -6
- package/dist/crons/Cron.mjs +6 -6
- package/dist/crons/CronBuilder.cjs +7 -7
- package/dist/crons/CronBuilder.mjs +7 -7
- package/dist/crons/index.cjs +7 -7
- package/dist/crons/index.d.mts +4 -4
- package/dist/crons/index.mjs +7 -7
- package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.cjs +7 -7
- package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.mjs +7 -7
- package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.cjs +8 -8
- package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.mjs +8 -8
- package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.cjs +8 -8
- package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.mjs +8 -8
- package/dist/endpoints/Endpoint.cjs +6 -6
- package/dist/endpoints/Endpoint.mjs +6 -6
- package/dist/endpoints/EndpointBuilder.cjs +7 -7
- package/dist/endpoints/EndpointBuilder.mjs +7 -7
- package/dist/endpoints/EndpointFactory.cjs +8 -8
- package/dist/endpoints/EndpointFactory.mjs +8 -8
- package/dist/endpoints/HonoEndpointAdaptor.cjs +8 -8
- package/dist/endpoints/HonoEndpointAdaptor.d.cts +1 -1
- package/dist/endpoints/HonoEndpointAdaptor.d.mts +1 -1
- package/dist/endpoints/HonoEndpointAdaptor.mjs +8 -8
- package/dist/endpoints/TestEndpointAdaptor.cjs +7 -7
- package/dist/endpoints/TestEndpointAdaptor.mjs +7 -7
- package/dist/endpoints/helpers.cjs +7 -7
- package/dist/endpoints/helpers.mjs +7 -7
- package/dist/endpoints/index.cjs +8 -8
- package/dist/endpoints/index.d.cts +2 -2
- package/dist/endpoints/index.d.mts +2 -2
- package/dist/endpoints/index.mjs +8 -8
- package/dist/functions/AWSLambdaFunction.cjs +6 -6
- package/dist/functions/AWSLambdaFunction.mjs +6 -6
- package/dist/functions/BaseFunctionBuilder.cjs +2 -2
- package/dist/functions/BaseFunctionBuilder.mjs +2 -2
- package/dist/functions/Function.cjs +2 -2
- package/dist/functions/Function.mjs +2 -2
- package/dist/functions/FunctionBuilder.cjs +4 -4
- package/dist/functions/FunctionBuilder.mjs +4 -4
- package/dist/functions/FunctionExecutionWrapper.cjs +5 -5
- package/dist/functions/FunctionExecutionWrapper.mjs +5 -5
- package/dist/functions/TestFunctionAdaptor.cjs +4 -4
- package/dist/functions/TestFunctionAdaptor.mjs +4 -4
- package/dist/functions/index.cjs +5 -5
- package/dist/functions/index.mjs +5 -5
- package/dist/functions-C6EK1xL6.mjs +8 -0
- package/dist/{functions-JhRsNoAZ.mjs.map → functions-C6EK1xL6.mjs.map} +1 -1
- package/dist/{functions-FCb-wWFC.cjs → functions-fTid0RMK.cjs} +2 -2
- package/dist/{functions-FCb-wWFC.cjs.map → functions-fTid0RMK.cjs.map} +1 -1
- package/dist/{helpers-DxxSpLfw.cjs → helpers-BcP1tXAi.cjs} +2 -2
- package/dist/{helpers-DxxSpLfw.cjs.map → helpers-BcP1tXAi.cjs.map} +1 -1
- package/dist/{helpers-C3B2lVrM.mjs → helpers-ByRTDO_m.mjs} +2 -2
- package/dist/{helpers-C3B2lVrM.mjs.map → helpers-ByRTDO_m.mjs.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/subscribers/Subscriber.cjs +2 -2
- package/dist/subscribers/Subscriber.mjs +2 -2
- package/dist/subscribers/SubscriberBuilder.cjs +3 -3
- package/dist/subscribers/SubscriberBuilder.mjs +3 -3
- package/dist/subscribers/index.cjs +3 -3
- package/dist/subscribers/index.d.cts +2 -2
- package/dist/subscribers/index.mjs +3 -3
- package/package.json +64 -13
- package/src/Construct.ts +3 -3
- package/src/__benchmarks__/endpoint.bench.ts +494 -0
- package/src/__benchmarks__/hono-server.bench.ts +249 -0
- package/src/endpoints/HonoEndpointAdaptor.ts +1 -2
- package/dist/functions-JhRsNoAZ.mjs +0 -8
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AuditRecord,
|
|
3
|
+
AuditStorage,
|
|
4
|
+
AuditableAction,
|
|
5
|
+
} from '@geekmidas/audit';
|
|
6
|
+
import type { EventPublisher, PublishableMessage } from '@geekmidas/events';
|
|
7
|
+
import { LogLevel } from '@geekmidas/logger';
|
|
8
|
+
import { ConsoleLogger } from '@geekmidas/logger/console';
|
|
9
|
+
import type { Service } from '@geekmidas/services';
|
|
10
|
+
import { bench, describe } from 'vitest';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { e } from '../endpoints';
|
|
13
|
+
import { TestEndpointAdaptor } from '../endpoints/TestEndpointAdaptor';
|
|
14
|
+
import type { MappedAudit } from '../endpoints/audit';
|
|
15
|
+
|
|
16
|
+
// Silent logger for benchmarks - no console output
|
|
17
|
+
const silentLogger = new ConsoleLogger({}, LogLevel.Silent);
|
|
18
|
+
const api = e.logger(silentLogger);
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Mock Services for Benchmarks
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
// Simple database service
|
|
25
|
+
interface MockDatabase {
|
|
26
|
+
query: (sql: string) => Promise<any[]>;
|
|
27
|
+
findById: (table: string, id: string) => Promise<any>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DatabaseService: Service<'database', MockDatabase> = {
|
|
31
|
+
serviceName: 'database' as const,
|
|
32
|
+
register: async () => ({
|
|
33
|
+
query: async () => [],
|
|
34
|
+
findById: async (table, id) => ({ id, table }),
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Cache service
|
|
39
|
+
interface MockCache {
|
|
40
|
+
get: (key: string) => Promise<string | null>;
|
|
41
|
+
set: (key: string, value: string) => Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const CacheService: Service<'cache', MockCache> = {
|
|
45
|
+
serviceName: 'cache' as const,
|
|
46
|
+
register: async () => ({
|
|
47
|
+
get: async () => null,
|
|
48
|
+
set: async () => {},
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Auth service
|
|
53
|
+
interface MockAuth {
|
|
54
|
+
validateToken: (token: string) => Promise<boolean>;
|
|
55
|
+
getUserId: (token: string) => Promise<string>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const AuthService: Service<'auth', MockAuth> = {
|
|
59
|
+
serviceName: 'auth' as const,
|
|
60
|
+
register: async () => ({
|
|
61
|
+
validateToken: async () => true,
|
|
62
|
+
getUserId: async () => 'user-123',
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Audit storage
|
|
67
|
+
type TestAuditAction =
|
|
68
|
+
| AuditableAction<'user.created', { userId: string }>
|
|
69
|
+
| AuditableAction<'user.updated', { userId: string }>;
|
|
70
|
+
|
|
71
|
+
class MockAuditStorage implements AuditStorage<TestAuditAction> {
|
|
72
|
+
declare readonly __auditActionType?: TestAuditAction;
|
|
73
|
+
async write(_records: AuditRecord[]): Promise<void> {}
|
|
74
|
+
async query(): Promise<AuditRecord[]> {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const AuditStorageService: Service<'auditStorage', MockAuditStorage> = {
|
|
80
|
+
serviceName: 'auditStorage' as const,
|
|
81
|
+
register: async () => new MockAuditStorage(),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Event publisher
|
|
85
|
+
type TestEvent = PublishableMessage<'user.created', { userId: string }>;
|
|
86
|
+
|
|
87
|
+
class MockPublisher implements EventPublisher<TestEvent> {
|
|
88
|
+
async publish(_messages: TestEvent[]): Promise<void> {}
|
|
89
|
+
async close(): Promise<void> {}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const PublisherService: Service<'publisher', MockPublisher> = {
|
|
93
|
+
serviceName: 'publisher' as const,
|
|
94
|
+
register: async () => new MockPublisher(),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Pre-registered services for benchmarks
|
|
98
|
+
const registeredDatabase = await DatabaseService.register({} as any);
|
|
99
|
+
const registeredCache = await CacheService.register({} as any);
|
|
100
|
+
const registeredAuth = await AuthService.register({} as any);
|
|
101
|
+
const auditStorage = new MockAuditStorage();
|
|
102
|
+
|
|
103
|
+
describe('Endpoint Handling - Simple', () => {
|
|
104
|
+
const simpleEndpoint = api
|
|
105
|
+
.get('/health')
|
|
106
|
+
.handle(async () => ({ status: 'ok' }));
|
|
107
|
+
const adaptor = new TestEndpointAdaptor(simpleEndpoint);
|
|
108
|
+
|
|
109
|
+
bench('simple GET endpoint', async () => {
|
|
110
|
+
await adaptor.request({
|
|
111
|
+
services: {},
|
|
112
|
+
headers: {},
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('Endpoint Handling - With Validation', () => {
|
|
118
|
+
const validatedEndpoint = api
|
|
119
|
+
.post('/users')
|
|
120
|
+
.body(z.object({ name: z.string(), email: z.string().email() }))
|
|
121
|
+
.output(z.object({ id: z.string() }))
|
|
122
|
+
.handle(async () => ({ id: '123' }));
|
|
123
|
+
|
|
124
|
+
const adaptor = new TestEndpointAdaptor(validatedEndpoint);
|
|
125
|
+
|
|
126
|
+
bench('POST with body validation', async () => {
|
|
127
|
+
await adaptor.request({
|
|
128
|
+
services: {},
|
|
129
|
+
headers: { 'content-type': 'application/json' },
|
|
130
|
+
body: { name: 'Test User', email: 'test@example.com' },
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const complexBodyEndpoint = api
|
|
135
|
+
.post('/complex')
|
|
136
|
+
.body(
|
|
137
|
+
z.object({
|
|
138
|
+
user: z.object({
|
|
139
|
+
name: z.string(),
|
|
140
|
+
email: z.string().email(),
|
|
141
|
+
profile: z.object({
|
|
142
|
+
bio: z.string().optional(),
|
|
143
|
+
avatar: z.string().url().optional(),
|
|
144
|
+
}),
|
|
145
|
+
}),
|
|
146
|
+
items: z.array(
|
|
147
|
+
z.object({
|
|
148
|
+
id: z.string(),
|
|
149
|
+
quantity: z.number().int().positive(),
|
|
150
|
+
}),
|
|
151
|
+
),
|
|
152
|
+
}),
|
|
153
|
+
)
|
|
154
|
+
.output(z.object({ success: z.boolean() }))
|
|
155
|
+
.handle(async () => ({ success: true }));
|
|
156
|
+
|
|
157
|
+
const complexAdaptor = new TestEndpointAdaptor(complexBodyEndpoint);
|
|
158
|
+
|
|
159
|
+
bench('POST with complex body validation', async () => {
|
|
160
|
+
await complexAdaptor.request({
|
|
161
|
+
services: {},
|
|
162
|
+
headers: { 'content-type': 'application/json' },
|
|
163
|
+
body: {
|
|
164
|
+
user: {
|
|
165
|
+
name: 'Test',
|
|
166
|
+
email: 'test@example.com',
|
|
167
|
+
profile: { bio: 'Hello', avatar: 'https://example.com/avatar.jpg' },
|
|
168
|
+
},
|
|
169
|
+
items: [
|
|
170
|
+
{ id: '1', quantity: 2 },
|
|
171
|
+
{ id: '2', quantity: 5 },
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe('Endpoint Handling - Path Params', () => {
|
|
179
|
+
const paramsEndpoint = api
|
|
180
|
+
.get('/users/:id')
|
|
181
|
+
.params(z.object({ id: z.string() }))
|
|
182
|
+
.output(z.object({ id: z.string(), name: z.string() }))
|
|
183
|
+
.handle(async ({ params }) => ({ id: params.id, name: 'User' }));
|
|
184
|
+
|
|
185
|
+
const adaptor = new TestEndpointAdaptor(paramsEndpoint);
|
|
186
|
+
|
|
187
|
+
bench('GET with path params', async () => {
|
|
188
|
+
await adaptor.request({
|
|
189
|
+
services: {},
|
|
190
|
+
headers: {},
|
|
191
|
+
params: { id: '123' },
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('Endpoint Handling - Query Params', () => {
|
|
197
|
+
const queryEndpoint = api
|
|
198
|
+
.get('/search')
|
|
199
|
+
.query(
|
|
200
|
+
z.object({
|
|
201
|
+
q: z.string(),
|
|
202
|
+
page: z.coerce.number().default(1),
|
|
203
|
+
limit: z.coerce.number().default(10),
|
|
204
|
+
}),
|
|
205
|
+
)
|
|
206
|
+
.output(z.object({ results: z.array(z.unknown()) }))
|
|
207
|
+
.handle(async () => ({ results: [] }));
|
|
208
|
+
|
|
209
|
+
const adaptor = new TestEndpointAdaptor(queryEndpoint);
|
|
210
|
+
|
|
211
|
+
bench('GET with query params', async () => {
|
|
212
|
+
await adaptor.request({
|
|
213
|
+
services: {},
|
|
214
|
+
headers: {},
|
|
215
|
+
query: { q: 'test', page: 2, limit: 20 },
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// ============================================================================
|
|
221
|
+
// Service Integration Benchmarks
|
|
222
|
+
// ============================================================================
|
|
223
|
+
|
|
224
|
+
describe('Endpoint Handling - Single Service', () => {
|
|
225
|
+
const singleServiceEndpoint = api
|
|
226
|
+
.get('/users/:id')
|
|
227
|
+
.services([DatabaseService])
|
|
228
|
+
.params(z.object({ id: z.string() }))
|
|
229
|
+
.output(z.object({ id: z.string(), name: z.string() }))
|
|
230
|
+
.handle(async ({ params, services }) => {
|
|
231
|
+
const user = await services.database.findById('users', params.id);
|
|
232
|
+
return { id: user.id, name: 'User' };
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const adaptor = new TestEndpointAdaptor(singleServiceEndpoint);
|
|
236
|
+
|
|
237
|
+
bench('GET with single service', async () => {
|
|
238
|
+
await adaptor.request({
|
|
239
|
+
services: { database: registeredDatabase },
|
|
240
|
+
headers: {},
|
|
241
|
+
params: { id: '123' },
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe('Endpoint Handling - Multiple Services', () => {
|
|
247
|
+
const multiServiceEndpoint = api
|
|
248
|
+
.get('/users/:id')
|
|
249
|
+
.services([DatabaseService, CacheService, AuthService])
|
|
250
|
+
.params(z.object({ id: z.string() }))
|
|
251
|
+
.output(z.object({ id: z.string(), name: z.string(), cached: z.boolean() }))
|
|
252
|
+
.handle(async ({ params, services }) => {
|
|
253
|
+
// Check cache first
|
|
254
|
+
const cached = await services.cache.get(`user:${params.id}`);
|
|
255
|
+
if (cached) {
|
|
256
|
+
return { id: params.id, name: cached, cached: true };
|
|
257
|
+
}
|
|
258
|
+
// Validate auth
|
|
259
|
+
await services.auth.validateToken('token');
|
|
260
|
+
// Query database
|
|
261
|
+
const user = await services.database.findById('users', params.id);
|
|
262
|
+
await services.cache.set(`user:${params.id}`, 'User');
|
|
263
|
+
return { id: user.id, name: 'User', cached: false };
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const adaptor = new TestEndpointAdaptor(multiServiceEndpoint);
|
|
267
|
+
|
|
268
|
+
bench('GET with multiple services (3)', async () => {
|
|
269
|
+
await adaptor.request({
|
|
270
|
+
services: {
|
|
271
|
+
database: registeredDatabase,
|
|
272
|
+
cache: registeredCache,
|
|
273
|
+
auth: registeredAuth,
|
|
274
|
+
},
|
|
275
|
+
headers: {},
|
|
276
|
+
params: { id: '123' },
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// ============================================================================
|
|
282
|
+
// Session & Authorization Benchmarks
|
|
283
|
+
// ============================================================================
|
|
284
|
+
|
|
285
|
+
describe('Endpoint Handling - Session Extraction', () => {
|
|
286
|
+
type UserSession = { userId: string; role: string };
|
|
287
|
+
|
|
288
|
+
// Session is configured at factory level
|
|
289
|
+
const sessionApi = api.session<UserSession>(async ({ header }) => {
|
|
290
|
+
const token = header('authorization')?.replace('Bearer ', '');
|
|
291
|
+
if (!token) return { userId: 'anonymous', role: 'guest' };
|
|
292
|
+
return { userId: 'user-123', role: 'admin' };
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const sessionEndpoint = sessionApi
|
|
296
|
+
.get('/profile')
|
|
297
|
+
.output(z.object({ userId: z.string(), role: z.string() }))
|
|
298
|
+
.handle(async ({ session }) => ({
|
|
299
|
+
userId: session.userId,
|
|
300
|
+
role: session.role,
|
|
301
|
+
}));
|
|
302
|
+
|
|
303
|
+
const adaptor = new TestEndpointAdaptor(sessionEndpoint);
|
|
304
|
+
|
|
305
|
+
bench('GET with session extraction', async () => {
|
|
306
|
+
await adaptor.request({
|
|
307
|
+
services: {},
|
|
308
|
+
headers: { authorization: 'Bearer test-token' },
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
describe('Endpoint Handling - Authorization', () => {
|
|
314
|
+
// Authorization is configured at factory level
|
|
315
|
+
const authApi = api.authorize(async ({ header }) => {
|
|
316
|
+
const token = header('authorization');
|
|
317
|
+
return token === 'Bearer admin-token';
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const authEndpoint = authApi
|
|
321
|
+
.post('/admin/action')
|
|
322
|
+
.body(z.object({ action: z.string() }))
|
|
323
|
+
.output(z.object({ success: z.boolean() }))
|
|
324
|
+
.handle(async () => ({ success: true }));
|
|
325
|
+
|
|
326
|
+
const adaptor = new TestEndpointAdaptor(authEndpoint);
|
|
327
|
+
|
|
328
|
+
bench('POST with authorization check', async () => {
|
|
329
|
+
await adaptor.request({
|
|
330
|
+
services: {},
|
|
331
|
+
headers: {
|
|
332
|
+
authorization: 'Bearer admin-token',
|
|
333
|
+
'content-type': 'application/json',
|
|
334
|
+
},
|
|
335
|
+
body: { action: 'delete-all' },
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// ============================================================================
|
|
341
|
+
// Audit Logging Benchmarks
|
|
342
|
+
// ============================================================================
|
|
343
|
+
|
|
344
|
+
describe('Endpoint Handling - Declarative Audit', () => {
|
|
345
|
+
const outputSchema = z.object({ id: z.string(), email: z.string() });
|
|
346
|
+
type OutputType = z.infer<typeof outputSchema>;
|
|
347
|
+
|
|
348
|
+
const audits: MappedAudit<TestAuditAction, typeof outputSchema>[] = [
|
|
349
|
+
{
|
|
350
|
+
type: 'user.created',
|
|
351
|
+
payload: (response: OutputType) => ({ userId: response.id }),
|
|
352
|
+
},
|
|
353
|
+
];
|
|
354
|
+
|
|
355
|
+
const auditEndpoint = api
|
|
356
|
+
.post('/users')
|
|
357
|
+
.auditor(AuditStorageService)
|
|
358
|
+
.body(z.object({ name: z.string(), email: z.string() }))
|
|
359
|
+
.output(outputSchema)
|
|
360
|
+
.audit(audits)
|
|
361
|
+
.handle(async ({ body }) => ({
|
|
362
|
+
id: crypto.randomUUID(),
|
|
363
|
+
email: body.email,
|
|
364
|
+
}));
|
|
365
|
+
|
|
366
|
+
const adaptor = new TestEndpointAdaptor(auditEndpoint);
|
|
367
|
+
|
|
368
|
+
bench('POST with declarative audit', async () => {
|
|
369
|
+
await adaptor.request({
|
|
370
|
+
services: {},
|
|
371
|
+
headers: { 'content-type': 'application/json' },
|
|
372
|
+
body: { name: 'Test User', email: 'test@example.com' },
|
|
373
|
+
auditorStorage: auditStorage,
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
describe('Endpoint Handling - Manual Audit', () => {
|
|
379
|
+
const manualAuditEndpoint = api
|
|
380
|
+
.post('/users')
|
|
381
|
+
.auditor(AuditStorageService)
|
|
382
|
+
.body(z.object({ name: z.string(), email: z.string() }))
|
|
383
|
+
.output(z.object({ id: z.string() }))
|
|
384
|
+
.handle(async ({ body, auditor }) => {
|
|
385
|
+
const id = crypto.randomUUID();
|
|
386
|
+
auditor?.audit('user.created', { userId: id });
|
|
387
|
+
return { id };
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const adaptor = new TestEndpointAdaptor(manualAuditEndpoint);
|
|
391
|
+
|
|
392
|
+
bench('POST with manual audit', async () => {
|
|
393
|
+
await adaptor.request({
|
|
394
|
+
services: {},
|
|
395
|
+
headers: { 'content-type': 'application/json' },
|
|
396
|
+
body: { name: 'Test User', email: 'test@example.com' },
|
|
397
|
+
auditorStorage: auditStorage,
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// ============================================================================
|
|
403
|
+
// Event Publishing Benchmarks
|
|
404
|
+
// ============================================================================
|
|
405
|
+
|
|
406
|
+
describe('Endpoint Handling - Event Publishing', () => {
|
|
407
|
+
const publisherEndpoint = api
|
|
408
|
+
.post('/users')
|
|
409
|
+
.publisher(PublisherService)
|
|
410
|
+
.body(z.object({ name: z.string(), email: z.string() }))
|
|
411
|
+
.output(z.object({ id: z.string() }))
|
|
412
|
+
.event({
|
|
413
|
+
type: 'user.created',
|
|
414
|
+
payload: (response) => ({ userId: response.id }),
|
|
415
|
+
})
|
|
416
|
+
.handle(async () => ({ id: crypto.randomUUID() }));
|
|
417
|
+
|
|
418
|
+
const adaptor = new TestEndpointAdaptor(publisherEndpoint);
|
|
419
|
+
|
|
420
|
+
bench('POST with event publishing', async () => {
|
|
421
|
+
await adaptor.request({
|
|
422
|
+
services: {},
|
|
423
|
+
headers: { 'content-type': 'application/json' },
|
|
424
|
+
body: { name: 'Test User', email: 'test@example.com' },
|
|
425
|
+
publisher: PublisherService,
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// ============================================================================
|
|
431
|
+
// Complex Real-World Scenarios
|
|
432
|
+
// ============================================================================
|
|
433
|
+
|
|
434
|
+
describe('Endpoint Handling - Full Stack (Services + Session + Audit)', () => {
|
|
435
|
+
type UserSession = { userId: string; role: string };
|
|
436
|
+
const outputSchema = z.object({ id: z.string(), email: z.string() });
|
|
437
|
+
type OutputType = z.infer<typeof outputSchema>;
|
|
438
|
+
|
|
439
|
+
const audits: MappedAudit<TestAuditAction, typeof outputSchema>[] = [
|
|
440
|
+
{
|
|
441
|
+
type: 'user.created',
|
|
442
|
+
payload: (response: OutputType) => ({ userId: response.id }),
|
|
443
|
+
},
|
|
444
|
+
];
|
|
445
|
+
|
|
446
|
+
// Configure factory with session and authorization
|
|
447
|
+
const fullStackApi = api
|
|
448
|
+
.services([DatabaseService, CacheService])
|
|
449
|
+
.session<UserSession>(async () => ({ userId: 'admin-123', role: 'admin' }))
|
|
450
|
+
.authorize(async ({ session }) => session.role === 'admin');
|
|
451
|
+
|
|
452
|
+
const fullStackEndpoint = fullStackApi
|
|
453
|
+
.post('/users')
|
|
454
|
+
.auditor(AuditStorageService)
|
|
455
|
+
.body(
|
|
456
|
+
z.object({
|
|
457
|
+
name: z.string(),
|
|
458
|
+
email: z.string().email(),
|
|
459
|
+
profile: z.object({ bio: z.string().optional() }),
|
|
460
|
+
}),
|
|
461
|
+
)
|
|
462
|
+
.output(outputSchema)
|
|
463
|
+
.audit(audits)
|
|
464
|
+
.handle(async ({ body, services }) => {
|
|
465
|
+
// Simulate real work
|
|
466
|
+
await services.database.query('INSERT INTO users...');
|
|
467
|
+
await services.cache.set(`user:new`, body.name);
|
|
468
|
+
return {
|
|
469
|
+
id: crypto.randomUUID(),
|
|
470
|
+
email: body.email,
|
|
471
|
+
};
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
const adaptor = new TestEndpointAdaptor(fullStackEndpoint);
|
|
475
|
+
|
|
476
|
+
bench('POST full stack (services + session + audit)', async () => {
|
|
477
|
+
await adaptor.request({
|
|
478
|
+
services: {
|
|
479
|
+
database: registeredDatabase,
|
|
480
|
+
cache: registeredCache,
|
|
481
|
+
},
|
|
482
|
+
headers: {
|
|
483
|
+
authorization: 'Bearer admin-token',
|
|
484
|
+
'content-type': 'application/json',
|
|
485
|
+
},
|
|
486
|
+
body: {
|
|
487
|
+
name: 'New User',
|
|
488
|
+
email: 'new@example.com',
|
|
489
|
+
profile: { bio: 'Hello world' },
|
|
490
|
+
},
|
|
491
|
+
auditorStorage: auditStorage,
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
});
|