@ysk-kit/api-http 1.0.1 → 1.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/README.md +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/rate-limit.d.ts +17 -2
- package/dist/rate-limit.d.ts.map +1 -1
- package/dist/rate-limit.js +30 -0
- package/dist/rate-limit.js.map +1 -1
- package/package.json +14 -6
package/README.md
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { type EnvelopeErrorJson, envelopeError } from './envelope-error';
|
|
2
2
|
export { type ContractRoute, type ContractRouter, flattenContract, type HttpMethod, } from './flatten';
|
|
3
3
|
export { buildOpenApiDocument, type OpenApiDocument, scalarDocsHtml, } from './openapi';
|
|
4
|
-
export { clientIp, createMemoryRateLimit, isRateLimitSkipped, type RateLimitOpts, } from './rate-limit';
|
|
4
|
+
export { clientIp, createMemoryRateLimit, createRateLimit, createRedisRateLimit, isRateLimitSkipped, type RateLimiter, type RateLimitOpts, type RateLimitRedis, } from './rate-limit';
|
|
5
5
|
export { REQUEST_ID_HEADER } from './request-id';
|
|
6
6
|
export { applySecurityHeaders, securityHeaders } from './security-headers';
|
|
7
7
|
export { type HttpCtx, type HttpHandler, type HttpResult, headerValue } from './types';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,eAAe,EACf,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,oBAAoB,EACpB,KAAK,eAAe,EACpB,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,QAAQ,EACR,qBAAqB,EACrB,kBAAkB,EAClB,KAAK,aAAa,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,eAAe,EACf,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,oBAAoB,EACpB,KAAK,eAAe,EACpB,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,QAAQ,EACR,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { envelopeError } from './envelope-error';
|
|
2
2
|
export { flattenContract, } from './flatten';
|
|
3
3
|
export { buildOpenApiDocument, scalarDocsHtml, } from './openapi';
|
|
4
|
-
export { clientIp, createMemoryRateLimit, isRateLimitSkipped, } from './rate-limit';
|
|
4
|
+
export { clientIp, createMemoryRateLimit, createRateLimit, createRedisRateLimit, isRateLimitSkipped, } from './rate-limit';
|
|
5
5
|
export { REQUEST_ID_HEADER } from './request-id';
|
|
6
6
|
export { applySecurityHeaders, securityHeaders } from './security-headers';
|
|
7
7
|
export { headerValue } from './types';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAGL,eAAe,GAEhB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,oBAAoB,EAEpB,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,QAAQ,EACR,qBAAqB,EACrB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAGL,eAAe,GAEhB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,oBAAoB,EAEpB,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,QAAQ,EACR,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,GAInB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAmD,WAAW,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/rate-limit.d.ts
CHANGED
|
@@ -5,7 +5,22 @@ export type RateLimitOpts = {
|
|
|
5
5
|
};
|
|
6
6
|
export declare const clientIp: (forwarded: string | string[] | undefined, remote: string | undefined) => string;
|
|
7
7
|
export declare const isRateLimitSkipped: (path: string) => boolean;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/** Minimal Redis surface so the HTTP package does not import ioredis. */
|
|
9
|
+
export type RateLimitRedis = {
|
|
10
|
+
incr(key: string): Promise<number>;
|
|
11
|
+
pexpire(key: string, milliseconds: number): Promise<unknown>;
|
|
10
12
|
};
|
|
13
|
+
export type RateLimiter = {
|
|
14
|
+
check(ip: string, path: string): void | Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
export declare const createMemoryRateLimit: (opts: RateLimitOpts) => RateLimiter;
|
|
17
|
+
/** Fixed window shared across API processes. Used when `REDIS_URL` is set. */
|
|
18
|
+
export declare const createRedisRateLimit: (opts: RateLimitOpts & {
|
|
19
|
+
redis: RateLimitRedis;
|
|
20
|
+
now?: () => number;
|
|
21
|
+
prefix?: string;
|
|
22
|
+
}) => RateLimiter;
|
|
23
|
+
export declare const createRateLimit: (opts: RateLimitOpts & {
|
|
24
|
+
redis?: RateLimitRedis;
|
|
25
|
+
}) => RateLimiter;
|
|
11
26
|
//# sourceMappingURL=rate-limit.d.ts.map
|
package/dist/rate-limit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACtC,CAAC;AAEF,eAAO,MAAM,QAAQ,GACnB,WAAW,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EACxC,QAAQ,MAAM,GAAG,SAAS,KACzB,MAKF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OACc,CAAC;AAEjE,eAAO,MAAM,qBAAqB,GAAI,MAAM,aAAa;
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACtC,CAAC;AAEF,eAAO,MAAM,QAAQ,GACnB,WAAW,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EACxC,QAAQ,MAAM,GAAG,SAAS,KACzB,MAKF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OACc,CAAC;AAEjE,yEAAyE;AACzE,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,MAAM,aAAa,KAAG,WAkB3D,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,GAC/B,MAAM,aAAa,GAAG;IAAE,KAAK,EAAE,cAAc,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,KACnF,WAcF,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,MAAM,aAAa,GAAG;IAAE,KAAK,CAAC,EAAE,cAAc,CAAA;CAAE,KAAG,WAUlF,CAAC"}
|
package/dist/rate-limit.js
CHANGED
|
@@ -26,4 +26,34 @@ export const createMemoryRateLimit = (opts) => {
|
|
|
26
26
|
},
|
|
27
27
|
};
|
|
28
28
|
};
|
|
29
|
+
/** Fixed window shared across API processes. Used when `REDIS_URL` is set. */
|
|
30
|
+
export const createRedisRateLimit = (opts) => {
|
|
31
|
+
const skip = opts.skipPath ?? isRateLimitSkipped;
|
|
32
|
+
const prefix = opts.prefix ?? 'ysk-rl';
|
|
33
|
+
return {
|
|
34
|
+
async check(ip, path) {
|
|
35
|
+
if (skip(path))
|
|
36
|
+
return;
|
|
37
|
+
const now = (opts.now ?? Date.now)();
|
|
38
|
+
const bucket = Math.floor(now / opts.windowMs);
|
|
39
|
+
const key = `${prefix}:${ip}:${bucket}`;
|
|
40
|
+
const count = await opts.redis.incr(key);
|
|
41
|
+
if (count === 1)
|
|
42
|
+
await opts.redis.pexpire(key, opts.windowMs);
|
|
43
|
+
if (count > opts.max)
|
|
44
|
+
throw new AppError('RATE_LIMITED');
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export const createRateLimit = (opts) => {
|
|
49
|
+
if (opts.redis) {
|
|
50
|
+
return createRedisRateLimit({
|
|
51
|
+
windowMs: opts.windowMs,
|
|
52
|
+
max: opts.max,
|
|
53
|
+
redis: opts.redis,
|
|
54
|
+
...(opts.skipPath ? { skipPath: opts.skipPath } : {}),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return createMemoryRateLimit(opts);
|
|
58
|
+
};
|
|
29
59
|
//# sourceMappingURL=rate-limit.js.map
|
package/dist/rate-limit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAQlD,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,SAAwC,EACxC,MAA0B,EAClB,EAAE;IACV,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,MAAM,KAAK,GAAG,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IACzC,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,MAAM,IAAI,SAAS,CAAC;AAC7B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAY,EAAW,EAAE,CAC1D,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ysk-kit/api-http",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -11,15 +11,15 @@
|
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@ysk-kit/auth": "1.0
|
|
15
|
-
"@ysk-kit/contracts": "1.0
|
|
16
|
-
"@ysk-kit/domain-kernel": "1.0
|
|
14
|
+
"@ysk-kit/auth": "1.1.0",
|
|
15
|
+
"@ysk-kit/contracts": "1.1.0",
|
|
16
|
+
"@ysk-kit/domain-kernel": "1.1.0",
|
|
17
17
|
"zod": "^4.6.5"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@types/node": "^24.
|
|
20
|
+
"@types/node": "^24.19.0",
|
|
21
21
|
"typescript": "^6.0.3",
|
|
22
|
-
"vitest": "^5.0.
|
|
22
|
+
"vitest": "^5.0.3"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"dist"
|
|
@@ -28,6 +28,14 @@
|
|
|
28
28
|
"registry": "https://registry.npmjs.org",
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"description": "Framework-free HTTP handlers, OpenAPI, and rate limits for YSK Kit.",
|
|
33
|
+
"homepage": "https://ysk.hk/products/ysk-kit",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/yanshekki/ysk-kit.git",
|
|
37
|
+
"directory": "packages/api-http"
|
|
38
|
+
},
|
|
31
39
|
"scripts": {
|
|
32
40
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
33
41
|
"test": "vitest run --passWithNoTests",
|