@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 ADDED
@@ -0,0 +1,7 @@
1
+ # @ysk-kit/api-http
2
+
3
+ Framework-free HTTP handlers, OpenAPI, and rate limits for YSK Kit.
4
+
5
+ - Homepage: https://ysk.hk/products/ysk-kit
6
+ - Source: https://github.com/yanshekki/ysk-kit/tree/main/packages/api-http
7
+ - License: MIT
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';
@@ -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,GACnB,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"}
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,GAEnB,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"}
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"}
@@ -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
- export declare const createMemoryRateLimit: (opts: RateLimitOpts) => {
9
- check(ip: string, path: string): void;
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
@@ -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;cAK3C,MAAM,QAAQ,MAAM,GAAG,IAAI;CAaxC,CAAC"}
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"}
@@ -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
@@ -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;AAEjE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,IAAmB,EAAE,EAAE;IAC3D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,kBAAkB,CAAC;IAEjD,OAAO;QACL,KAAK,CAAC,EAAU,EAAE,IAAY;YAC5B,IAAI,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO;YACvB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,MAAM,WAAW,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;YACxC,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,WAAW,CAAC,CAAC;YACrE,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;gBACrB,MAAM,IAAI,QAAQ,CAAC,cAAc,CAAC,CAAC;YACrC,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC,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.1",
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.1",
15
- "@ysk-kit/contracts": "1.0.1",
16
- "@ysk-kit/domain-kernel": "1.0.1",
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.5.2",
20
+ "@types/node": "^24.19.0",
21
21
  "typescript": "^6.0.3",
22
- "vitest": "^5.0.2"
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",