@getvision/server 0.1.2 → 0.2.0-b49d8db-develop
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 +1 -0
- package/package.json +5 -4
- package/src/service.ts +48 -2
- package/src/types.ts +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ tsc --noEmit
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getvision/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-b49d8db-develop",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vision Server - Meta-framework with built-in observability, pub/sub, and type-safe APIs",
|
|
6
6
|
"exports": {
|
|
@@ -13,17 +13,18 @@
|
|
|
13
13
|
},
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@getvision/core": "0.0.
|
|
16
|
+
"@getvision/core": "0.0.3",
|
|
17
17
|
"@hono/node-server": "^1.19.5",
|
|
18
18
|
"bullmq": "^5.62.0",
|
|
19
|
+
"hono-rate-limiter": "^0.4.2",
|
|
19
20
|
"hono": "^4.10.4",
|
|
20
21
|
"ioredis": "^5.8.2",
|
|
21
22
|
"zod": "^4.1.11"
|
|
22
23
|
},
|
|
23
24
|
"peerDependencies": {},
|
|
24
25
|
"devDependencies": {
|
|
25
|
-
"@repo/eslint-config": "
|
|
26
|
-
"@repo/typescript-config": "
|
|
26
|
+
"@repo/eslint-config": "0.0.1",
|
|
27
|
+
"@repo/typescript-config": "0.0.1",
|
|
27
28
|
"@types/node": "^20.14.9",
|
|
28
29
|
"typescript": "5.9.3"
|
|
29
30
|
},
|
package/src/service.ts
CHANGED
|
@@ -5,6 +5,32 @@ import type { EndpointConfig, Handler } from './types'
|
|
|
5
5
|
import { getVisionContext } from './vision-app'
|
|
6
6
|
import { eventRegistry } from './event-registry'
|
|
7
7
|
import type { EventBus } from './event-bus'
|
|
8
|
+
import { rateLimiter } from 'hono-rate-limiter'
|
|
9
|
+
|
|
10
|
+
// Simple window parser supporting values like '15m', '1h', '30s', '2d' or plain milliseconds as number string
|
|
11
|
+
function parseWindowMs(window: string): number {
|
|
12
|
+
const trimmed = window.trim()
|
|
13
|
+
if (/^\d+$/.test(trimmed)) return Number(trimmed)
|
|
14
|
+
const match = trimmed.match(/^(\d+)\s*([smhd])$/i)
|
|
15
|
+
if (!match) throw new Error(`Invalid ratelimit window: ${window}`)
|
|
16
|
+
const value = Number(match[1])
|
|
17
|
+
const unit = match[2].toLowerCase()
|
|
18
|
+
const multipliers: Record<string, number> = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 }
|
|
19
|
+
return value * multipliers[unit]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getClientKey(c: Context, method: string, path: string): string {
|
|
23
|
+
const ip =
|
|
24
|
+
c.req.header('x-forwarded-for')?.split(',')[0].trim() ||
|
|
25
|
+
c.req.header('x-real-ip') ||
|
|
26
|
+
c.req.header('cf-connecting-ip') ||
|
|
27
|
+
c.req.header('fly-client-ip') ||
|
|
28
|
+
c.req.header('x-client-ip') ||
|
|
29
|
+
''
|
|
30
|
+
// Fallback to UA if no IP available (still scoped per endpoint)
|
|
31
|
+
const ua = c.req.header('user-agent') || 'unknown'
|
|
32
|
+
return `${ip || ua}:${method}:${path}`
|
|
33
|
+
}
|
|
8
34
|
|
|
9
35
|
/**
|
|
10
36
|
* Event schema map - accumulates event types as they're registered
|
|
@@ -315,8 +341,28 @@ export class ServiceBuilder<
|
|
|
315
341
|
|
|
316
342
|
// Register HTTP endpoints
|
|
317
343
|
this.endpoints.forEach((ep) => {
|
|
318
|
-
//
|
|
319
|
-
|
|
344
|
+
// Prepare rate limiter when configured per-endpoint
|
|
345
|
+
let rateLimitMw: MiddlewareHandler<E, string, any, any> | undefined
|
|
346
|
+
const rl = ep.config?.ratelimit as EndpointConfig['ratelimit'] | undefined
|
|
347
|
+
if (rl) {
|
|
348
|
+
const windowMs = parseWindowMs(rl.window)
|
|
349
|
+
const limit = rl.requests
|
|
350
|
+
rateLimitMw = rateLimiter({
|
|
351
|
+
windowMs,
|
|
352
|
+
limit,
|
|
353
|
+
standardHeaders: 'draft-6',
|
|
354
|
+
keyGenerator: (c) => getClientKey(c, ep.method, ep.path),
|
|
355
|
+
// If user provides a distributed store (e.g., RedisStore), pass it through
|
|
356
|
+
...(rl.store ? { store: rl.store } : {}),
|
|
357
|
+
})
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Combine global + rate-limit (if any) + endpoint-specific middleware
|
|
361
|
+
const allMiddleware = [
|
|
362
|
+
...this.globalMiddleware,
|
|
363
|
+
...(rateLimitMw ? [rateLimitMw] as MiddlewareHandler<E, string, any, any>[] : []),
|
|
364
|
+
...ep.middleware,
|
|
365
|
+
]
|
|
320
366
|
|
|
321
367
|
// Create handler with middleware chain
|
|
322
368
|
const finalHandler = async (c: Context<E, any, I>) => {
|
package/src/types.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface EndpointConfig {
|
|
|
19
19
|
middleware?: MiddlewareHandler[]
|
|
20
20
|
// TODO: Below not implemented yet features
|
|
21
21
|
auth?: boolean
|
|
22
|
-
ratelimit?: { requests: number; window: string }
|
|
22
|
+
ratelimit?: { requests: number; window: string; store?: any }
|
|
23
23
|
cache?: { ttl: number }
|
|
24
24
|
}
|
|
25
25
|
|