glasswork 0.1.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/LICENSE +21 -0
- package/README.md +182 -0
- package/dist/bootstrap.d.ts +55 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +359 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/core/bootstrap.d.ts +55 -0
- package/dist/core/bootstrap.d.ts.map +1 -0
- package/dist/core/bootstrap.js +329 -0
- package/dist/core/bootstrap.js.map +1 -0
- package/dist/core/module.d.ts +21 -0
- package/dist/core/module.d.ts.map +1 -0
- package/dist/core/module.js +85 -0
- package/dist/core/module.js.map +1 -0
- package/dist/core/types.d.ts +273 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/environment.d.ts +21 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +29 -0
- package/dist/environment.js.map +1 -0
- package/dist/error-handler.d.ts +33 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/error-handler.js +53 -0
- package/dist/error-handler.js.map +1 -0
- package/dist/errors.d.ts +36 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +65 -0
- package/dist/errors.js.map +1 -0
- package/dist/get-client-ip.d.ts +24 -0
- package/dist/get-client-ip.d.ts.map +1 -0
- package/dist/get-client-ip.js +49 -0
- package/dist/get-client-ip.js.map +1 -0
- package/dist/http/error-handler.d.ts +33 -0
- package/dist/http/error-handler.d.ts.map +1 -0
- package/dist/http/error-handler.js +55 -0
- package/dist/http/error-handler.js.map +1 -0
- package/dist/http/errors.d.ts +36 -0
- package/dist/http/errors.d.ts.map +1 -0
- package/dist/http/errors.js +65 -0
- package/dist/http/errors.js.map +1 -0
- package/dist/http/route-helpers.d.ts +172 -0
- package/dist/http/route-helpers.d.ts.map +1 -0
- package/dist/http/route-helpers.js +249 -0
- package/dist/http/route-helpers.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +37 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +52 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +7 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +182 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/module.d.ts +21 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +85 -0
- package/dist/module.js.map +1 -0
- package/dist/omit.d.ts +20 -0
- package/dist/omit.d.ts.map +1 -0
- package/dist/omit.js +28 -0
- package/dist/omit.js.map +1 -0
- package/dist/openapi/defaults.d.ts +11 -0
- package/dist/openapi/defaults.d.ts.map +1 -0
- package/dist/openapi/defaults.js +127 -0
- package/dist/openapi/defaults.js.map +1 -0
- package/dist/openapi/hooks.d.ts +14 -0
- package/dist/openapi/hooks.d.ts.map +1 -0
- package/dist/openapi/hooks.js +80 -0
- package/dist/openapi/hooks.js.map +1 -0
- package/dist/openapi/openapi.d.ts +24 -0
- package/dist/openapi/openapi.d.ts.map +1 -0
- package/dist/openapi/openapi.js +108 -0
- package/dist/openapi/openapi.js.map +1 -0
- package/dist/openapi-defaults.d.ts +11 -0
- package/dist/openapi-defaults.d.ts.map +1 -0
- package/dist/openapi-defaults.js +127 -0
- package/dist/openapi-defaults.js.map +1 -0
- package/dist/openapi-hooks.d.ts +14 -0
- package/dist/openapi-hooks.d.ts.map +1 -0
- package/dist/openapi-hooks.js +80 -0
- package/dist/openapi-hooks.js.map +1 -0
- package/dist/openapi.d.ts +24 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +106 -0
- package/dist/openapi.js.map +1 -0
- package/dist/pagination.d.ts +48 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +43 -0
- package/dist/pagination.js.map +1 -0
- package/dist/pick.d.ts +20 -0
- package/dist/pick.d.ts.map +1 -0
- package/dist/pick.js +28 -0
- package/dist/pick.js.map +1 -0
- package/dist/rate-limit.d.ts +7 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +180 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/route-helpers.d.ts +172 -0
- package/dist/route-helpers.d.ts.map +1 -0
- package/dist/route-helpers.js +249 -0
- package/dist/route-helpers.js.map +1 -0
- package/dist/serialize-dates.d.ts +27 -0
- package/dist/serialize-dates.d.ts.map +1 -0
- package/dist/serialize-dates.js +39 -0
- package/dist/serialize-dates.js.map +1 -0
- package/dist/testing.d.ts +87 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +94 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +273 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/deep-merge.d.ts +5 -0
- package/dist/utils/deep-merge.d.ts.map +1 -0
- package/dist/utils/deep-merge.js +21 -0
- package/dist/utils/deep-merge.js.map +1 -0
- package/dist/utils/environment.d.ts +21 -0
- package/dist/utils/environment.d.ts.map +1 -0
- package/dist/utils/environment.js +29 -0
- package/dist/utils/environment.js.map +1 -0
- package/dist/utils/get-client-ip.d.ts +24 -0
- package/dist/utils/get-client-ip.d.ts.map +1 -0
- package/dist/utils/get-client-ip.js +49 -0
- package/dist/utils/get-client-ip.js.map +1 -0
- package/dist/utils/logger.d.ts +37 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/omit.d.ts +20 -0
- package/dist/utils/omit.d.ts.map +1 -0
- package/dist/utils/omit.js +28 -0
- package/dist/utils/omit.js.map +1 -0
- package/dist/utils/pagination.d.ts +48 -0
- package/dist/utils/pagination.d.ts.map +1 -0
- package/dist/utils/pagination.js +42 -0
- package/dist/utils/pagination.js.map +1 -0
- package/dist/utils/pick.d.ts +20 -0
- package/dist/utils/pick.d.ts.map +1 -0
- package/dist/utils/pick.js +28 -0
- package/dist/utils/pick.js.map +1 -0
- package/dist/utils/serialize-dates.d.ts +27 -0
- package/dist/utils/serialize-dates.d.ts.map +1 -0
- package/dist/utils/serialize-dates.js +39 -0
- package/dist/utils/serialize-dates.js.map +1 -0
- package/dist/utils.d.ts +9 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +31 -0
- package/dist/utils.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
/**
|
|
3
|
+
* Pagination input parameters
|
|
4
|
+
*/
|
|
5
|
+
export interface PaginationInput {
|
|
6
|
+
page: number;
|
|
7
|
+
limit: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Paginated output structure
|
|
11
|
+
*/
|
|
12
|
+
export interface PaginatedOutput<T> {
|
|
13
|
+
data: T[];
|
|
14
|
+
total: number;
|
|
15
|
+
page: number;
|
|
16
|
+
limit: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Execute a paginated query and set response headers.
|
|
20
|
+
*
|
|
21
|
+
* This helper extracts pagination parameters from the query string,
|
|
22
|
+
* executes the provided function, and sets the appropriate response headers.
|
|
23
|
+
*
|
|
24
|
+
* @template T - The type of items in the result
|
|
25
|
+
* @param c - Hono context
|
|
26
|
+
* @param fn - Function that executes the paginated query
|
|
27
|
+
* @returns Array of items (headers are set as side effect)
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* router.get('/users', route({
|
|
32
|
+
* paginate: true,
|
|
33
|
+
* handler: async ({ context }) => {
|
|
34
|
+
* return await paginate(context, (p) => userService.getUsers(p));
|
|
35
|
+
* }
|
|
36
|
+
* }));
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function paginate<T>(c: Context, fn: (p: PaginationInput) => Promise<PaginatedOutput<T>>): Promise<T[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Extract pagination parameters from Hono context.
|
|
42
|
+
*
|
|
43
|
+
* @param c - Hono context
|
|
44
|
+
* @param defaultLimit - Default limit if not provided (default: 100)
|
|
45
|
+
* @returns Pagination input with page and limit
|
|
46
|
+
*/
|
|
47
|
+
export declare function paginationInput(c: Context, defaultLimit?: number): PaginationInput;
|
|
48
|
+
//# sourceMappingURL=pagination.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,CAAC,EAAE,OAAO,EACV,EAAE,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GACtD,OAAO,CAAC,CAAC,EAAE,CAAC,CAUd;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,SAAM,GAAG,eAAe,CAI/E"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execute a paginated query and set response headers.
|
|
3
|
+
*
|
|
4
|
+
* This helper extracts pagination parameters from the query string,
|
|
5
|
+
* executes the provided function, and sets the appropriate response headers.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of items in the result
|
|
8
|
+
* @param c - Hono context
|
|
9
|
+
* @param fn - Function that executes the paginated query
|
|
10
|
+
* @returns Array of items (headers are set as side effect)
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* router.get('/users', route({
|
|
15
|
+
* paginate: true,
|
|
16
|
+
* handler: async ({ context }) => {
|
|
17
|
+
* return await paginate(context, (p) => userService.getUsers(p));
|
|
18
|
+
* }
|
|
19
|
+
* }));
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export async function paginate(c, fn) {
|
|
23
|
+
const pagination = paginationInput(c);
|
|
24
|
+
const result = await fn(pagination);
|
|
25
|
+
// Set pagination headers
|
|
26
|
+
c.header('X-Total-Count', result.total.toString());
|
|
27
|
+
c.header('X-Page', result.page.toString());
|
|
28
|
+
c.header('X-Limit', result.limit.toString());
|
|
29
|
+
return result.data;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Extract pagination parameters from Hono context.
|
|
33
|
+
*
|
|
34
|
+
* @param c - Hono context
|
|
35
|
+
* @param defaultLimit - Default limit if not provided (default: 100)
|
|
36
|
+
* @returns Pagination input with page and limit
|
|
37
|
+
*/
|
|
38
|
+
export function paginationInput(c, defaultLimit = 100) {
|
|
39
|
+
const page = Number.parseInt(c.req.query('page') ?? '1', 10);
|
|
40
|
+
const limit = Number.parseInt(c.req.query('limit') ?? defaultLimit.toString(), 10);
|
|
41
|
+
return { page, limit };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=pagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,CAAU,EACV,EAAuD;IAEvD,MAAM,UAAU,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,CAAC;IAEpC,yBAAyB;IACzB,CAAC,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE7C,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,CAAU,EAAE,YAAY,GAAG,GAAG;IAC5D,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IACnF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC"}
|
package/dist/pick.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a new object by picking specified keys from the source object.
|
|
3
|
+
*
|
|
4
|
+
* This implementation preserves the object's prototype, making it suitable
|
|
5
|
+
* for use with class instances and objects with custom prototypes.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of the source object
|
|
8
|
+
* @template K - The type of keys to pick (must be keys of T)
|
|
9
|
+
* @param obj - The source object to pick keys from
|
|
10
|
+
* @param paths - Array of keys to pick from the object
|
|
11
|
+
* @returns A new object containing only the specified keys
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const user = { id: 1, name: 'John', email: 'john@example.com', password: 'secret' };
|
|
16
|
+
* const publicUser = pick(user, ['id', 'name']); // { id: 1, name: 'John' }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function pick<T extends object, K extends keyof T>(obj: T, paths: K[]): Pick<T, K>;
|
|
20
|
+
//# sourceMappingURL=pick.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pick.d.ts","sourceRoot":"","sources":["../src/pick.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAUxF"}
|
package/dist/pick.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a new object by picking specified keys from the source object.
|
|
3
|
+
*
|
|
4
|
+
* This implementation preserves the object's prototype, making it suitable
|
|
5
|
+
* for use with class instances and objects with custom prototypes.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of the source object
|
|
8
|
+
* @template K - The type of keys to pick (must be keys of T)
|
|
9
|
+
* @param obj - The source object to pick keys from
|
|
10
|
+
* @param paths - Array of keys to pick from the object
|
|
11
|
+
* @returns A new object containing only the specified keys
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const user = { id: 1, name: 'John', email: 'john@example.com', password: 'secret' };
|
|
16
|
+
* const publicUser = pick(user, ['id', 'name']); // { id: 1, name: 'John' }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function pick(obj, paths) {
|
|
20
|
+
const result = Object.create(Object.getPrototypeOf(obj));
|
|
21
|
+
for (const key of paths) {
|
|
22
|
+
if (Object.hasOwn(obj, key)) {
|
|
23
|
+
result[key] = obj[key];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=pick.js.map
|
package/dist/pick.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pick.js","sourceRoot":"","sources":["../src/pick.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,IAAI,CAAsC,GAAM,EAAE,KAAU;IAC1E,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAe,CAAC;IAEvE,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MiddlewareHandler } from 'hono';
|
|
2
|
+
import type { RateLimitOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Create rate limiting middleware
|
|
5
|
+
*/
|
|
6
|
+
export declare function createRateLimitMiddleware(options: RateLimitOptions): MiddlewareHandler;
|
|
7
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAkKnD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,iBAAiB,CAuDtF"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory rate limiter storage
|
|
3
|
+
*/
|
|
4
|
+
class MemoryStore {
|
|
5
|
+
store = new Map();
|
|
6
|
+
async get(key) {
|
|
7
|
+
const item = this.store.get(key);
|
|
8
|
+
if (!item) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
if (item.windowEnd < Date.now()) {
|
|
12
|
+
this.store.delete(key);
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return item;
|
|
16
|
+
}
|
|
17
|
+
async set(key, value) {
|
|
18
|
+
this.store.set(key, value);
|
|
19
|
+
}
|
|
20
|
+
async increment(key) {
|
|
21
|
+
const item = this.store.get(key);
|
|
22
|
+
if (item) {
|
|
23
|
+
item.count += 1;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Cleanup expired entries periodically
|
|
27
|
+
startCleanup(intervalMs = 60000) {
|
|
28
|
+
setInterval(() => {
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
for (const [key, item] of this.store.entries()) {
|
|
31
|
+
if (item.windowEnd < now) {
|
|
32
|
+
this.store.delete(key);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}, intervalMs);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* DynamoDB rate limiter storage
|
|
40
|
+
*/
|
|
41
|
+
class DynamoDBStore {
|
|
42
|
+
client;
|
|
43
|
+
tableName;
|
|
44
|
+
constructor(tableName, region) {
|
|
45
|
+
this.tableName = tableName;
|
|
46
|
+
// Lazy load AWS SDK only when needed
|
|
47
|
+
this.initClient(region);
|
|
48
|
+
}
|
|
49
|
+
async initClient(region) {
|
|
50
|
+
const { DynamoDBClient } = await import('@aws-sdk/client-dynamodb');
|
|
51
|
+
const { DynamoDBDocumentClient } = await import('@aws-sdk/lib-dynamodb');
|
|
52
|
+
const dynamoClient = new DynamoDBClient(region ? { region } : {});
|
|
53
|
+
this.client = DynamoDBDocumentClient.from(dynamoClient);
|
|
54
|
+
}
|
|
55
|
+
async get(key) {
|
|
56
|
+
const { GetCommand } = await import('@aws-sdk/lib-dynamodb');
|
|
57
|
+
const client = this.client;
|
|
58
|
+
try {
|
|
59
|
+
// @ts-expect-error - client type is complex
|
|
60
|
+
const { Item } = await client.send(new GetCommand({
|
|
61
|
+
TableName: this.tableName,
|
|
62
|
+
Key: { bucketId: key },
|
|
63
|
+
}));
|
|
64
|
+
if (!Item || Item.windowEnd < Date.now()) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
count: Item.count,
|
|
69
|
+
windowEnd: Item.windowEnd,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async set(key, value) {
|
|
77
|
+
const { PutCommand } = await import('@aws-sdk/lib-dynamodb');
|
|
78
|
+
const client = this.client;
|
|
79
|
+
const expiresAt = Math.floor(value.windowEnd / 1000);
|
|
80
|
+
try {
|
|
81
|
+
// @ts-expect-error - client type is complex
|
|
82
|
+
await client.send(new PutCommand({
|
|
83
|
+
TableName: this.tableName,
|
|
84
|
+
Item: {
|
|
85
|
+
bucketId: key,
|
|
86
|
+
count: value.count,
|
|
87
|
+
windowEnd: value.windowEnd,
|
|
88
|
+
expiresAt,
|
|
89
|
+
},
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error('[Glasswork] DynamoDB rate limit set failed:', error);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async increment(key) {
|
|
97
|
+
const { UpdateCommand } = await import('@aws-sdk/lib-dynamodb');
|
|
98
|
+
const client = this.client;
|
|
99
|
+
try {
|
|
100
|
+
// @ts-expect-error - client type is complex
|
|
101
|
+
await client.send(new UpdateCommand({
|
|
102
|
+
TableName: this.tableName,
|
|
103
|
+
Key: { bucketId: key },
|
|
104
|
+
UpdateExpression: 'SET #c = #c + :inc',
|
|
105
|
+
ExpressionAttributeNames: { '#c': 'count' },
|
|
106
|
+
ExpressionAttributeValues: { ':inc': 1 },
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.error('[Glasswork] DynamoDB rate limit increment failed:', error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Extract client IP from request context
|
|
116
|
+
*/
|
|
117
|
+
function getClientIdentifier(context) {
|
|
118
|
+
// Check various headers for client IP
|
|
119
|
+
const forwarded = context.req.header('x-forwarded-for');
|
|
120
|
+
if (forwarded) {
|
|
121
|
+
return forwarded.split(',')[0].trim();
|
|
122
|
+
}
|
|
123
|
+
const realIp = context.req.header('x-real-ip');
|
|
124
|
+
if (realIp) {
|
|
125
|
+
return realIp;
|
|
126
|
+
}
|
|
127
|
+
return 'unknown';
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Create rate limiting middleware
|
|
131
|
+
*/
|
|
132
|
+
export function createRateLimitMiddleware(options) {
|
|
133
|
+
const { storage, windowMs = 60000, maxRequests = 100, dynamodb } = options;
|
|
134
|
+
// Create appropriate storage backend
|
|
135
|
+
const store = storage === 'dynamodb' && dynamodb
|
|
136
|
+
? new DynamoDBStore(dynamodb.tableName, dynamodb.region)
|
|
137
|
+
: new MemoryStore();
|
|
138
|
+
// Start cleanup for memory store
|
|
139
|
+
if (store instanceof MemoryStore) {
|
|
140
|
+
store.startCleanup();
|
|
141
|
+
}
|
|
142
|
+
return async (context, next) => {
|
|
143
|
+
const clientId = getClientIdentifier(context);
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
const windowEnd = now + windowMs;
|
|
146
|
+
const setHeaders = (remaining, resetMs) => {
|
|
147
|
+
context.header('RateLimit-Limit', String(maxRequests));
|
|
148
|
+
context.header('RateLimit-Remaining', String(Math.max(0, remaining)));
|
|
149
|
+
context.header('RateLimit-Reset', String(Math.ceil(resetMs / 1000)));
|
|
150
|
+
};
|
|
151
|
+
try {
|
|
152
|
+
const item = await store.get(clientId);
|
|
153
|
+
// New window or expired
|
|
154
|
+
if (!item) {
|
|
155
|
+
await store.set(clientId, { count: 1, windowEnd });
|
|
156
|
+
setHeaders(maxRequests - 1, windowMs);
|
|
157
|
+
await next();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// Rate limit exceeded
|
|
161
|
+
if (item.count >= maxRequests) {
|
|
162
|
+
const retryMs = item.windowEnd - now;
|
|
163
|
+
setHeaders(0, retryMs);
|
|
164
|
+
context.header('Retry-After', String(Math.ceil(retryMs / 1000)));
|
|
165
|
+
return context.json({ error: 'Too Many Requests' }, 429);
|
|
166
|
+
}
|
|
167
|
+
// Increment counter
|
|
168
|
+
await store.increment(clientId);
|
|
169
|
+
setHeaders(maxRequests - (item.count + 1), item.windowEnd - now);
|
|
170
|
+
await next();
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
console.error('[Glasswork] Rate limiter error:', error);
|
|
174
|
+
// Fail open - allow request
|
|
175
|
+
setHeaders(maxRequests, windowMs);
|
|
176
|
+
await next();
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { Context, Hono, MiddlewareHandler } from 'hono';
|
|
2
|
+
import type { DescribeRouteOptions } from 'hono-openapi';
|
|
3
|
+
import type { BaseIssue, BaseSchema, BaseSchemaAsync, InferOutput } from 'valibot';
|
|
4
|
+
import type { OpenAPIResponseHook } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Set the global OpenAPI response hooks.
|
|
7
|
+
* This is called during bootstrap and makes hooks available to route definitions.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export declare function setGlobalResponseHooks(hooks: OpenAPIResponseHook[]): void;
|
|
12
|
+
/**
|
|
13
|
+
* Get the current global OpenAPI response hooks.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export declare function getGlobalResponseHooks(): OpenAPIResponseHook[];
|
|
18
|
+
/**
|
|
19
|
+
* Valibot schema type (compatible with both sync and async schemas)
|
|
20
|
+
*/
|
|
21
|
+
export type ValibotSchema = BaseSchema<unknown, unknown, BaseIssue<unknown>> | BaseSchemaAsync<unknown, unknown, BaseIssue<unknown>>;
|
|
22
|
+
/**
|
|
23
|
+
* Helper type to infer the output type from a Valibot schema
|
|
24
|
+
*/
|
|
25
|
+
type InferSchemaType<T> = T extends ValibotSchema ? InferOutput<T> : never;
|
|
26
|
+
/**
|
|
27
|
+
* Helper type to infer the response type from the 200/201 status code schema
|
|
28
|
+
*
|
|
29
|
+
* Prisma client extension converts Date → string at the database layer,
|
|
30
|
+
* so handlers return string-based entities that match the response schema output type.
|
|
31
|
+
*/
|
|
32
|
+
type InferResponseType<T> = T extends {
|
|
33
|
+
200: infer TSchema;
|
|
34
|
+
} ? TSchema extends ValibotSchema ? InferOutput<TSchema> : unknown : T extends {
|
|
35
|
+
201: infer TSchema;
|
|
36
|
+
} ? TSchema extends ValibotSchema ? InferOutput<TSchema> : unknown : unknown;
|
|
37
|
+
/**
|
|
38
|
+
* HTTP status codes with descriptions
|
|
39
|
+
*/
|
|
40
|
+
declare const STATUS_DESCRIPTIONS: {
|
|
41
|
+
readonly 200: "Success";
|
|
42
|
+
readonly 201: "Created";
|
|
43
|
+
readonly 204: "No Content";
|
|
44
|
+
readonly 400: "Bad Request";
|
|
45
|
+
readonly 401: "Unauthorized";
|
|
46
|
+
readonly 403: "Forbidden";
|
|
47
|
+
readonly 404: "Not Found";
|
|
48
|
+
readonly 409: "Conflict";
|
|
49
|
+
readonly 422: "Unprocessable Entity";
|
|
50
|
+
readonly 429: "Too Many Requests";
|
|
51
|
+
readonly 500: "Internal Server Error";
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Route factory function type.
|
|
55
|
+
* Receives a real Hono instance and services from DI container.
|
|
56
|
+
*/
|
|
57
|
+
export type RouteFactory = (router: Hono, services: Record<string, unknown>) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Create routes with typed service injection.
|
|
60
|
+
*
|
|
61
|
+
* @template TServices - The shape of services required by these routes
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* interface AuthRoutesServices {
|
|
66
|
+
* authService: AuthService;
|
|
67
|
+
* userService: UserService;
|
|
68
|
+
* }
|
|
69
|
+
*
|
|
70
|
+
* export const authRoutes = createRoutes<AuthRoutesServices>((router, services) => {
|
|
71
|
+
* // services.authService is typed as AuthService!
|
|
72
|
+
* // services.userService is typed as UserService!
|
|
73
|
+
* router.post('/login', ...);
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function createRoutes<TServices = Record<string, unknown>>(factory: (router: Hono, services: TServices) => void): RouteFactory;
|
|
78
|
+
/**
|
|
79
|
+
* Route configuration with automatic type inference from Valibot schemas
|
|
80
|
+
*
|
|
81
|
+
* @template TBody - The body schema (automatically inferred)
|
|
82
|
+
* @template TResponses - The responses object (automatically inferred)
|
|
83
|
+
*/
|
|
84
|
+
export interface RouteConfig<TBody extends ValibotSchema | undefined = undefined, TResponses extends Partial<Record<keyof typeof STATUS_DESCRIPTIONS, ValibotSchema | undefined>> = Record<never, never>, TPublic extends boolean = false> {
|
|
85
|
+
tags?: string[];
|
|
86
|
+
summary?: string;
|
|
87
|
+
description?: string;
|
|
88
|
+
operationId?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Whether this is a public route (no authentication required)
|
|
91
|
+
* - false (default): Session is required and typed as non-nullable
|
|
92
|
+
* - true: Session is optional and typed as nullable
|
|
93
|
+
*/
|
|
94
|
+
public?: TPublic;
|
|
95
|
+
body?: TBody;
|
|
96
|
+
query?: ValibotSchema;
|
|
97
|
+
params?: ValibotSchema;
|
|
98
|
+
responses?: TResponses;
|
|
99
|
+
openapi?: Partial<Omit<DescribeRouteOptions, 'responses' | 'operationId' | 'tags' | 'summary' | 'description'>>;
|
|
100
|
+
/**
|
|
101
|
+
* Custom middleware to apply before validation and handler
|
|
102
|
+
*/
|
|
103
|
+
middleware?: MiddlewareHandler[];
|
|
104
|
+
/**
|
|
105
|
+
* Enable pagination - adds pagination query params and response headers to OpenAPI spec
|
|
106
|
+
*/
|
|
107
|
+
paginate?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Route handler - body and return types are automatically inferred from schemas
|
|
110
|
+
* Session is required by default (unless public: true)
|
|
111
|
+
*
|
|
112
|
+
* Prisma client extension converts Date → string at the database layer,
|
|
113
|
+
* so handler return types match the response schema output type.
|
|
114
|
+
*/
|
|
115
|
+
handler: (context: RouteContext<TBody extends ValibotSchema ? InferSchemaType<TBody> : never, TPublic extends true ? false : true>) => Promise<InferResponseType<TResponses>> | InferResponseType<TResponses>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Route handler context with typed body
|
|
119
|
+
*
|
|
120
|
+
* Leverages Hono's ContextVariableMap for session typing.
|
|
121
|
+
* If your middleware extends ContextVariableMap (e.g., auth middleware),
|
|
122
|
+
* the session will be automatically typed in route handlers.
|
|
123
|
+
*
|
|
124
|
+
* @template TBody - The typed body (inferred from schema)
|
|
125
|
+
* @template TSessionRequired - Whether session is guaranteed to exist (true by default, false for public routes)
|
|
126
|
+
*/
|
|
127
|
+
export interface RouteContext<TBody = never, TSessionRequired extends boolean = true> {
|
|
128
|
+
body: TBody;
|
|
129
|
+
query: Record<string, string>;
|
|
130
|
+
params: Record<string, string>;
|
|
131
|
+
services: Record<string, unknown>;
|
|
132
|
+
/**
|
|
133
|
+
* Session from context variables (typed via Hono's ContextVariableMap)
|
|
134
|
+
* - Required by default (for authenticated routes)
|
|
135
|
+
* - Optional only for public routes (public: true)
|
|
136
|
+
*/
|
|
137
|
+
session: TSessionRequired extends true ? NonNullable<Context['var']['session']> : Context['var']['session'];
|
|
138
|
+
/**
|
|
139
|
+
* Client IP address (extracted from headers and connection info)
|
|
140
|
+
*/
|
|
141
|
+
ip: string;
|
|
142
|
+
/**
|
|
143
|
+
* User agent string from request headers
|
|
144
|
+
*/
|
|
145
|
+
userAgent: string | undefined;
|
|
146
|
+
/**
|
|
147
|
+
* Original Hono context (for advanced use cases)
|
|
148
|
+
*/
|
|
149
|
+
context: Context;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Create a route handler with validation and OpenAPI metadata.
|
|
153
|
+
*
|
|
154
|
+
* Type inference works automatically:
|
|
155
|
+
* - Body type is inferred from the `body` schema
|
|
156
|
+
* - Return type is inferred from the `responses[200]` schema
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* route({
|
|
161
|
+
* body: StartSessionDto,
|
|
162
|
+
* responses: { 200: StartSessionResponseDto },
|
|
163
|
+
* handler: async ({ body }) => {
|
|
164
|
+
* // body is typed as InferOutput<typeof StartSessionDto>
|
|
165
|
+
* return { email: body.email }; // Must match StartSessionResponseDto
|
|
166
|
+
* }
|
|
167
|
+
* })
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
export declare function route<TBody extends ValibotSchema | undefined = undefined, TResponses extends Partial<Record<keyof typeof STATUS_DESCRIPTIONS, ValibotSchema | undefined>> = Record<never, never>, TPublic extends boolean = false>(config: RouteConfig<TBody, TResponses, TPublic>): MiddlewareHandler[];
|
|
171
|
+
export {};
|
|
172
|
+
//# sourceMappingURL=route-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-helpers.d.ts","sourceRoot":"","sources":["../src/route-helpers.ts"],"names":[],"mappings":"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"}
|