@mrxsys/mrx-core 2.6.0-canary-20250805-40e4647 → 2.7.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +66 -4
  2. package/dist/chunk-4mt568fz.js +26 -0
  3. package/dist/{chunk-7m70tmz2.js → chunk-qb6x364m.js} +1 -1
  4. package/dist/chunk-snqdnkk7.js +10 -0
  5. package/dist/{chunk-64d6w5kt.js → chunk-tm71j126.js} +1 -1
  6. package/dist/chunk-yd82hdxv.js +6 -0
  7. package/dist/{chunk-9wk2pajt.js → chunk-z6q192p8.js} +7 -6
  8. package/dist/modules/database/index.js +2 -2
  9. package/dist/modules/elysia/crud/crud.d.ts +8 -8
  10. package/dist/modules/elysia/crud/index.js +3 -3
  11. package/dist/modules/elysia/crud/types/crudOptions.d.ts +1 -1
  12. package/dist/modules/elysia/dbResolver/index.js +3 -3
  13. package/dist/modules/elysia/rateLimit/enums/index.d.ts +1 -0
  14. package/dist/modules/elysia/rateLimit/enums/index.js +7 -0
  15. package/dist/modules/elysia/rateLimit/enums/rateLimitErrorKeys.d.ts +3 -0
  16. package/dist/modules/elysia/rateLimit/index.d.ts +1 -0
  17. package/dist/modules/elysia/rateLimit/index.js +139 -0
  18. package/dist/modules/elysia/{ratelimit/ratelimit.d.ts → rateLimit/rateLimit.d.ts} +5 -5
  19. package/dist/modules/elysia/rateLimit/stores/memoryStore.d.ts +47 -0
  20. package/dist/modules/elysia/rateLimit/types/memoryStoreEntry.d.ts +11 -0
  21. package/dist/modules/elysia/{ratelimit → rateLimit}/types/rateLimitOptions.d.ts +7 -4
  22. package/dist/modules/elysia/rateLimit/types/rateLimitStore.d.ts +21 -0
  23. package/dist/modules/repository/index.js +1 -1
  24. package/dist/modules/totp/enums/index.d.ts +1 -0
  25. package/dist/modules/totp/enums/index.js +7 -0
  26. package/dist/modules/totp/enums/totpErrorKeys.d.ts +7 -0
  27. package/dist/modules/totp/index.d.ts +1 -0
  28. package/dist/modules/totp/index.js +113 -0
  29. package/dist/modules/totp/totp.d.ts +63 -0
  30. package/dist/modules/totp/types/index.d.ts +3 -0
  31. package/dist/modules/totp/types/index.js +1 -0
  32. package/dist/modules/totp/types/otpAuthUri.d.ts +35 -0
  33. package/dist/modules/totp/types/totpOptions.d.ts +23 -0
  34. package/dist/modules/totp/types/verifyOptions.d.ts +18 -0
  35. package/dist/modules/totp/utils/base32.d.ts +19 -0
  36. package/dist/modules/totp/utils/createCounterBuffer.d.ts +8 -0
  37. package/dist/modules/totp/utils/dynamicTruncation.d.ts +9 -0
  38. package/dist/modules/totp/utils/generateHmac.d.ts +9 -0
  39. package/dist/modules/totp/utils/generateSecretBytes.d.ts +10 -0
  40. package/dist/modules/totp/utils/index.d.ts +5 -0
  41. package/dist/modules/totp/utils/index.js +75 -0
  42. package/dist/utils/types/index.d.ts +1 -0
  43. package/dist/utils/types/renameKey.d.ts +10 -0
  44. package/package.json +15 -11
  45. package/dist/chunk-twaga0fp.js +0 -6
  46. package/dist/modules/elysia/ratelimit/enums/index.d.ts +0 -1
  47. package/dist/modules/elysia/ratelimit/enums/index.js +0 -7
  48. package/dist/modules/elysia/ratelimit/enums/ratelimitErrorKeys.d.ts +0 -3
  49. package/dist/modules/elysia/ratelimit/index.d.ts +0 -1
  50. package/dist/modules/elysia/ratelimit/index.js +0 -52
  51. /package/dist/modules/elysia/{ratelimit → rateLimit}/types/index.d.ts +0 -0
  52. /package/dist/modules/elysia/{ratelimit → rateLimit}/types/index.js +0 -0
package/CHANGELOG.md CHANGED
@@ -1,17 +1,79 @@
1
1
 
2
- ## v2.6.0-canary-20250805-40e4647
2
+ ## v2.7.0
3
3
 
4
- [compare changes](https://github.com/MRX-Systems/MRX-Core/compare/v2.5.1...v2.6.0-canary-20250805-40e4647)
4
+ [compare changes](https://github.com/MRX-Systems/MRX-Core/compare/v2.6.0-canary-20250805-40e4647...v2.7.0)
5
5
 
6
6
  ### 🚀 Enhancements
7
7
 
8
- - **🚀:** [add enums exports for various modules] ## Features - Added enums exports for error handling and data management modules. ([be66af33](https://github.com/MRX-Systems/MRX-Core/commit/be66af33))
8
+ - **🚀:** AND-228 fix throwIfNoResult custom message ([a1228463](https://github.com/MRX-Systems/MRX-Core/commit/a1228463))
9
+ - **🚀:** [add Base32 encoding and decoding utilities] ([e64b06cd](https://github.com/MRX-Systems/MRX-Core/commit/e64b06cd))
10
+ - **🚀:** [add createCounterBuffer utility function] ([29af86e4](https://github.com/MRX-Systems/MRX-Core/commit/29af86e4))
11
+ - **🚀:** [add generateHmac utility function] ([30142a1e](https://github.com/MRX-Systems/MRX-Core/commit/30142a1e))
12
+ - **🚀:** [add unit tests for generateHmac utility function] ([9ff1643a](https://github.com/MRX-Systems/MRX-Core/commit/9ff1643a))
13
+ - **🚀:** [add TOTP error keys and generateSecretBytes utility function] ([527008ea](https://github.com/MRX-Systems/MRX-Core/commit/527008ea))
14
+ - **🚀:** [add dynamic truncation utility function for HMAC results] ([31c261b0](https://github.com/MRX-Systems/MRX-Core/commit/31c261b0))
15
+ - **🚀:** [add utility functions exports for TOTP module] ## Features - Exported utility functions: base32Decode, base32Encode, createCounterBuffer, generateSecretBytes, generateHmac, and dynamicTruncation. ([c92359bd](https://github.com/MRX-Systems/MRX-Core/commit/c92359bd))
16
+ - **🚀:** [implement TOTP and HOTP functionalities] ([9a9a3983](https://github.com/MRX-Systems/MRX-Core/commit/9a9a3983))
17
+ - **🚀:** [add TOTP module exports for better accessibility] ([aec89c93](https://github.com/MRX-Systems/MRX-Core/commit/aec89c93))
18
+ - **🚀:** [add TOTP_ERROR_KEYS export for error handling] ([73e361e3](https://github.com/MRX-Systems/MRX-Core/commit/73e361e3))
19
+ - **🚀:** [Add OtpAuth URI type and refactor related code] ([2c2d2b88](https://github.com/MRX-Systems/MRX-Core/commit/2c2d2b88))
20
+ - **🚀:** [refactor rate limiting implementation and add MemoryStore] ([158b7ddf](https://github.com/MRX-Systems/MRX-Core/commit/158b7ddf))
21
+
22
+ ### 🔧 Fixes
23
+
24
+ - **🔧:** [Improve error message handling for no results] ([76c297fa](https://github.com/MRX-Systems/MRX-Core/commit/76c297fa))
25
+ - **🔧:** [Fix review] ([87c68deb](https://github.com/MRX-Systems/MRX-Core/commit/87c68deb))
26
+
27
+ ### 🧹 Refactors
28
+
29
+ - **🧹:** [Change in tenary expression] ([4b4c4864](https://github.com/MRX-Systems/MRX-Core/commit/4b4c4864))
30
+ - **🧹:** [Update orderBy syntax for query builder] - Refactored the orderBy method to include table name in the query. - Ensured that selected fields are properly referenced with the table name for better clarity and to avoid potential conflicts. ([53eb355e](https://github.com/MRX-Systems/MRX-Core/commit/53eb355e))
31
+ - **🧹:** [Refactor query returning and selection logic] ([a6b46fb2](https://github.com/MRX-Systems/MRX-Core/commit/a6b46fb2))
32
+ - **🧹:** [Simplify query returning and selection logic] ([b6c04169](https://github.com/MRX-Systems/MRX-Core/commit/b6c04169))
33
+ - **🧹:** [Refactor HOTP options parameter initialization] ## Refactoring - Updated the HOTP function's options parameter to provide a default value. ([3937ae22](https://github.com/MRX-Systems/MRX-Core/commit/3937ae22))
34
+ - **🧹:** [correct export name for rate limiting module] ([cbb3e9f7](https://github.com/MRX-Systems/MRX-Core/commit/cbb3e9f7))
35
+ - **🧹:** [standardize rate limit module exports and structure] ## Refactoring - Corrected export names in the rate limiting module for consistency. - Removed outdated `ratelimitErrorKeys` file and integrated its functionality into the main rate limiting logic. - Updated test cases to reflect the new export structure and ensure proper functionality. - Introduced a new `rateLimit` function with comprehensive documentation. ([f192bb39](https://github.com/MRX-Systems/MRX-Core/commit/f192bb39))
36
+
37
+ ### 📖 Documentation
38
+
39
+ - **📖:** [Update documentation for RenameKey type utility] ([379a2403](https://github.com/MRX-Systems/MRX-Core/commit/379a2403))
40
+
41
+ ### 📦 Build
42
+
43
+ - **📦:** [update devDependencies to latest versions] Updated @eslint/js, @stylistic/eslint-plugin, @types/bun, eslint, and typescript-eslint to their latest versions for improved functionality and compatibility. ([1c40c8e7](https://github.com/MRX-Systems/MRX-Core/commit/1c40c8e7))
44
+ - **📦:** [add TOTP module paths to builder and package.json] ([d5ad0b26](https://github.com/MRX-Systems/MRX-Core/commit/d5ad0b26))
45
+
46
+ ### 🌊 Types
47
+
48
+ - **🌊:** [fix TOperations with default CrudOperationsOptions] ([b8b6169d](https://github.com/MRX-Systems/MRX-Core/commit/b8b6169d))
49
+ - **🌊:** [add RenameKey type utility for key renaming in objects] ([0d661646](https://github.com/MRX-Systems/MRX-Core/commit/0d661646))
50
+ - **🌊:** [Add RenameKey type to type definitions] ## Type Changes - Added `RenameKey` type to the type definitions in `index.ts`. ([e389bcba](https://github.com/MRX-Systems/MRX-Core/commit/e389bcba))
51
+ - **🌊:** [add type definitions for TOTP module] ([03917acc](https://github.com/MRX-Systems/MRX-Core/commit/03917acc))
52
+ - **🌊:** [Update OtpAuthUri interface for optional properties] ## Type Changes - Made `algorithm`, `digits`, and `period` properties optional in the `OtpAuthUri` interface. ([d1d47681](https://github.com/MRX-Systems/MRX-Core/commit/d1d47681))
9
53
 
10
54
  ### 🦉 Chore
11
55
 
12
- - **🦉:** [Clean CHANGELOG] ([f291cbbd](https://github.com/MRX-Systems/MRX-Core/commit/f291cbbd))
56
+ - **🦉:** V2.6.0 ([3a016766](https://github.com/MRX-Systems/MRX-Core/commit/3a016766))
57
+ - **🦉:** [clean up CHANGELOG.md for better readability] ([ab70e0fc](https://github.com/MRX-Systems/MRX-Core/commit/ab70e0fc))
58
+
59
+ ### 🧪 Tests
60
+
61
+ - **🧪:** [Update error message for no result case] ## Tests - Changed the error message for the no result case in the repository tests. ([0f71ce8f](https://github.com/MRX-Systems/MRX-Core/commit/0f71ce8f))
62
+ - **🧪:** [add unit tests for Base32 encoding and decoding] ([2b25e11d](https://github.com/MRX-Systems/MRX-Core/commit/2b25e11d))
63
+ - **🧪:** [add unit tests for createCounterBuffer function] ([71153e42](https://github.com/MRX-Systems/MRX-Core/commit/71153e42))
64
+ - **🧪:** [add unit tests for generateSecretBytes utility function] ([bb33cd78](https://github.com/MRX-Systems/MRX-Core/commit/bb33cd78))
65
+ - **🧪:** [add unit tests for dynamic truncation utility function] ([ba6fee56](https://github.com/MRX-Systems/MRX-Core/commit/ba6fee56))
66
+ - **🧪:** [add comprehensive unit tests for TOTP functionalities] ([82d5f4d1](https://github.com/MRX-Systems/MRX-Core/commit/82d5f4d1))
67
+ - **🧪:** [add comprehensive tests for MemoryStore functionality] ([d1364340](https://github.com/MRX-Systems/MRX-Core/commit/d1364340))
68
+ - **🧪:** Enhance rate limiting tests for Redis and memory stores ([2df081e1](https://github.com/MRX-Systems/MRX-Core/commit/2df081e1))
69
+
70
+ ### 🤖 CI
71
+
72
+ - **🤖:** [Add CI workflows for build, test, and deployment] ([7d98d68b](https://github.com/MRX-Systems/MRX-Core/commit/7d98d68b))
73
+ - **🤖:** [update copilot instructions] ([a8540b78](https://github.com/MRX-Systems/MRX-Core/commit/a8540b78))
13
74
 
14
75
  ### ❤️ Contributors
15
76
 
16
77
  - Ruby <necrelox@proton.me>
78
+ - Github-actions <maxime.meriaux@mrxsys.com>
17
79
 
@@ -0,0 +1,26 @@
1
+ // source/modules/totp/utils/createCounterBuffer.ts
2
+ var createCounterBuffer = (counter) => {
3
+ const counterBuffer = new ArrayBuffer(8);
4
+ const counterView = new DataView(counterBuffer);
5
+ if (typeof counter === "bigint")
6
+ counterView.setBigUint64(0, counter, false);
7
+ else
8
+ counterView.setUint32(4, counter, false);
9
+ return counterBuffer;
10
+ };
11
+
12
+ // source/modules/totp/utils/dynamicTruncation.ts
13
+ var dynamicTruncation = (hmacArray, digits) => {
14
+ const offset = hmacArray[hmacArray.length - 1] & 15;
15
+ const code = ((hmacArray[offset] & 127) << 24 | (hmacArray[offset + 1] & 255) << 16 | (hmacArray[offset + 2] & 255) << 8 | hmacArray[offset + 3] & 255) % 10 ** digits;
16
+ return code.toString().padStart(digits, "0");
17
+ };
18
+
19
+ // source/modules/totp/utils/generateHmac.ts
20
+ import { webcrypto } from "crypto";
21
+ var generateHmac = async (key, data) => {
22
+ const hmac = await webcrypto.subtle.sign("HMAC", key, data);
23
+ return new Uint8Array(hmac);
24
+ };
25
+
26
+ export { createCounterBuffer, dynamicTruncation, generateHmac };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Repository
3
- } from "./chunk-9wk2pajt.js";
3
+ } from "./chunk-z6q192p8.js";
4
4
  import {
5
5
  TypedEventEmitter
6
6
  } from "./chunk-yvyahr2h.js";
@@ -0,0 +1,10 @@
1
+ // source/modules/totp/enums/totpErrorKeys.ts
2
+ var TOTP_ERROR_KEYS = {
3
+ INVALID_BASE32_CHARACTER: "totp.error.invalid_base32_character",
4
+ INVALID_SECRET_LENGTH: "totp.error.invalid_secret_length",
5
+ INVALID_ALGORITHM: "totp.error.invalid_algorithm",
6
+ INVALID_OTP_AUTH_URI: "totp.error.invalid_otp_auth_uri",
7
+ MISSING_SECRET: "totp.error.missing_secret"
8
+ };
9
+
10
+ export { TOTP_ERROR_KEYS };
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-dwfyt1m6.js";
7
7
  import {
8
8
  MSSQL
9
- } from "./chunk-7m70tmz2.js";
9
+ } from "./chunk-qb6x364m.js";
10
10
  import {
11
11
  HttpError
12
12
  } from "./chunk-683sda6e.js";
@@ -0,0 +1,6 @@
1
+ // source/modules/elysia/rateLimit/enums/rateLimitErrorKeys.ts
2
+ var RATE_LIMIT_ERROR_KEYS = {
3
+ RATE_LIMIT_EXCEEDED: "elysia.rateLimit.error.exceeded"
4
+ };
5
+
6
+ export { RATE_LIMIT_ERROR_KEYS };
@@ -124,10 +124,11 @@ class Repository {
124
124
  }
125
125
  _applySelectedFields(query, selectedFields) {
126
126
  const qMethod = query._method;
127
+ const sanitizedFields = selectedFields ? Array.isArray(selectedFields) ? selectedFields.map((selectedField) => `${selectedField} as ${selectedField}`) : `${selectedFields} as ${selectedFields}` : "*";
127
128
  if (qMethod === "del" || qMethod === "update" || qMethod === "insert")
128
- query.returning(selectedFields ?? "*");
129
+ query.returning(sanitizedFields);
129
130
  else
130
- query.select(selectedFields ?? "*");
131
+ query.select(sanitizedFields);
131
132
  }
132
133
  _applyFilter(query, search) {
133
134
  const processing = (query2, search2) => {
@@ -166,13 +167,13 @@ class Repository {
166
167
  if (!(qMethod === "select"))
167
168
  return;
168
169
  if (!orderBy)
169
- query.orderBy(this._table.primaryKey[0], "asc");
170
+ query.orderBy(`[${this._table.name}].${this._table.primaryKey[0]}`, "asc");
170
171
  else if (Array.isArray(orderBy))
171
172
  orderBy.forEach((item) => {
172
- query.orderBy(item.selectedField, item.direction);
173
+ query.orderBy(`[${this._table.name}].${item.selectedField}`, item.direction);
173
174
  });
174
175
  else
175
- query.orderBy(orderBy.selectedField, orderBy.direction);
176
+ query.orderBy(`[${this._table.name}].${orderBy.selectedField}`, orderBy.direction);
176
177
  }
177
178
  _applyQueryOptions(query, options) {
178
179
  this._applyFilter(query, options?.filters);
@@ -201,7 +202,7 @@ class Repository {
201
202
  const result = await query;
202
203
  if (throwIfNoResult && result.length === 0)
203
204
  throw new HttpError({
204
- message: DATABASE_ERROR_KEYS.MSSQL_NO_RESULT,
205
+ message: typeof throwIfNoResult === "string" ? throwIfNoResult : DATABASE_ERROR_KEYS.MSSQL_NO_RESULT,
205
206
  cause: {
206
207
  query: query.toSQL().sql
207
208
  },
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  MSSQL,
4
4
  Table
5
- } from "../../chunk-7m70tmz2.js";
6
- import"../../chunk-9wk2pajt.js";
5
+ } from "../../chunk-qb6x364m.js";
6
+ import"../../chunk-z6q192p8.js";
7
7
  import"../../chunk-5qtpggzv.js";
8
8
  import"../../chunk-yvyahr2h.js";
9
9
  import"../../chunk-zaje5tv4.js";
@@ -14,14 +14,14 @@ import type { CrudOperationUpdateOneOptions } from './types/crudOperationUpdateO
14
14
  import type { CrudOperationUpdateOptions } from './types/crudOperationUpdateOptions';
15
15
  import type { CrudOptions } from './types/crudOptions';
16
16
  export declare const crud: <const TDatabase extends DynamicDbOptions | string, const TTableName extends string, const TSourceSchema extends TObject, const TOperations extends CrudOperationsOptions = {
17
- find: true;
18
- findOne: true;
19
- insert: true;
20
- update: true;
21
- updateOne: true;
22
- delete: true;
23
- deleteOne: true;
24
- count: true;
17
+ find: CrudOperationFindOptions | true;
18
+ findOne: CrudOperationFindOneOptions | true;
19
+ insert: CrudOperationInsertOptions | true;
20
+ update: CrudOperationUpdateOptions | true;
21
+ updateOne: CrudOperationUpdateOneOptions | true;
22
+ delete: CrudOperationDeleteOptions | true;
23
+ deleteOne: CrudOperationDeleteOneOptions | true;
24
+ count: CrudOperationCountOptions | true;
25
25
  }, const TSourceFindSchema extends TObject = TSourceSchema, const TSourceCountSchema extends TObject = TSourceSchema, const TSourceInsertSchema extends TObject = TSourceSchema, const TSourceUpdateSchema extends TObject = TSourceSchema, const TSourceDeleteSchema extends TObject = TSourceSchema, const TSourceResponseSchema extends TObject = TSourceSchema>({ database, tableName, schema, operations }: CrudOptions<TDatabase, TTableName, TSourceSchema, TOperations, TSourceFindSchema, TSourceCountSchema, TSourceInsertSchema, TSourceUpdateSchema, TSourceDeleteSchema, TSourceResponseSchema>) => Elysia<TTableName, {
26
26
  decorator: SingletonBase["decorator"];
27
27
  store: SingletonBase["store"];
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  dbResolver
4
- } from "../../../chunk-64d6w5kt.js";
4
+ } from "../../../chunk-tm71j126.js";
5
5
  import"../../../chunk-7jsxw1ts.js";
6
6
  import"../../../chunk-hcc6g1fd.js";
7
7
  import"../../../chunk-dwfyt1m6.js";
@@ -11,8 +11,8 @@ import {
11
11
  import"../../../chunk-26brdrbz.js";
12
12
  import"../../../chunk-4n72ks5x.js";
13
13
  import"../../../chunk-j28jpfv2.js";
14
- import"../../../chunk-7m70tmz2.js";
15
- import"../../../chunk-9wk2pajt.js";
14
+ import"../../../chunk-qb6x364m.js";
15
+ import"../../../chunk-z6q192p8.js";
16
16
  import"../../../chunk-5qtpggzv.js";
17
17
  import"../../../chunk-yvyahr2h.js";
18
18
  import"../../../chunk-zaje5tv4.js";
@@ -7,7 +7,7 @@ import type { CrudOperationsOptions } from './crudOperationsOptions';
7
7
  * @template TSourceSchema - The type of the object to be used in the CRUD operations extending {@link TObject}
8
8
  * @template KEnumPermission - The type of the enum for permissions extending {@link String}
9
9
  */
10
- export interface CrudOptions<TDatabase extends DynamicDbOptions | string, TTableName extends string, TSourceSchema extends TObject, TOperations extends CrudOperationsOptions = CrudOperationsOptions, TSourceFindSchema extends TObject = TSourceSchema, TSourceCountSchema extends TObject = TSourceSchema, TSourceInsertSchema extends TObject = TSourceSchema, TSourceUpdateSchema extends TObject = TSourceSchema, TSourceDeleteSchema extends TObject = TSourceSchema, TSourceResponseSchema extends TObject = TSourceSchema> {
10
+ export interface CrudOptions<TDatabase extends DynamicDbOptions | string, TTableName extends string, TSourceSchema extends TObject, TOperations extends CrudOperationsOptions, TSourceFindSchema extends TObject = TSourceSchema, TSourceCountSchema extends TObject = TSourceSchema, TSourceInsertSchema extends TObject = TSourceSchema, TSourceUpdateSchema extends TObject = TSourceSchema, TSourceDeleteSchema extends TObject = TSourceSchema, TSourceResponseSchema extends TObject = TSourceSchema> {
11
11
  readonly database: TDatabase;
12
12
  readonly tableName: TTableName;
13
13
  readonly schema: {
@@ -1,12 +1,12 @@
1
1
  // @bun
2
2
  import {
3
3
  dbResolver
4
- } from "../../../chunk-64d6w5kt.js";
4
+ } from "../../../chunk-tm71j126.js";
5
5
  import"../../../chunk-7jsxw1ts.js";
6
6
  import"../../../chunk-hcc6g1fd.js";
7
7
  import"../../../chunk-dwfyt1m6.js";
8
- import"../../../chunk-7m70tmz2.js";
9
- import"../../../chunk-9wk2pajt.js";
8
+ import"../../../chunk-qb6x364m.js";
9
+ import"../../../chunk-z6q192p8.js";
10
10
  import"../../../chunk-5qtpggzv.js";
11
11
  import"../../../chunk-yvyahr2h.js";
12
12
  import"../../../chunk-zaje5tv4.js";
@@ -0,0 +1 @@
1
+ export { RATE_LIMIT_ERROR_KEYS } from './rateLimitErrorKeys';
@@ -0,0 +1,7 @@
1
+ // @bun
2
+ import {
3
+ RATE_LIMIT_ERROR_KEYS
4
+ } from "../../../../chunk-yd82hdxv.js";
5
+ export {
6
+ RATE_LIMIT_ERROR_KEYS
7
+ };
@@ -0,0 +1,3 @@
1
+ export declare const RATE_LIMIT_ERROR_KEYS: {
2
+ readonly RATE_LIMIT_EXCEEDED: "elysia.rateLimit.error.exceeded";
3
+ };
@@ -0,0 +1 @@
1
+ export { rateLimit } from './rateLimit';
@@ -0,0 +1,139 @@
1
+ // @bun
2
+ import {
3
+ RATE_LIMIT_ERROR_KEYS
4
+ } from "../../../chunk-yd82hdxv.js";
5
+ import {
6
+ HttpError
7
+ } from "../../../chunk-683sda6e.js";
8
+ import"../../../chunk-9nw6qekv.js";
9
+ import"../../../chunk-vknq69e0.js";
10
+
11
+ // source/modules/elysia/rateLimit/rateLimit.ts
12
+ import { Elysia } from "elysia";
13
+
14
+ // source/modules/elysia/rateLimit/stores/memoryStore.ts
15
+ class MemoryStore {
16
+ _store = new Map;
17
+ _cleanupInterval;
18
+ _cleanupTimer = null;
19
+ constructor(cleanupIntervalMs) {
20
+ this._cleanupInterval = cleanupIntervalMs ?? 300000;
21
+ this._startCleanup();
22
+ }
23
+ get(key) {
24
+ const entry = this._store.get(key);
25
+ if (!entry)
26
+ return null;
27
+ const now = Date.now();
28
+ if (now > entry.expiresAt && entry.expiresAt !== -1) {
29
+ this._store.delete(key);
30
+ return null;
31
+ }
32
+ return entry.value;
33
+ }
34
+ setex(key, seconds, value) {
35
+ if (seconds <= 0)
36
+ return;
37
+ const expiresAt = Date.now() + seconds * 1000;
38
+ this._store.set(key, {
39
+ value,
40
+ expiresAt
41
+ });
42
+ }
43
+ incr(key) {
44
+ const now = Date.now();
45
+ const entry = this._store.get(key);
46
+ if (!entry || now > entry.expiresAt && entry.expiresAt !== -1) {
47
+ if (entry)
48
+ this._store.delete(key);
49
+ this._store.set(key, {
50
+ value: "1",
51
+ expiresAt: -1
52
+ });
53
+ return 1;
54
+ }
55
+ const currentValue = parseInt(entry.value) || 0;
56
+ const newValue = currentValue + 1;
57
+ this._store.set(key, {
58
+ value: newValue.toString(),
59
+ expiresAt: entry.expiresAt
60
+ });
61
+ return newValue;
62
+ }
63
+ ttl(key) {
64
+ const entry = this._store.get(key);
65
+ if (!entry)
66
+ return -1;
67
+ if (entry.expiresAt === -1)
68
+ return -1;
69
+ const now = Date.now();
70
+ if (now > entry.expiresAt) {
71
+ this._store.delete(key);
72
+ return -1;
73
+ }
74
+ return Math.ceil((entry.expiresAt - now) / 1000);
75
+ }
76
+ _startCleanup() {
77
+ this._cleanupTimer = setInterval(() => {
78
+ this._cleanup();
79
+ }, this._cleanupInterval);
80
+ }
81
+ _cleanup() {
82
+ const now = Date.now();
83
+ for (const [key, entry] of this._store.entries())
84
+ if (now > entry.expiresAt && entry.expiresAt !== -1)
85
+ this._store.delete(key);
86
+ }
87
+ destroy() {
88
+ if (this._cleanupTimer) {
89
+ clearInterval(this._cleanupTimer);
90
+ this._cleanupTimer = null;
91
+ }
92
+ this._store.clear();
93
+ }
94
+ }
95
+
96
+ // source/modules/elysia/rateLimit/rateLimit.ts
97
+ var rateLimit = ({ store, limit, window }) => {
98
+ const storeInstance = store === ":memory:" || !store ? new MemoryStore : store;
99
+ return new Elysia({
100
+ name: "rateLimit",
101
+ seed: {
102
+ store,
103
+ limit,
104
+ window
105
+ }
106
+ }).onRequest(async ({ set, request, server }) => {
107
+ const ip = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || server?.requestIP(request)?.address || "127.0.0.1";
108
+ const key = `rateLimit:${ip}`;
109
+ const current = await storeInstance.get(key);
110
+ const count = current ? parseInt(current) : 0;
111
+ if (count === 0)
112
+ await storeInstance.setex(key, window, "1");
113
+ else
114
+ await storeInstance.incr(key);
115
+ const newCount = await storeInstance.get(key);
116
+ const currentCount = newCount ? parseInt(newCount) : 0;
117
+ if (currentCount > limit) {
118
+ set.status = 429;
119
+ throw new HttpError({
120
+ message: RATE_LIMIT_ERROR_KEYS.RATE_LIMIT_EXCEEDED,
121
+ httpStatusCode: "TOO_MANY_REQUESTS",
122
+ cause: {
123
+ limit,
124
+ window,
125
+ remaining: 0,
126
+ reset: await storeInstance.ttl(key)
127
+ }
128
+ });
129
+ }
130
+ set.headers = {
131
+ "X-RateLimit-Limit": limit.toString(),
132
+ "X-RateLimit-Remaining": Math.max(0, limit - currentCount).toString(),
133
+ "X-RateLimit-Reset": (await storeInstance.ttl(key)).toString()
134
+ };
135
+ }).as("global");
136
+ };
137
+ export {
138
+ rateLimit
139
+ };
@@ -27,7 +27,7 @@ import type { RateLimitOptions } from './types/rateLimitOptions';
27
27
  * // Create and configure the application with rate limiting
28
28
  * const app = new Elysia()
29
29
  * .use(rateLimit({
30
- * redis,
30
+ * store: redis,
31
31
  * limit: 100, // 100 requests
32
32
  * window: 60, // per minute
33
33
  * }))
@@ -39,7 +39,7 @@ import type { RateLimitOptions } from './types/rateLimitOptions';
39
39
  * app.listen(3000);
40
40
  * ```
41
41
  */
42
- export declare const rateLimit: ({ redis, limit, window }: RateLimitOptions) => Elysia<"", {
42
+ export declare const rateLimit: ({ store, limit, window }: RateLimitOptions) => Elysia<"", {
43
43
  decorator: {};
44
44
  store: {};
45
45
  derive: {};
@@ -48,7 +48,7 @@ export declare const rateLimit: ({ redis, limit, window }: RateLimitOptions) =>
48
48
  typebox: {};
49
49
  error: {};
50
50
  }, {
51
- schema: {};
51
+ schema: import("elysia").MergeSchema<import("elysia").MergeSchema<{}, {}, "">, {}, "">;
52
52
  standaloneSchema: {};
53
53
  macro: {};
54
54
  macroFn: {};
@@ -56,8 +56,8 @@ export declare const rateLimit: ({ redis, limit, window }: RateLimitOptions) =>
56
56
  }, {}, {
57
57
  derive: {};
58
58
  resolve: {};
59
- schema: import("elysia").MergeSchema<{}, {}, "">;
60
- standaloneSchema: import("elysia/types").PrettifySchema<{}>;
59
+ schema: {};
60
+ standaloneSchema: {};
61
61
  }, {
62
62
  derive: {};
63
63
  resolve: {};
@@ -0,0 +1,47 @@
1
+ import type { RateLimitStore } from '../../../../modules/elysia/rateLimit/types/rateLimitStore';
2
+ export declare class MemoryStore implements RateLimitStore {
3
+ /**
4
+ * Internal storage map.
5
+ */
6
+ private readonly _store;
7
+ /**
8
+ * Cleanup interval (5 minutes by default).
9
+ */
10
+ private readonly _cleanupInterval;
11
+ /**
12
+ * Timer for cleanup operations.
13
+ */
14
+ private _cleanupTimer;
15
+ /**
16
+ * Creates instance and starts cleanup process.
17
+ */
18
+ constructor(cleanupIntervalMs?: number);
19
+ /**
20
+ * Get current count for a key.
21
+ */
22
+ get(key: string): string | null;
23
+ /**
24
+ * Set key with expiration time.
25
+ */
26
+ setex(key: string, seconds: number, value: string): void;
27
+ /**
28
+ * Increment key value and return new count.
29
+ */
30
+ incr(key: string): number;
31
+ /**
32
+ * Get time to live for key in seconds.
33
+ */
34
+ ttl(key: string): number;
35
+ /**
36
+ * Start cleanup process.
37
+ */
38
+ private _startCleanup;
39
+ /**
40
+ * Clean up expired entries.
41
+ */
42
+ private _cleanup;
43
+ /**
44
+ * Stop cleanup and clear data.
45
+ */
46
+ destroy(): void;
47
+ }
@@ -0,0 +1,11 @@
1
+ export interface MemoryStoreEntry {
2
+ /**
3
+ * Current count value for the key.
4
+ */
5
+ readonly value: string;
6
+ /**
7
+ * Timestamp when this entry expires (in milliseconds).
8
+ * -1 means no expiration (like Redis behavior).
9
+ */
10
+ readonly expiresAt: number;
11
+ }
@@ -5,18 +5,21 @@ import type { Redis } from 'ioredis';
5
5
  * @example
6
6
  * ```ts
7
7
  * const options: RateLimitOptions = {
8
- * redis: redisInstance, // Your Redis instance
8
+ * store: redisInstance, // Your Redis instance
9
9
  * limit: 100, // Allow 100 requests
10
10
  * window: 60, // Per 60 seconds
11
- * message: 'You have exceeded the rate limit. Please try again later.'
12
11
  * };
13
12
  * ```
14
13
  */
15
14
  export interface RateLimitOptions {
16
15
  /**
17
- * Redis instance used for storing rate limit data.
16
+ * Storage backend for rate limit data.
17
+ *
18
+ * - If not specified, defaults to in-memory storage
19
+ * - Use ':memory:' to explicitly specify in-memory storage
20
+ * - Provide a Redis instance for persistent distributed storage
18
21
  */
19
- readonly redis: Redis;
22
+ readonly store?: ':memory:' | Redis;
20
23
  /**
21
24
  * Maximum number of requests allowed in the time window.
22
25
  *
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Common interface for rate limit storage backends (Memory, Redis, etc.).
3
+ */
4
+ export interface RateLimitStore {
5
+ /**
6
+ * Get value for a key.
7
+ */
8
+ get(key: string): string | null | Promise<string | null>;
9
+ /**
10
+ * Set key with expiration.
11
+ */
12
+ setex(key: string, seconds: number, value: string): void | Promise<void>;
13
+ /**
14
+ * Increment key value.
15
+ */
16
+ incr(key: string): number | Promise<number>;
17
+ /**
18
+ * Get time to live for key.
19
+ */
20
+ ttl(key: string): number | Promise<number>;
21
+ }
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  Repository
4
- } from "../../chunk-9wk2pajt.js";
4
+ } from "../../chunk-z6q192p8.js";
5
5
  import"../../chunk-5qtpggzv.js";
6
6
  import"../../chunk-zaje5tv4.js";
7
7
  import"../../chunk-683sda6e.js";
@@ -0,0 +1 @@
1
+ export { TOTP_ERROR_KEYS } from './totpErrorKeys';
@@ -0,0 +1,7 @@
1
+ // @bun
2
+ import {
3
+ TOTP_ERROR_KEYS
4
+ } from "../../../chunk-snqdnkk7.js";
5
+ export {
6
+ TOTP_ERROR_KEYS
7
+ };
@@ -0,0 +1,7 @@
1
+ export declare const TOTP_ERROR_KEYS: {
2
+ readonly INVALID_BASE32_CHARACTER: "totp.error.invalid_base32_character";
3
+ readonly INVALID_SECRET_LENGTH: "totp.error.invalid_secret_length";
4
+ readonly INVALID_ALGORITHM: "totp.error.invalid_algorithm";
5
+ readonly INVALID_OTP_AUTH_URI: "totp.error.invalid_otp_auth_uri";
6
+ readonly MISSING_SECRET: "totp.error.missing_secret";
7
+ };
@@ -0,0 +1 @@
1
+ export { hotp, totp, verifyTotp, buildOtpAuthUri, parseOtpAuthUri, timeRemaining } from './totp';
@@ -0,0 +1,113 @@
1
+ // @bun
2
+ import {
3
+ createCounterBuffer,
4
+ dynamicTruncation,
5
+ generateHmac
6
+ } from "../../chunk-4mt568fz.js";
7
+ import {
8
+ TOTP_ERROR_KEYS
9
+ } from "../../chunk-snqdnkk7.js";
10
+ import {
11
+ BaseError
12
+ } from "../../chunk-vknq69e0.js";
13
+
14
+ // source/modules/totp/totp.ts
15
+ import { webcrypto } from "crypto";
16
+ var hotp = async (secret, counter, {
17
+ algorithm = "SHA-1",
18
+ digits = 6
19
+ } = {}) => {
20
+ const counterBuffer = createCounterBuffer(counter);
21
+ const key = await webcrypto.subtle.importKey("raw", secret, { name: "HMAC", hash: algorithm }, false, ["sign"]);
22
+ const hmacArray = await generateHmac(key, counterBuffer);
23
+ return dynamicTruncation(hmacArray, digits);
24
+ };
25
+ var totp = async (secret, {
26
+ algorithm = "SHA-1",
27
+ digits = 6,
28
+ period = 30,
29
+ now = Date.now()
30
+ } = {}) => {
31
+ const timeStep = Math.floor(now / 1000 / period);
32
+ return hotp(secret, timeStep, { algorithm, digits });
33
+ };
34
+ var verifyTotp = async (secret, code, {
35
+ algorithm = "SHA-1",
36
+ digits = 6,
37
+ period = 30,
38
+ window = 0,
39
+ now = Date.now()
40
+ } = {}) => {
41
+ const currentTimeStep = Math.floor(now / 1000 / period);
42
+ for (let i = -window;i <= window; ++i) {
43
+ const timeStep = currentTimeStep + i;
44
+ const expectedCode = await hotp(secret, timeStep, { algorithm, digits });
45
+ if (expectedCode === code)
46
+ return true;
47
+ }
48
+ return false;
49
+ };
50
+ var buildOtpAuthUri = ({
51
+ secretBase32,
52
+ label,
53
+ issuer,
54
+ algorithm = "SHA-1",
55
+ digits = 6,
56
+ period = 30
57
+ }) => {
58
+ const encodedLabel = encodeURIComponent(label);
59
+ const encodedIssuer = issuer ? encodeURIComponent(issuer) : undefined;
60
+ let uri = `otpauth://totp/${encodedLabel}?secret=${secretBase32}`;
61
+ if (encodedIssuer)
62
+ uri += `&issuer=${encodedIssuer}`;
63
+ if (algorithm !== "SHA-1")
64
+ uri += `&algorithm=${algorithm}`;
65
+ if (digits !== 6)
66
+ uri += `&digits=${digits}`;
67
+ if (period !== 30)
68
+ uri += `&period=${period}`;
69
+ return uri;
70
+ };
71
+ var parseOtpAuthUri = (uri) => {
72
+ const url = new URL(uri);
73
+ if (url.protocol !== "otpauth:")
74
+ throw new BaseError({
75
+ message: TOTP_ERROR_KEYS.INVALID_OTP_AUTH_URI
76
+ });
77
+ if (url.hostname !== "totp")
78
+ throw new BaseError({
79
+ message: TOTP_ERROR_KEYS.INVALID_OTP_AUTH_URI
80
+ });
81
+ const label = decodeURIComponent(url.pathname.slice(1));
82
+ const secretBase32 = url.searchParams.get("secret");
83
+ if (!secretBase32)
84
+ throw new BaseError({
85
+ message: TOTP_ERROR_KEYS.MISSING_SECRET
86
+ });
87
+ const issuerParam = url.searchParams.get("issuer");
88
+ const issuer = issuerParam ? decodeURIComponent(issuerParam) : undefined;
89
+ const algorithm = url.searchParams.get("algorithm") || "SHA-1";
90
+ const digits = parseInt(url.searchParams.get("digits") || "6", 10);
91
+ const period = parseInt(url.searchParams.get("period") || "30", 10);
92
+ const result = {
93
+ secretBase32,
94
+ label,
95
+ algorithm,
96
+ digits,
97
+ period,
98
+ ...issuer && { issuer }
99
+ };
100
+ return result;
101
+ };
102
+ var timeRemaining = (period = 30, now = Date.now()) => {
103
+ const elapsed = Math.floor(now / 1000) % period;
104
+ return period - elapsed;
105
+ };
106
+ export {
107
+ verifyTotp,
108
+ totp,
109
+ timeRemaining,
110
+ parseOtpAuthUri,
111
+ hotp,
112
+ buildOtpAuthUri
113
+ };
@@ -0,0 +1,63 @@
1
+ import type { OtpAuthUri } from './types/otpAuthUri';
2
+ import type { TotpOptions } from './types/totpOptions';
3
+ import type { VerifyOptions } from './types/verifyOptions';
4
+ /**
5
+ * HMAC-based One-Time Password (HOTP) implementation
6
+ *
7
+ * @param secret - Secret key as bytes
8
+ * @param counter - Counter value
9
+ * @param opts - HOTP options
10
+ *
11
+ * @returns Promise resolving to the HOTP code
12
+ */
13
+ export declare const hotp: (secret: Uint8Array, counter: number | bigint, { algorithm, digits }?: TotpOptions) => Promise<string>;
14
+ /**
15
+ * Time-based One-Time Password (TOTP) implementation
16
+ *
17
+ * @param secret - Secret key as bytes
18
+ * @param opts - TOTP options including current time
19
+ *
20
+ * @returns Promise resolving to the TOTP code
21
+ */
22
+ export declare const totp: (secret: Uint8Array, { algorithm, digits, period, now }?: TotpOptions & {
23
+ now?: number;
24
+ }) => Promise<string>;
25
+ /**
26
+ * Verify a TOTP code against a secret
27
+ *
28
+ * @param secret - Secret key as bytes
29
+ * @param code - Code to verify
30
+ * @param opts - Verification options
31
+ *
32
+ * @returns Promise resolving to true if code is valid
33
+ */
34
+ export declare const verifyTotp: (secret: Uint8Array, code: string, { algorithm, digits, period, window, now }?: VerifyOptions) => Promise<boolean>;
35
+ /**
36
+ * Build an OTPAuth URI for QR code generation
37
+ *
38
+ * @param params - URI parameters
39
+ *
40
+ * @returns OTPAuth URI string
41
+ */
42
+ export declare const buildOtpAuthUri: ({ secretBase32, label, issuer, algorithm, digits, period }: OtpAuthUri) => string;
43
+ /**
44
+ * Parse an OTPAuth URI
45
+ *
46
+ * @param uri - OTPAuth URI to parse
47
+ *
48
+ * @throws ({@link BaseError}) if the URI is invalid or missing required parameters
49
+ *
50
+ * @returns Parsed URI parameters
51
+ */
52
+ export declare const parseOtpAuthUri: (uri: string) => Required<Omit<OtpAuthUri, "issuer">> & {
53
+ issuer?: string;
54
+ };
55
+ /**
56
+ * Calculate remaining time until next TOTP code
57
+ *
58
+ * @param period - Time period in seconds (default: 30)
59
+ * @param now - Current timestamp in milliseconds (default: Date.now())
60
+ *
61
+ * @returns Seconds remaining until next code
62
+ */
63
+ export declare const timeRemaining: (period?: number, now?: number) => number;
@@ -0,0 +1,3 @@
1
+ export type { OtpAuthUri } from './otpAuthUri';
2
+ export type { TotpOptions } from './totpOptions';
3
+ export type { VerifyOptions } from './verifyOptions';
@@ -0,0 +1 @@
1
+ // @bun
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Complete OTPAuth URI data structure
3
+ */
4
+ export interface OtpAuthUri {
5
+ /**
6
+ * Base32 encoded secret
7
+ */
8
+ secretBase32: string;
9
+ /**
10
+ * Label for the account (usually email or username)
11
+ */
12
+ label: string;
13
+ /**
14
+ * Issuer name (app/service name)
15
+ */
16
+ issuer: string;
17
+ /**
18
+ * Hash algorithm
19
+ *
20
+ * @defaultValue 'SHA-1'
21
+ */
22
+ algorithm?: 'SHA-1' | 'SHA-256' | 'SHA-512';
23
+ /**
24
+ * Number of digits
25
+ *
26
+ * @defaultValue 6
27
+ */
28
+ digits?: 6 | 8;
29
+ /**
30
+ * Time period in seconds
31
+ *
32
+ * @defaultValue 30
33
+ */
34
+ period?: number;
35
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Options for TOTP/HOTP generation
3
+ */
4
+ export interface TotpOptions {
5
+ /**
6
+ * Hash algorithm to use
7
+ *
8
+ * @defaultValue SHA-1
9
+ */
10
+ algorithm?: 'SHA-1' | 'SHA-256' | 'SHA-512';
11
+ /**
12
+ * Number of digits in the code
13
+ *
14
+ * @defaultValue 6
15
+ */
16
+ digits?: 6 | 8;
17
+ /**
18
+ * Time step in seconds for TOTP
19
+ *
20
+ * @defaultValue 30
21
+ */
22
+ period?: number;
23
+ }
@@ -0,0 +1,18 @@
1
+ import type { TotpOptions } from './totpOptions';
2
+ /**
3
+ * Options for TOTP verification
4
+ */
5
+ export interface VerifyOptions extends TotpOptions {
6
+ /**
7
+ * Time window for verification (±window periods)
8
+ *
9
+ * @defaultValue 1
10
+ */
11
+ window?: number;
12
+ /**
13
+ * Current timestamp in milliseconds
14
+ *
15
+ * @defaultValue Date.now()
16
+ */
17
+ now?: number;
18
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Encode bytes to Base32 string
3
+ *
4
+ * @param input - Bytes or string to encode
5
+ * @param withPadding - Whether to include padding (default: true)
6
+ *
7
+ * @returns Base32 encoded string
8
+ */
9
+ export declare const base32Encode: (input: string | Uint8Array, withPadding?: boolean) => string;
10
+ /**
11
+ * Decode Base32 string to bytes
12
+ *
13
+ * @param base32 - Base32 string to decode
14
+ *
15
+ * @throws ({@link BaseError}) if invalid Base32 character is found
16
+ *
17
+ * @returns Decoded bytes
18
+ */
19
+ export declare const base32Decode: (base32: string) => Uint8Array;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Convert a counter value to an 8-byte big-endian buffer
3
+ *
4
+ * @param counter - Counter value as number or bigint
5
+ *
6
+ * @returns ArrayBuffer containing the counter in big-endian format
7
+ */
8
+ export declare const createCounterBuffer: (counter: number | bigint) => ArrayBuffer;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Perform dynamic truncation on HMAC result according to RFC 4226
3
+ *
4
+ * @param hmacArray - HMAC result as byte array
5
+ * @param digits - Number of digits in the final code
6
+ *
7
+ * @returns Truncated code as string with leading zeros
8
+ */
9
+ export declare const dynamicTruncation: (hmacArray: Uint8Array, digits: number) => string;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generate HMAC for given data using a crypto key
3
+ *
4
+ * @param key - Crypto key for HMAC
5
+ * @param data - Data to sign
6
+ *
7
+ * @returns Promise resolving to HMAC as Uint8Array
8
+ */
9
+ export declare const generateHmac: (key: CryptoKey, data: ArrayBuffer) => Promise<Uint8Array>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Generate cryptographically secure random bytes for TOTP secret
3
+ *
4
+ * @param length - Number of bytes to generate (default: 20)
5
+ *
6
+ * @throws ({@link BaseError}) if length is not positive
7
+ *
8
+ * @returns Uint8Array containing the random bytes
9
+ */
10
+ export declare const generateSecretBytes: (length?: number) => Uint8Array;
@@ -0,0 +1,5 @@
1
+ export { base32Decode, base32Encode } from './base32';
2
+ export { createCounterBuffer } from './createCounterBuffer';
3
+ export { generateSecretBytes } from './generateSecretBytes';
4
+ export { generateHmac } from './generateHmac';
5
+ export { dynamicTruncation } from './dynamicTruncation';
@@ -0,0 +1,75 @@
1
+ // @bun
2
+ import {
3
+ createCounterBuffer,
4
+ dynamicTruncation,
5
+ generateHmac
6
+ } from "../../../chunk-4mt568fz.js";
7
+ import {
8
+ TOTP_ERROR_KEYS
9
+ } from "../../../chunk-snqdnkk7.js";
10
+ import {
11
+ BaseError
12
+ } from "../../../chunk-vknq69e0.js";
13
+
14
+ // source/modules/totp/utils/base32.ts
15
+ var BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
16
+ var base32Encode = (input, withPadding = true) => {
17
+ let result = "";
18
+ let bits = 0;
19
+ let value = 0;
20
+ const bytes = input instanceof Uint8Array ? input : new TextEncoder().encode(input);
21
+ for (const byte of bytes) {
22
+ value = value << 8 | byte;
23
+ bits += 8;
24
+ while (bits >= 5) {
25
+ result += BASE32_ALPHABET[value >>> bits - 5 & 31];
26
+ bits -= 5;
27
+ }
28
+ }
29
+ if (bits > 0)
30
+ result += BASE32_ALPHABET[value << 5 - bits & 31];
31
+ if (withPadding)
32
+ while (result.length % 8 !== 0)
33
+ result += "=";
34
+ return result;
35
+ };
36
+ var base32Decode = (base32) => {
37
+ const cleanBase32 = base32.replace(/=+$/, "");
38
+ if (cleanBase32.length === 0)
39
+ return new Uint8Array(0);
40
+ const result = [];
41
+ let bits = 0;
42
+ let value = 0;
43
+ for (const char of cleanBase32) {
44
+ const charValue = BASE32_ALPHABET.indexOf(char);
45
+ if (charValue === -1)
46
+ throw new BaseError({
47
+ message: TOTP_ERROR_KEYS.INVALID_BASE32_CHARACTER,
48
+ cause: `Invalid Base32 character: ${char}`
49
+ });
50
+ value = value << 5 | charValue;
51
+ bits += 5;
52
+ if (bits >= 8) {
53
+ result.push(value >>> bits - 8 & 255);
54
+ bits -= 8;
55
+ }
56
+ }
57
+ return new Uint8Array(result);
58
+ };
59
+ // source/modules/totp/utils/generateSecretBytes.ts
60
+ import { getRandomValues } from "crypto";
61
+ var generateSecretBytes = (length = 20) => {
62
+ if (length <= 0)
63
+ throw new BaseError({
64
+ message: TOTP_ERROR_KEYS.INVALID_SECRET_LENGTH
65
+ });
66
+ return getRandomValues(new Uint8Array(length));
67
+ };
68
+ export {
69
+ generateSecretBytes,
70
+ generateHmac,
71
+ dynamicTruncation,
72
+ createCounterBuffer,
73
+ base32Encode,
74
+ base32Decode
75
+ };
@@ -1 +1,2 @@
1
+ export type { RenameKey } from './renameKey';
1
2
  export type { StreamWithAsyncIterable } from './streamWithAsyncIterable';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Renames a key in an object type while preserving all other properties.
3
+ *
4
+ * @template T - The original object type
5
+ * @template From - The key to rename (must exist in T)
6
+ * @template To - The new key name
7
+ */
8
+ export type RenameKey<T, From extends keyof T, To extends PropertyKey> = {
9
+ [K in keyof T as K extends From ? To : K]: T[K];
10
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrxsys/mrx-core",
3
- "version": "2.6.0-canary-20250805-40e4647",
3
+ "version": "2.7.0",
4
4
  "author": "Ruby",
5
5
  "description": " Core provides a set of tools to help you build a microservice",
6
6
  "type": "module",
@@ -35,9 +35,9 @@
35
35
  "./modules/elysia/jwt/enums": "./dist/modules/elysia/jwt/enums/index.js",
36
36
  "./modules/elysia/jwt/types": "./dist/modules/elysia/jwt/types/index.js",
37
37
  "./modules/elysia/microservice": "./dist/modules/elysia/microservice/index.js",
38
- "./modules/elysia/ratelimit": "./dist/modules/elysia/ratelimit/index.js",
39
- "./modules/elysia/ratelimit/enums": "./dist/modules/elysia/ratelimit/enums/index.js",
40
- "./modules/elysia/ratelimit/types": "./dist/modules/elysia/ratelimit/types/index.js",
38
+ "./modules/elysia/rateLimit": "./dist/modules/elysia/rateLimit/index.js",
39
+ "./modules/elysia/rateLimit/enums": "./dist/modules/elysia/rateLimit/enums/index.js",
40
+ "./modules/elysia/rateLimit/types": "./dist/modules/elysia/rateLimit/types/index.js",
41
41
  "./modules/logger": "./dist/modules/logger/index.js",
42
42
  "./modules/logger/enums": "./dist/modules/logger/enums/index.js",
43
43
  "./modules/logger/events": "./dist/modules/logger/events/index.js",
@@ -50,6 +50,10 @@
50
50
  "./modules/repository/types": "./dist/modules/repository/types/index.js",
51
51
  "./modules/singletonManager": "./dist/modules/singletonManager/index.js",
52
52
  "./modules/singletonManager/enums": "./dist/modules/singletonManager/enums/index.js",
53
+ "./modules/totp": "./dist/modules/totp/index.js",
54
+ "./modules/totp/enums": "./dist/modules/totp/enums/index.js",
55
+ "./modules/totp/types": "./dist/modules/totp/types/index.js",
56
+ "./modules/totp/utils": "./dist/modules/totp/utils/index.js",
53
57
  "./modules/typedEventEmitter": "./dist/modules/typedEventEmitter/index.js",
54
58
  "./modules/typedEventEmitter/types": "./dist/modules/typedEventEmitter/types/index.js",
55
59
  "./utils": "./dist/utils/index.js",
@@ -62,25 +66,25 @@
62
66
  "fix-lint": "eslint --fix ./source",
63
67
  "lint": "eslint ./source",
64
68
  "start": "bun build/index.js",
65
- "test:integration": "bun test --timeout 5500 $(find test/integration -name '*.spec.ts')",
66
- "test:unit": "bun test --timeout 5500 --coverage $(find test/unit -name '*.spec.ts')",
69
+ "test:integration": "bun test --timeout 5800 $(find test/integration -name '*.spec.ts')",
70
+ "test:unit": "bun test --timeout 5800 --coverage $(find test/unit -name '*.spec.ts')",
67
71
  "test": "bun test --coverage --timeout 5500"
68
72
  },
69
73
  "devDependencies": {
70
- "@eslint/js": "^9.32.0",
74
+ "@eslint/js": "^9.33.0",
71
75
  "@sinclair/typebox": "0.34.38",
72
- "@stylistic/eslint-plugin": "^5.2.2",
73
- "@types/bun": "^1.2.19",
76
+ "@stylistic/eslint-plugin": "^5.2.3",
77
+ "@types/bun": "^1.2.20",
74
78
  "@types/nodemailer": "^6.4.17",
75
79
  "elysia": "^1.3.8",
76
- "eslint": "^9.32.0",
80
+ "eslint": "^9.33.0",
77
81
  "globals": "^16.3.0",
78
82
  "ioredis": "^5.7.0",
79
83
  "jose": "^6.0.12",
80
84
  "knex": "^3.1.0",
81
85
  "mssql": "^11.0.1",
82
86
  "nodemailer": "^7.0.5",
83
- "typescript-eslint": "^8.38.0"
87
+ "typescript-eslint": "^8.39.0"
84
88
  },
85
89
  "peerDependencies": {
86
90
  "@sinclair/typebox": "0.34.38",
@@ -1,6 +0,0 @@
1
- // source/modules/elysia/ratelimit/enums/ratelimitErrorKeys.ts
2
- var RATELIMIT_ERROR_KEYS = {
3
- RATELIMIT_EXCEEDED: "elysia.ratelimit.error.exceeded"
4
- };
5
-
6
- export { RATELIMIT_ERROR_KEYS };
@@ -1 +0,0 @@
1
- export { RATELIMIT_ERROR_KEYS } from './ratelimitErrorKeys';
@@ -1,7 +0,0 @@
1
- // @bun
2
- import {
3
- RATELIMIT_ERROR_KEYS
4
- } from "../../../../chunk-twaga0fp.js";
5
- export {
6
- RATELIMIT_ERROR_KEYS
7
- };
@@ -1,3 +0,0 @@
1
- export declare const RATELIMIT_ERROR_KEYS: {
2
- readonly RATELIMIT_EXCEEDED: "elysia.ratelimit.error.exceeded";
3
- };
@@ -1 +0,0 @@
1
- export { rateLimit } from './ratelimit';
@@ -1,52 +0,0 @@
1
- // @bun
2
- import {
3
- RATELIMIT_ERROR_KEYS
4
- } from "../../../chunk-twaga0fp.js";
5
- import {
6
- HttpError
7
- } from "../../../chunk-683sda6e.js";
8
- import"../../../chunk-9nw6qekv.js";
9
- import"../../../chunk-vknq69e0.js";
10
-
11
- // source/modules/elysia/ratelimit/ratelimit.ts
12
- import { Elysia } from "elysia";
13
- var rateLimit = ({ redis, limit, window }) => new Elysia({
14
- name: "rateLimit",
15
- seed: {
16
- redis,
17
- limit,
18
- window
19
- }
20
- }).onRequest(async ({ set, request, server }) => {
21
- const ip = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || server?.requestIP(request)?.address || "127.0.0.1";
22
- const key = `ratelimit:${ip}`;
23
- const current = await redis.get(key);
24
- const count = current ? parseInt(current) : 0;
25
- if (count === 0)
26
- await redis.setex(key, window, "1");
27
- else
28
- await redis.incr(key);
29
- const newCount = await redis.get(key);
30
- const currentCount = newCount ? parseInt(newCount) : 0;
31
- if (currentCount > limit) {
32
- set.status = 429;
33
- throw new HttpError({
34
- message: RATELIMIT_ERROR_KEYS.RATELIMIT_EXCEEDED,
35
- httpStatusCode: "TOO_MANY_REQUESTS",
36
- cause: {
37
- limit,
38
- window,
39
- remaining: 0,
40
- reset: await redis.ttl(key)
41
- }
42
- });
43
- }
44
- set.headers = {
45
- "X-RateLimit-Limit": limit.toString(),
46
- "X-RateLimit-Remaining": Math.max(0, limit - currentCount).toString(),
47
- "X-RateLimit-Reset": (await redis.ttl(key)).toString()
48
- };
49
- }).as("scoped");
50
- export {
51
- rateLimit
52
- };